From 47633bccc532a07559ef3e2329f231032ed34009 Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Thu, 4 Jun 2026 10:58:22 +0000 Subject: [PATCH 01/12] add support for Elu operator for Generic target --- Deeploy/Targets/Generic/Bindings.py | 7 ++- Deeploy/Targets/Generic/Layers.py | 9 ++++ Deeploy/Targets/Generic/Parsers.py | 9 ++++ Deeploy/Targets/Generic/Platform.py | 45 ++++++++++-------- .../Generic/Templates/FloatEluTemplate.py | 23 +++++++++ DeeployTest/Tests/Kernels/FP32/Elu/inputs.npz | Bin 0 -> 776 bytes .../Tests/Kernels/FP32/Elu/network.onnx | Bin 0 -> 129 bytes .../Tests/Kernels/FP32/Elu/outputs.npz | Bin 0 -> 778 bytes .../Generic/inc/DeeployBasicMath.h | 1 + TargetLibraries/Generic/inc/kernel/Elu.h | 22 +++++++++ TargetLibraries/Generic/src/Elu_fp32.c | 20 ++++++++ 11 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/FloatEluTemplate.py create mode 100644 DeeployTest/Tests/Kernels/FP32/Elu/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/Elu/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/Elu/outputs.npz create mode 100644 TargetLibraries/Generic/inc/kernel/Elu.h create mode 100644 TargetLibraries/Generic/src/Elu_fp32.c diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 21cf01e52a..68302c15e5 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -14,7 +14,7 @@ from Deeploy.Targets.Generic.Templates import AddTemplate, BatchNormalizationTemplate, ConcatTemplate, ConvTemplate, \ ConvTransposeTemplate, DebugPrintTemplate, DequantTemplate, DummyTemplate, DWConvTemplate, FloatAddTemplate, \ FloatAveragePoolTemplate, FloatCeilTemplate, FloatClipTemplate, FloatConvTemplate, FloatDivTemplate, \ - FloatDWConvTemplate, FloatExpTemplate, FloatFloorTemplate, FloatGELUTemplate, FloatGemmTemplate, \ + FloatDWConvTemplate, FloatEluTemplate, FloatExpTemplate, FloatFloorTemplate, FloatGELUTemplate, FloatGemmTemplate, \ FloatGlobalAveragePoolTemplate, FloatGlobalMaxPoolTemplate, FloatGroupNormTemplate, FloatHardSigmoidTemplate, \ FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, FloatMatMulTemplate, \ FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, FloatPowTemplate, FloatReduceMeanTemplate, \ @@ -385,6 +385,11 @@ FloatHardSwishTemplate.referenceTemplate, BasicTransformer), ] +BasicEluBindings = [ + NodeBinding(DummyChecker([PointerClass(float32_t)], [PointerClass(float32_t)]), FloatEluTemplate.referenceTemplate, + BasicTransformer), +] + BasicInstanceNormBindings = [ NodeBinding( DummyChecker( diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index d0a1e1db3c..4f39e92fe7 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -750,6 +750,15 @@ def computeOps(self): return self.mapper.parser.operatorRepresentation['size'] * 5 +class EluLayer(ONNXLayer): + + def computeOps(self): + # input > 0 -> y = x (just an assignment) + # input <=0 -> y = alpha * (expf(x) - 1): exp, add, mul + # consider the worst case, which is 3 ops + return self.mapper.parser.operatorRepresentation['size'] * 3 + + class InstanceNormLayer(ONNXLayer): def computeOps(self): diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index aa8bd8724a..8ae5c45bf4 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -2959,6 +2959,15 @@ def parseNode(self, node: gs.Node) -> bool: return super().parseNode(node) and node.op == 'HardSwish' +class EluParser(UnaryElementWiseParser): + + def parseNode(self, node: gs.Node) -> bool: + if not (super().parseNode(node) and node.op == 'Elu'): + return False + self.operatorRepresentation['alpha'] = node.attrs.get('alpha', 1.0) + return True + + class NormalizationParser(NodeParser): def parseNode(self, node: gs.Node) -> bool: diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index 2aa1ef1c38..7431702aea 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -9,30 +9,31 @@ from Deeploy.Targets.Generic.Bindings import BasicAddBindings, BasicAveragePool1DBindings, BasicAveragePool2DBindings, \ BasicBatchNormBindings, BasicCeilBindings, BasicClipBindings, BasicConcatBindings, BasicConv1DBindings, \ BasicConv2DBindings, BasicConvTransposeBindings, BasicDebugPrintBindings, BasicDequantBindings, BasicDivBindings, \ - BasicDWConv1DBinding, BasicDWConv2DBindings, BasicExpBindings, BasicFloorBindings, BasicGatherBindings, \ - BasicGELUBindings, BasicGEMMBindings, BasicGlobalAveragePoolBindings, BasicGlobalMaxPoolBindings, \ - BasicGroupNormBindings, BasicHardSigmoidBindings, BasicHardSwishBindings, BasicInstanceNormBindings, \ - BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, BasicLayerNormBindings, BasicMatMulBindings, \ - BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, BasicPad1DBindings, BasicPad2DBindings, \ - BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, BasicReluBinding, \ - BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, BasicSigmoidBindings, \ - BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, \ - BasicTransposeBindings, DummyBinding + BasicDWConv1DBinding, BasicDWConv2DBindings, BasicEluBindings, BasicExpBindings, BasicFloorBindings, \ + BasicGatherBindings, BasicGELUBindings, BasicGEMMBindings, BasicGlobalAveragePoolBindings, \ + BasicGlobalMaxPoolBindings, BasicGroupNormBindings, BasicHardSigmoidBindings, BasicHardSwishBindings, \ + BasicInstanceNormBindings, BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, BasicLayerNormBindings, \ + BasicMatMulBindings, BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, BasicPad1DBindings, \ + BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, \ + BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, \ + BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, \ + BasicSwishBindings, BasicTransposeBindings, DummyBinding from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \ - ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, ExpLayer, FloorLayer, \ - GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, InstanceNormLayer, \ - ITAMaxLayer, LayerNormLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, PowLayer, QuantLayer, ReduceMeanLayer, \ - ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, RQIntegerDivLayer, RQSiGELULayer, SigmoidLayer, \ - SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer + ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, ExpLayer, \ + FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, \ + InstanceNormLayer, ITAMaxLayer, LayerNormLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, PowLayer, \ + QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, RQIntegerDivLayer, \ + RQSiGELULayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ CeilParser, ClipParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, DummyParser, \ - ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, GenericConv2DParser, \ - GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, GlobalAveragePoolParser, \ - GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, InstanceNormParser, IntegerDivParser, \ - ITAMaxParser, ITAPartialMaxParser, LayerNormParser, MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, \ - Pad2DParser, PowParser, QuantParser, ReduceMeanParser, ReduceSumParser, ReluParser, RequantShiftParser, \ - ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, \ - SubParser, SwishParser, TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser + EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ + GenericConv2DParser, GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, \ + GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ + InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, MatMulParser, \ + MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, ReduceSumParser, \ + ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SigmoidParser, SliceParser, \ + SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, iLayerNormParser, \ + iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ @@ -88,6 +89,7 @@ SwishMapper = NodeMapper(SwishParser(), BasicSwishBindings) HardSigmoidMapper = NodeMapper(HardSigmoidParser(), BasicHardSigmoidBindings) HardSwishMapper = NodeMapper(HardSwishParser(), BasicHardSwishBindings) +EluMapper = NodeMapper(EluParser(), BasicEluBindings) InstanceNormMapper = NodeMapper(InstanceNormParser(), BasicInstanceNormBindings) GroupNormMapper = NodeMapper(GroupNormParser(), BasicGroupNormBindings) AveragePool1DMapper = NodeMapper(AveragePool1DParser(), BasicAveragePool1DBindings) @@ -106,6 +108,7 @@ 'Concat': ConcatLayer([ConcatMapper]), 'DebugPrint': DebugPrintLayer([DebugMapper]), 'Div': DivLayer([DivMapper]), + 'Elu': EluLayer([EluMapper]), 'Flatten': ReshapeLayer([FlattenMapper]), 'Gather': GatherLayer([GatherMapper]), 'Gemm': GEMMLayer([GEMMMapper]), diff --git a/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py b/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py new file mode 100644 index 0000000000..fc7a1886ae --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 +import numpy as np + +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + + +class _EluTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: + + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['size'] = int(np.prod(data_in.shape)) + operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth + return ctxt, operatorRepresentation, [] + + +referenceTemplate = _EluTemplate(""" +// ELU (Name: ${nodeName}, Op: ${nodeOp}) +Elu_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}, ${alpha}); +""") diff --git a/DeeployTest/Tests/Kernels/FP32/Elu/inputs.npz b/DeeployTest/Tests/Kernels/FP32/Elu/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..070beaf0150b59c30404882cc8ebf14802ffea2f GIT binary patch literal 776 zcmWIWW@gc4fB;2?%{I!L|3d*Mg9t-rUO{PzUS2^ZBZB}#0~16UjGpWl>KhQr$WX>m zt)7xvoLr=CrJ!z;W}>d6pq`drR8o|f7oT60k_r-cOUx-w1&SAEBo?Fs`5G2F3WjEy zItsN4z`eBs7tUvDdRP-?!VEvDx!D4+OA6bf6Y+`BMzq=oEq!2zhe`(!!I=k$3={f z9OPbU>~qUsu%Ck~P&9A= literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/Elu/network.onnx b/DeeployTest/Tests/Kernels/FP32/Elu/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..123fab1153d5f1a9cc256aa6e9044c335cafc9d9 GIT binary patch literal 129 zcmdqQ}gp6oG5Xaem*W94n`pkE+!5T7D$rdf|@9VCga4y#UQ{901}HF A4*&oF literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/Elu/outputs.npz b/DeeployTest/Tests/Kernels/FP32/Elu/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..223e5bfc85117e0e8bfc73393f2451b82bb7bcac GIT binary patch literal 778 zcmbVKZAg<*6n@`Xf?8=3DiN_jo#8Ac6O-^gS7S(Gj8IBUY`BS(xUrAGBs6DgR7gmZ zrXdQMIW+uW<`Pe!NSP4@#WKq=4IAuVx^(Jp_thl&+qqoMInQ}GUmRs_(5@tITaIvp z$=yHc9|`3Sa9Wd5XEGkE)K&3Z1XnBB=I!W~a|LGL#bYtA(fpjOO?r` zvNEmSs8>~%X!UB1h$~bT1`Wdv(ADTPPLrqZ_sqc&;`^RkL$_m9u?GT7H z6sunJF>cK88h&Cr+b)3xbp&xIObKF~KCBgHOjhzku}^65vzag+mU?jVja}&3q*!dEc;;R^Q69cXEX-_;CQd;kCd literal 0 HcmV?d00001 diff --git a/TargetLibraries/Generic/inc/DeeployBasicMath.h b/TargetLibraries/Generic/inc/DeeployBasicMath.h index 2023b9e725..8fb2cf8e97 100644 --- a/TargetLibraries/Generic/inc/DeeployBasicMath.h +++ b/TargetLibraries/Generic/inc/DeeployBasicMath.h @@ -40,6 +40,7 @@ #include "kernel/Convolution.h" #include "kernel/DWConvolution.h" #include "kernel/Div.h" +#include "kernel/Elu.h" #include "kernel/Exp.h" #include "kernel/Floor.h" #include "kernel/GELU.h" diff --git a/TargetLibraries/Generic/inc/kernel/Elu.h b/TargetLibraries/Generic/inc/kernel/Elu.h new file mode 100644 index 0000000000..ac6d03c4ee --- /dev/null +++ b/TargetLibraries/Generic/inc/kernel/Elu.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DEEPLOY_BASIC_MATH_ELU_KERNEL_HEADER_ +#define __DEEPLOY_BASIC_MATH_ELU_KERNEL_HEADER_ + +#include "DeeployBasicMath.h" + +/* + * element wise Exponential Linear Unit (ELU) function + */ + +/******************************************************************************/ +/* Elu */ +/******************************************************************************/ +void Elu_fp32_fp32(const float32_t *data_in, float32_t *data_out, int32_t size, + float32_t alpha); + +#endif //__DEEPLOY_BASIC_MATH_ELU_KERNEL_HEADER_ diff --git a/TargetLibraries/Generic/src/Elu_fp32.c b/TargetLibraries/Generic/src/Elu_fp32.c new file mode 100644 index 0000000000..c71a1c9282 --- /dev/null +++ b/TargetLibraries/Generic/src/Elu_fp32.c @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "DeeployBasicMath.h" +#include + +void Elu_fp32_fp32(const float32_t *input, float32_t *output, int32_t size, + float32_t alpha) { + + for (int i = 0; i < size; i++) { + if (input[i] >= 0) { + output[i] = input[i]; + } else { + output[i] = alpha * (expf(input[i]) - 1.0f); + } + } +} \ No newline at end of file From f4671a9efeb43bfe9b803e5d31d2574bdfba542d Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Thu, 4 Jun 2026 12:40:17 +0000 Subject: [PATCH 02/12] add support for LeakyRelu operator for Generic target --- Deeploy/Targets/Generic/Bindings.py | 20 +++++++----- Deeploy/Targets/Generic/Layers.py | 4 +++ Deeploy/Targets/Generic/Parsers.py | 9 ++++++ Deeploy/Targets/Generic/Platform.py | 29 ++++++++++-------- .../Templates/FloatLeakyReluTemplate.py | 23 ++++++++++++++ .../Tests/Kernels/FP32/LeakyRelu/inputs.npz | Bin 0 -> 776 bytes .../Tests/Kernels/FP32/LeakyRelu/network.onnx | Bin 0 -> 135 bytes .../Tests/Kernels/FP32/LeakyRelu/outputs.npz | Bin 0 -> 778 bytes .../Generic/inc/DeeployBasicMath.h | 1 + .../Generic/inc/kernel/LeakyRelu.h | 22 +++++++++++++ TargetLibraries/Generic/src/LeakyRelu_fp32.c | 19 ++++++++++++ 11 files changed, 107 insertions(+), 20 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py create mode 100644 DeeployTest/Tests/Kernels/FP32/LeakyRelu/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/LeakyRelu/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/LeakyRelu/outputs.npz create mode 100644 TargetLibraries/Generic/inc/kernel/LeakyRelu.h create mode 100644 TargetLibraries/Generic/src/LeakyRelu_fp32.c diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 68302c15e5..9221f5932e 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -16,13 +16,14 @@ FloatAveragePoolTemplate, FloatCeilTemplate, FloatClipTemplate, FloatConvTemplate, FloatDivTemplate, \ FloatDWConvTemplate, FloatEluTemplate, FloatExpTemplate, FloatFloorTemplate, FloatGELUTemplate, FloatGemmTemplate, \ FloatGlobalAveragePoolTemplate, FloatGlobalMaxPoolTemplate, FloatGroupNormTemplate, FloatHardSigmoidTemplate, \ - FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, FloatMatMulTemplate, \ - FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, FloatPowTemplate, FloatReduceMeanTemplate, \ - FloatReluTemplate, FloatSigmoidTemplate, FloatSoftmaxTemplate, FloatSqrtTemplate, FloatSubTemplate, \ - FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, ITAMaxTemplate, ITAPartialMaxTemplate, \ - MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, ReduceMeanTemplate, ReduceSumTemplate, \ - RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, RQSiGELUTemplate, SliceTemplate, SubTemplate, \ - TransposeTemplate, iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate + FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, FloatLeakyReluTemplate, \ + FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, FloatPowTemplate, \ + FloatReduceMeanTemplate, FloatReluTemplate, FloatSigmoidTemplate, FloatSoftmaxTemplate, FloatSqrtTemplate, \ + FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, ITAMaxTemplate, \ + ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, \ + ReduceMeanTemplate, ReduceSumTemplate, RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, \ + RQSiGELUTemplate, SliceTemplate, SubTemplate, TransposeTemplate, iGELUTemplate, iLayernormTemplate, \ + iRMSNormTemplate, iSoftmaxTemplate from Deeploy.Targets.Generic.TypeCheckers import AddChecker, BatchNormChecker, ConcatChecker, ConvChecker, \ DebugPrintChecker, DequantChecker, DivChecker, DummyChecker, GatherChecker, GELUChecker, GEMMChecker, \ LayerNormChecker, MatMulChecker, MaxPoolChecker, MulChecker, PadChecker, QuantChecker, ReduceMeanChecker, \ @@ -390,6 +391,11 @@ BasicTransformer), ] +BasicLeakyReluBindings = [ + NodeBinding(DummyChecker([PointerClass(float32_t)], [PointerClass(float32_t)]), + FloatLeakyReluTemplate.referenceTemplate, BasicTransformer), +] + BasicInstanceNormBindings = [ NodeBinding( DummyChecker( diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index 4f39e92fe7..e36f550742 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -759,6 +759,10 @@ def computeOps(self): return self.mapper.parser.operatorRepresentation['size'] * 3 +class LeakyReluLayer(SingleOperationPerElementLayer): + pass + + class InstanceNormLayer(ONNXLayer): def computeOps(self): diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index 8ae5c45bf4..889cb34576 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -2968,6 +2968,15 @@ def parseNode(self, node: gs.Node) -> bool: return True +class LeakyReluParser(UnaryElementWiseParser): + + def parseNode(self, node: gs.Node) -> bool: + if not (super().parseNode(node) and node.op == 'LeakyRelu'): + return False + self.operatorRepresentation['alpha'] = node.attrs.get('alpha', 0.01) + return True + + class NormalizationParser(NodeParser): def parseNode(self, node: gs.Node) -> bool: diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index 7431702aea..e7e4ee0462 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -13,27 +13,28 @@ BasicGatherBindings, BasicGELUBindings, BasicGEMMBindings, BasicGlobalAveragePoolBindings, \ BasicGlobalMaxPoolBindings, BasicGroupNormBindings, BasicHardSigmoidBindings, BasicHardSwishBindings, \ BasicInstanceNormBindings, BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, BasicLayerNormBindings, \ - BasicMatMulBindings, BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, BasicPad1DBindings, \ - BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, \ - BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, \ - BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, \ - BasicSwishBindings, BasicTransposeBindings, DummyBinding + BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, \ + BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, \ + BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, \ + BasicRQSGELUBinding, BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, \ + BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, DummyBinding from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \ ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, ExpLayer, \ FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, \ - InstanceNormLayer, ITAMaxLayer, LayerNormLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, PowLayer, \ - QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, RQIntegerDivLayer, \ - RQSiGELULayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer + InstanceNormLayer, ITAMaxLayer, LayerNormLayer, LeakyReluLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, \ + PowLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, \ + RQIntegerDivLayer, RQSiGELULayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, \ + TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ CeilParser, ClipParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, DummyParser, \ EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ GenericConv2DParser, GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, \ GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ - InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, MatMulParser, \ - MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, ReduceSumParser, \ - ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SigmoidParser, SliceParser, \ - SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, iLayerNormParser, \ - iSoftmaxParser + InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, LeakyReluParser, \ + MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, \ + ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SigmoidParser, \ + SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, \ + iLayerNormParser, iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ @@ -90,6 +91,7 @@ HardSigmoidMapper = NodeMapper(HardSigmoidParser(), BasicHardSigmoidBindings) HardSwishMapper = NodeMapper(HardSwishParser(), BasicHardSwishBindings) EluMapper = NodeMapper(EluParser(), BasicEluBindings) +LeakyReluMapper = NodeMapper(LeakyReluParser(), BasicLeakyReluBindings) InstanceNormMapper = NodeMapper(InstanceNormParser(), BasicInstanceNormBindings) GroupNormMapper = NodeMapper(GroupNormParser(), BasicGroupNormBindings) AveragePool1DMapper = NodeMapper(AveragePool1DParser(), BasicAveragePool1DBindings) @@ -115,6 +117,7 @@ 'iGELU': GELULayer([GELUMapper]), 'Gelu': GELULayer([GELUMapper]), 'LayerNormalization': LayerNormLayer([LayerNormMapper]), + 'LeakyRelu': LeakyReluLayer([LeakyReluMapper]), 'iLayerNorm': LayerNormLayer([iLayerNormMapper]), 'IntegerDiv': DivLayer([IntegerDivMapper]), 'IntegerMean': ReduceMeanLayer([ReduceMeanMapper]), diff --git a/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py b/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py new file mode 100644 index 0000000000..35804bd3d7 --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 +import numpy as np + +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + + +class _LeakyReluTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: + + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['size'] = int(np.prod(data_in.shape)) + operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth + return ctxt, operatorRepresentation, [] + + +referenceTemplate = _LeakyReluTemplate(""" +// LeakyRelu (Name: ${nodeName}, Op: ${nodeOp}) +LeakyRelu_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}, ${alpha}); +""") diff --git a/DeeployTest/Tests/Kernels/FP32/LeakyRelu/inputs.npz b/DeeployTest/Tests/Kernels/FP32/LeakyRelu/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..1ae95b34f824ba503ed3809103dd2d01154a8fef GIT binary patch literal 776 zcmbV~Ye>^!6vnriikH&7Q9?Qko62U1OGBMICn>CDEla~oUT}ZWvZkj-*uX*lz>Ki;q{DM6aTW*uOuc``;{9-qt0 zEmV{!vgMhD3K?U|H)R(n8EdZ0%PwN9f6#h9H!LEU&ppKbUpWgrM__~Aoi1)>!(4PG z4eB?6a*rwYO>!`n^_NiJ9~&X7)_~1-I2if8DR%VbNzoON1eU+8Aw6f#Lg>1k@S!k@ zj9^@>?8Q;4MG^Xs7m|x#8pP3664V6fiTSH2_VM8eG~Ugle6yam4RJuPGlOs7F8KF9Me#60)X;rjPbhVgCf&H~1JEnah-GsSsa{9HHi_ zYO3_VXY}-)Byzh~NKJ0x{iklv1J9J}dHX8b8y~JZ}gjuf*V`d z_-rs9^EIwi+3JKHPvTKF-3Re*1-RCrL8*kz5!n3;uZ>OHIxQe=GY#8z|Dr6F*gMz) Mwsj({-u)ZvU!CzOl>h($ literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/LeakyRelu/network.onnx b/DeeployTest/Tests/Kernels/FP32/LeakyRelu/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..038a4c7562f63e9760e37e0ae8fcd6c76d3c43db GIT binary patch literal 135 zcmdpvD<~}yV#_Zr0n$pGKBl|iXFrCR)4tcf`V8Hu7?*B9F? zU}O{$&(F)Nh)*v{EXXJ>ElN+#&x3HH#9^lJaq)043UP2Tae%Nuk^~pjTp=_WCl)RS G0d4@nhad?6 literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/LeakyRelu/outputs.npz b/DeeployTest/Tests/Kernels/FP32/LeakyRelu/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..3cf5e869a78e3d16f4c5560e1f53fdf1a6607c0c GIT binary patch literal 778 zcmbV~e=O8-7{|YNI_CU{No^XPnvUE_xwzE#?s>8r-Q-&92dNlmDL<||=f_TF{V?pv z&>w!Jp|VUfk&^p<*fO<-&SnauGPZP{*kFfjs{HI1yjK z4SH{%Nmq_`j}N|lE`8r;ntT!&VBkv-XwN3X#*rK%*2qZJ_7EJJi6foP{`eviv3sRJdQumT z6Z#f1d~rd_nG`S;H%6G^+vUV3A`Lhno6v7D3T?GQ$geI!O}Y(obFHv0Tu0;)TX8DO z6-Q?5Nu+}#x`lbPzoAYMuE2)06~@!r4_tE3b(vf_YS|&`pMst9pr0`g5Z*$T^gyXy zN4jqV>2{ja&$?_P??N2Ogoiyo$8gBWT>?dBAKj|!f@~EHX$nh7Ob=iT^%An6?UsfK zSwtxeh0LBM_|9$uu&QOcn{#mdu^X1nXi#&h2u0qH7^T?23>FW7k+TiGGg65nZVUv| zPoPr#On)s=OzNFC6TLB?XsrXFGrodkV;EE$TbK(i!(e+?2dBp~r8`=0!Sum4y_+!* z9v)`ltHEF_dhJKl_14(j8;r4wJrKM;0ewy1av6&)wD^}^Gn?k-t}$)4Y?gWcWmzuc PSeg@-sl+DN|K|D^rNk?j literal 0 HcmV?d00001 diff --git a/TargetLibraries/Generic/inc/DeeployBasicMath.h b/TargetLibraries/Generic/inc/DeeployBasicMath.h index 8fb2cf8e97..899f9d9b2b 100644 --- a/TargetLibraries/Generic/inc/DeeployBasicMath.h +++ b/TargetLibraries/Generic/inc/DeeployBasicMath.h @@ -52,6 +52,7 @@ #include "kernel/HardSwish.h" #include "kernel/InstanceNorm.h" #include "kernel/Layernorm.h" +#include "kernel/LeakyRelu.h" #include "kernel/MatMul.h" #include "kernel/MaxPool.h" #include "kernel/Pow.h" diff --git a/TargetLibraries/Generic/inc/kernel/LeakyRelu.h b/TargetLibraries/Generic/inc/kernel/LeakyRelu.h new file mode 100644 index 0000000000..daa096c2a9 --- /dev/null +++ b/TargetLibraries/Generic/inc/kernel/LeakyRelu.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DEEPLOY_BASIC_MATH_LEAKYRELU_KERNEL_HEADER_ +#define __DEEPLOY_BASIC_MATH_LEAKYRELU_KERNEL_HEADER_ + +#include "DeeployBasicMath.h" + +/* + * element wise LeakyRelu function + */ + +/******************************************************************************/ +/* LeakyRelu */ +/******************************************************************************/ +void LeakyRelu_fp32_fp32(const float32_t *data_in, float32_t *data_out, + int32_t size, float32_t alpha); + +#endif //__DEEPLOY_BASIC_MATH_LEAKYRELU_KERNEL_HEADER_ diff --git a/TargetLibraries/Generic/src/LeakyRelu_fp32.c b/TargetLibraries/Generic/src/LeakyRelu_fp32.c new file mode 100644 index 0000000000..3994b98937 --- /dev/null +++ b/TargetLibraries/Generic/src/LeakyRelu_fp32.c @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "DeeployBasicMath.h" + +void LeakyRelu_fp32_fp32(const float32_t *input, float32_t *output, + int32_t size, float32_t alpha) { + + for (int i = 0; i < size; i++) { + if (input[i] >= 0) { + output[i] = input[i]; + } else { + output[i] = alpha * input[i]; + } + } +} \ No newline at end of file From 1c09e963bbc23150025a8e61bbf3d7048012bdec Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Thu, 4 Jun 2026 13:04:26 +0000 Subject: [PATCH 03/12] add support for Selu operator for Generic target --- Deeploy/Targets/Generic/Bindings.py | 11 ++++++--- Deeploy/Targets/Generic/Layers.py | 9 +++++++ Deeploy/Targets/Generic/Parsers.py | 10 ++++++++ Deeploy/Targets/Generic/Platform.py | 14 ++++++----- .../Generic/Templates/FloatSeluTemplate.py | 23 ++++++++++++++++++ .../Tests/Kernels/FP32/Selu/inputs.npz | Bin 0 -> 776 bytes .../Tests/Kernels/FP32/Selu/network.onnx | Bin 0 -> 113 bytes .../Tests/Kernels/FP32/Selu/outputs.npz | Bin 0 -> 778 bytes .../Generic/inc/DeeployBasicMath.h | 1 + TargetLibraries/Generic/inc/kernel/Selu.h | 22 +++++++++++++++++ TargetLibraries/Generic/src/Selu_fp32.c | 20 +++++++++++++++ 11 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py create mode 100644 DeeployTest/Tests/Kernels/FP32/Selu/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/Selu/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/Selu/outputs.npz create mode 100644 TargetLibraries/Generic/inc/kernel/Selu.h create mode 100644 TargetLibraries/Generic/src/Selu_fp32.c diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 9221f5932e..98c233aab5 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -18,9 +18,9 @@ FloatGlobalAveragePoolTemplate, FloatGlobalMaxPoolTemplate, FloatGroupNormTemplate, FloatHardSigmoidTemplate, \ FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, FloatLeakyReluTemplate, \ FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, FloatPowTemplate, \ - FloatReduceMeanTemplate, FloatReluTemplate, FloatSigmoidTemplate, FloatSoftmaxTemplate, FloatSqrtTemplate, \ - FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, ITAMaxTemplate, \ - ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, \ + FloatReduceMeanTemplate, FloatReluTemplate, FloatSeluTemplate, FloatSigmoidTemplate, FloatSoftmaxTemplate, \ + FloatSqrtTemplate, FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, \ + ITAMaxTemplate, ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, \ ReduceMeanTemplate, ReduceSumTemplate, RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, \ RQSiGELUTemplate, SliceTemplate, SubTemplate, TransposeTemplate, iGELUTemplate, iLayernormTemplate, \ iRMSNormTemplate, iSoftmaxTemplate @@ -391,6 +391,11 @@ BasicTransformer), ] +BasicSeluBindings = [ + NodeBinding(DummyChecker([PointerClass(float32_t)], [PointerClass(float32_t)]), FloatSeluTemplate.referenceTemplate, + BasicTransformer), +] + BasicLeakyReluBindings = [ NodeBinding(DummyChecker([PointerClass(float32_t)], [PointerClass(float32_t)]), FloatLeakyReluTemplate.referenceTemplate, BasicTransformer), diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index e36f550742..634f78e6dd 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -759,6 +759,15 @@ def computeOps(self): return self.mapper.parser.operatorRepresentation['size'] * 3 +class SeluLayer(ONNXLayer): + + def computeOps(self): + # input > 0 -> y = gamma * x: mul + # input <=0 -> y = gamma * alpha * (expf(x) - 1): exp, add, 2 mul + # consider the worst case, which is 4 ops + return self.mapper.parser.operatorRepresentation['size'] * 4 + + class LeakyReluLayer(SingleOperationPerElementLayer): pass diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index 889cb34576..3e35a264f7 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -2968,6 +2968,16 @@ def parseNode(self, node: gs.Node) -> bool: return True +class SeluParser(UnaryElementWiseParser): + + def parseNode(self, node: gs.Node) -> bool: + if not (super().parseNode(node) and node.op == 'Selu'): + return False + self.operatorRepresentation['alpha'] = node.attrs.get('alpha', 1.67326319217681884765625) + self.operatorRepresentation['gamma'] = node.attrs.get('gamma', 1.05070102214813232421875) + return True + + class LeakyReluParser(UnaryElementWiseParser): def parseNode(self, node: gs.Node) -> bool: diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index e7e4ee0462..d8c6da5c43 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -16,15 +16,15 @@ BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, \ BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, \ BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, \ - BasicRQSGELUBinding, BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, \ - BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, DummyBinding + BasicRQSGELUBinding, BasicSeluBindings, BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, \ + BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, DummyBinding from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \ ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, ExpLayer, \ FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, \ InstanceNormLayer, ITAMaxLayer, LayerNormLayer, LeakyReluLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, \ PowLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, \ - RQIntegerDivLayer, RQSiGELULayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, \ - TransposeLayer + RQIntegerDivLayer, RQSiGELULayer, SeluLayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, \ + SwishLayer, TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ CeilParser, ClipParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, DummyParser, \ EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ @@ -32,8 +32,8 @@ GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, LeakyReluParser, \ MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, \ - ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SigmoidParser, \ - SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, \ + ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SeluParser, \ + SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, \ iLayerNormParser, iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ @@ -91,6 +91,7 @@ HardSigmoidMapper = NodeMapper(HardSigmoidParser(), BasicHardSigmoidBindings) HardSwishMapper = NodeMapper(HardSwishParser(), BasicHardSwishBindings) EluMapper = NodeMapper(EluParser(), BasicEluBindings) +SeluMapper = NodeMapper(SeluParser(), BasicSeluBindings) LeakyReluMapper = NodeMapper(LeakyReluParser(), BasicLeakyReluBindings) InstanceNormMapper = NodeMapper(InstanceNormParser(), BasicInstanceNormBindings) GroupNormMapper = NodeMapper(GroupNormParser(), BasicGroupNormBindings) @@ -151,6 +152,7 @@ 'Floor': FloorLayer([FloorMapper]), 'Clip': ClipLayer([ClipMapper]), 'Exp': ExpLayer([ExpMapper]), + 'Selu': SeluLayer([SeluMapper]), 'Sigmoid': SigmoidLayer([SigmoidMapper]), 'Swish': SwishLayer([SwishMapper]), 'HardSigmoid': SigmoidLayer([HardSigmoidMapper]), diff --git a/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py b/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py new file mode 100644 index 0000000000..2585a1966d --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 +import numpy as np + +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + + +class _SeluTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: + + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['size'] = int(np.prod(data_in.shape)) + operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth + return ctxt, operatorRepresentation, [] + + +referenceTemplate = _SeluTemplate(""" +// SELU (Name: ${nodeName}, Op: ${nodeOp}) +Selu_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}, ${alpha}, ${gamma}); +""") diff --git a/DeeployTest/Tests/Kernels/FP32/Selu/inputs.npz b/DeeployTest/Tests/Kernels/FP32/Selu/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..4565d80b6c3528d86e5063e8c59b4ded41a47b10 GIT binary patch literal 776 zcmbV~e=O8-7{|YNR4z5QWkfZn?cC8RZHa{M^Dz|HaYPF@cHG^Baors;yQbk9%iOTo z&M3(yQcRoZd_SN0ZPh5&w3%8pKNc&)lD4{w+WNQmv*&s5dGGz_ODeXp^`^GvP8H+H z@Si*4P>$4l-e|nAb=+bRGkutcmh?9 zMyHWyr{ri9N^2f2&(bO_xi(#{R$6|czleA8w5N!t=l!qt&PKU2H=Gyofes`6yd#FF z&g8>8bu@WzU@r!S>cel>K~XhIt1@4?aEP)U*$yzSo0tmOY4y8ss*W-#rC%O7hWry*5&LVZM7nn_5MX!D&Do* O8n7(mXYsz@SpNc{w=M4g literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/Selu/network.onnx b/DeeployTest/Tests/Kernels/FP32/Selu/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..55a951a5137cf4a7c9773e769312967f0f6fa105 GIT binary patch literal 113 zcmdLMTrF&#id2*srh*j kPLw!IJs%ej2cr-N7ZV2v3nWQ!K}`@slW}6qoAIaUsO_*m=~X4l#&V(cT3DEP6dh=XCxM+0{I#iItqqn znmP)#3giN=cPT;p?>v04|0DAZdk@Zs`{$%LIjm~Awg3G5`wmjC=hx^ljV&=aC9gb>-J4W7pu;0(p zc>k-zVvgC`n;j%1q8-#zSSZ7XN^OV{#J+Z--jHEZyPxDX39H00GhLY z`h)$KZzVX?`3cz{lQVTZd^Oxr0%$JCY}9_+ssxpDt**3R!krwZKKbWRce&G{$s*T5EMSM@UXMBZ!(?ympLx%~ z@vfqe9}&6hC||mCk}HyHSZ7QXK~Q=Il7MlXlA$D{rwB) zi94En^WL}UlbEBYZLj0a`49HnZm4n0+jVb$7*I`7jGN;-V7O;iE_dAEDsRv9|H1wz z!0=Eyf8HUnhTjq7HXDoC4n40P?B{s=(>4~!XOxX|*a{41B|}EX-MRG+0p5&Ey3DAl j7nC+ZmmIK literal 0 HcmV?d00001 diff --git a/TargetLibraries/Generic/inc/DeeployBasicMath.h b/TargetLibraries/Generic/inc/DeeployBasicMath.h index 899f9d9b2b..eaf3f0c6ee 100644 --- a/TargetLibraries/Generic/inc/DeeployBasicMath.h +++ b/TargetLibraries/Generic/inc/DeeployBasicMath.h @@ -62,6 +62,7 @@ #include "kernel/RQHardswish.h" #include "kernel/Relu.h" #include "kernel/RequantShift.h" +#include "kernel/Selu.h" #include "kernel/Sigmoid.h" #include "kernel/Softmax.h" #include "kernel/Sqrt.h" diff --git a/TargetLibraries/Generic/inc/kernel/Selu.h b/TargetLibraries/Generic/inc/kernel/Selu.h new file mode 100644 index 0000000000..225ec75df8 --- /dev/null +++ b/TargetLibraries/Generic/inc/kernel/Selu.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __DEEPLOY_BASIC_MATH_SELU_KERNEL_HEADER_ +#define __DEEPLOY_BASIC_MATH_SELU_KERNEL_HEADER_ + +#include "DeeployBasicMath.h" + +/* + * element wise Scaled Exponential Linear Unit (SELU) function + */ + +/******************************************************************************/ +/* Selu */ +/******************************************************************************/ +void Selu_fp32_fp32(const float32_t *input, float32_t *output, int32_t size, + float32_t alpha, float32_t gamma); + +#endif //__DEEPLOY_BASIC_MATH_SELU_KERNEL_HEADER_ diff --git a/TargetLibraries/Generic/src/Selu_fp32.c b/TargetLibraries/Generic/src/Selu_fp32.c new file mode 100644 index 0000000000..ac120c7e55 --- /dev/null +++ b/TargetLibraries/Generic/src/Selu_fp32.c @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "DeeployBasicMath.h" +#include + +void Selu_fp32_fp32(const float32_t *input, float32_t *output, int32_t size, + float32_t alpha, float32_t gamma) { + + for (int i = 0; i < size; i++) { + float32_t tmp = input[i]; + if (input[i] < 0) { + tmp = alpha * (expf(tmp) - 1.0f); + } + output[i] = gamma * tmp; + } +} \ No newline at end of file From 3dc7012edc9609b6db6a133eeab9cf87b8fa5dae Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Fri, 5 Jun 2026 10:49:09 +0000 Subject: [PATCH 04/12] add support for Scatter and ScatterElements operator for Generic target --- Deeploy/Targets/Generic/Bindings.py | 17 +- Deeploy/Targets/Generic/Layers.py | 12 + Deeploy/Targets/Generic/Parsers.py | 43 +++ Deeploy/Targets/Generic/Platform.py | 18 +- .../Generic/Templates/ScatterTemplate.py | 49 +++ Deeploy/Targets/Generic/TypeCheckers.py | 283 +++--------------- .../Kernels/FP32/ScatterElements/inputs.npz | Bin 0 -> 4050 bytes .../Kernels/FP32/ScatterElements/network.onnx | Bin 0 -> 211 bytes .../Kernels/FP32/ScatterElements/outputs.npz | Bin 0 -> 2314 bytes .../Integer/ScatterElements/inputs.npz | Bin 0 -> 4050 bytes .../Integer/ScatterElements/network.onnx | Bin 0 -> 211 bytes .../Integer/ScatterElements/outputs.npz | Bin 0 -> 2314 bytes .../Generic/inc/DeeployBasicMath.h | 1 + TargetLibraries/Generic/inc/kernel/Scatter.h | 42 +++ TargetLibraries/Generic/src/Scatter.c | 67 +++++ 15 files changed, 271 insertions(+), 261 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/ScatterTemplate.py create mode 100644 DeeployTest/Tests/Kernels/FP32/ScatterElements/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/ScatterElements/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/ScatterElements/outputs.npz create mode 100644 DeeployTest/Tests/Kernels/Integer/ScatterElements/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/Integer/ScatterElements/network.onnx create mode 100644 DeeployTest/Tests/Kernels/Integer/ScatterElements/outputs.npz create mode 100644 TargetLibraries/Generic/inc/kernel/Scatter.h create mode 100644 TargetLibraries/Generic/src/Scatter.c diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 98c233aab5..14e9a14db6 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -22,13 +22,13 @@ FloatSqrtTemplate, FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, \ ITAMaxTemplate, ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, \ ReduceMeanTemplate, ReduceSumTemplate, RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, \ - RQSiGELUTemplate, SliceTemplate, SubTemplate, TransposeTemplate, iGELUTemplate, iLayernormTemplate, \ - iRMSNormTemplate, iSoftmaxTemplate + RQSiGELUTemplate, ScatterTemplate, SliceTemplate, SubTemplate, TransposeTemplate, iGELUTemplate, \ + iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate from Deeploy.Targets.Generic.TypeCheckers import AddChecker, BatchNormChecker, ConcatChecker, ConvChecker, \ DebugPrintChecker, DequantChecker, DivChecker, DummyChecker, GatherChecker, GELUChecker, GEMMChecker, \ - LayerNormChecker, MatMulChecker, MaxPoolChecker, MulChecker, PadChecker, QuantChecker, ReduceMeanChecker, \ - ReduceSumChecker, ReluChecker, RequantShiftChecker, ReshapeChecker, RQIntegerDivChecker, SliceChecker, \ - SoftmaxChecker, TransposeChecker + LayerNormChecker, MatMulChecker, MaxPoolChecker, MulChecker, PadChecker, PassThroughTypeChecker, QuantChecker, \ + ReduceMeanChecker, ReduceSumChecker, ReluChecker, RequantShiftChecker, ReshapeChecker, RQIntegerDivChecker, \ + SliceChecker, SoftmaxChecker, TransposeChecker BasicTransformer = CodeTransformation([ArgumentStructGeneration(), MemoryManagementGeneration(), FutureGeneration()]) @@ -436,3 +436,10 @@ NodeBinding(DummyChecker([PointerClass(float32_t)], [PointerClass(float32_t)]), FloatGlobalMaxPoolTemplate.referenceTemplate, BasicTransformer) ] + +BasicScatterBindings = [ + NodeBinding( + PassThroughTypeChecker( + [PointerClass(type), PointerClass(int32_t), PointerClass(type)], [PointerClass(type)]), + ScatterTemplate.referenceTemplate, BasicTransformer) for type in (int8_t, uint8_t, float32_t) +] diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index 634f78e6dd..6d2559a302 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -814,3 +814,15 @@ def computeOps(self): opRep = self.mapper.parser.operatorRepresentation # (spatial_size - 1) comparisons per output channel return int(opRep['batch_size'] * opRep['num_channels'] * (opRep['spatial_size'] - 1)) + + +class ScatterLayer(ONNXLayer): + + def computeOps(self): + opRep = self.mapper.parser.operatorRepresentation + if opRep.get('reduction', 'none') == 'none': + # no arithmetic operations + return 0 + else: + # 1 op per index element + return int(np.prod(opRep['indices_shape'])) diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index 3e35a264f7..a0c0ef411a 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -3152,3 +3152,46 @@ class GlobalMaxPoolParser(GlobalPoolParser): def parseNode(self, node: gs.Node) -> bool: return super().parseNode(node) and node.op == 'GlobalMaxPool' + + +class ScatterParser(NodeParser): + + def parseNode(self, node: gs.Node) -> bool: + + if not all([ + node.op == 'Scatter' or node.op == 'ScatterElements', + len(node.inputs) == 3, + len(node.outputs) == 1, + ]): + return False + + axis = node.attrs.get('axis', 0) + reduction = node.attrs.get('reduction', 'none') + + if reduction not in ('none', 'add', 'mul', 'max', 'min'): + return False + + self.operatorRepresentation['axis'] = axis + self.operatorRepresentation['reduction'] = reduction + + return True + + def parseNodeCtxt(self, + ctxt: NetworkContext, + node: gs.Node, + channels_first: bool = True) -> Tuple[NetworkContext, bool]: + + data_in = ctxt.lookup(node.inputs[0].name) + indices = ctxt.lookup(node.inputs[1].name) + updates = ctxt.lookup(node.inputs[2].name) + data_out = ctxt.lookup(node.outputs[0].name) + self.operatorRepresentation['data_in'] = data_in.name + self.operatorRepresentation['indices'] = indices.name + self.operatorRepresentation['updates'] = updates.name + self.operatorRepresentation['data_out'] = data_out.name + + self.operatorRepresentation['ndim'] = len(data_in.shape) + self.operatorRepresentation['data_shape'] = list(data_in.shape) + self.operatorRepresentation['indices_shape'] = list(indices.shape) + + return ctxt, True \ No newline at end of file diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index d8c6da5c43..de751b3cfc 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -16,15 +16,16 @@ BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, \ BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, \ BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, \ - BasicRQSGELUBinding, BasicSeluBindings, BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, \ - BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, DummyBinding + BasicRQSGELUBinding, BasicScatterBindings, BasicSeluBindings, BasicSigmoidBindings, BasicSliceBindings, \ + BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, \ + DummyBinding from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \ ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, ExpLayer, \ FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, \ InstanceNormLayer, ITAMaxLayer, LayerNormLayer, LeakyReluLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, \ PowLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, \ - RQIntegerDivLayer, RQSiGELULayer, SeluLayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, SubLayer, \ - SwishLayer, TransposeLayer + RQIntegerDivLayer, RQSiGELULayer, ScatterLayer, SeluLayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, \ + SubLayer, SwishLayer, TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ CeilParser, ClipParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, DummyParser, \ EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ @@ -32,9 +33,9 @@ GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, LeakyReluParser, \ MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, \ - ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, SeluParser, \ - SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, \ - iLayerNormParser, iSoftmaxParser + ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, ScatterParser, \ + SeluParser, SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, \ + UnsqueezeParser, iLayerNormParser, iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ @@ -99,6 +100,7 @@ AveragePool2DMapper = NodeMapper(AveragePool2DParser(), BasicAveragePool2DBindings) GlobalAveragePoolMapper = NodeMapper(GlobalAveragePoolParser(), BasicGlobalAveragePoolBindings) GlobalMaxPoolMapper = NodeMapper(GlobalMaxPoolParser(), BasicGlobalMaxPoolBindings) +ScatterMapper = NodeMapper(ScatterParser(), BasicScatterBindings) # Dummy nodes are intended for development purposes only! # They should always generate compiler errors to not accidentally end up in production code @@ -162,6 +164,8 @@ 'AveragePool': AveragePoolLayer([AveragePool1DMapper, AveragePool2DMapper]), 'GlobalAveragePool': GlobalAveragePoolLayer([GlobalAveragePoolMapper]), 'GlobalMaxPool': GlobalMaxPoolLayer([GlobalMaxPoolMapper]), + 'Scatter': ScatterLayer([ScatterMapper]), + 'ScatterElements': ScatterLayer([ScatterMapper]), # # For example, you can use the DummpyMapper, in case you want to test # # deployment or optimizations with GlobalAveragePool nodes but did not yet # # implement the corresponding kernel diff --git a/Deeploy/Targets/Generic/Templates/ScatterTemplate.py b/Deeploy/Targets/Generic/Templates/ScatterTemplate.py new file mode 100644 index 0000000000..71172ab3d6 --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/ScatterTemplate.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import Dict, List, Tuple + +from Deeploy.AbstractDataTypes import FloatImmediate +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + +_REDUCTION_MAP = { + "none": "SCATTER_REDUCTION_NONE", + "add": "SCATTER_REDUCTION_ADD", + "mul": "SCATTER_REDUCTION_MUL", + "min": "SCATTER_REDUCTION_MIN", + "max": "SCATTER_REDUCTION_MAX", +} + + +def _typeSuffix(ref_type) -> str: + if issubclass(ref_type, FloatImmediate): + return f'fp{ref_type.typeWidth}' + elif ref_type.signed: + return f's{ref_type.typeWidth}' + else: + return f'u{ref_type.typeWidth}' + + +class _ScatterTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> Tuple[NetworkContext, Dict, List[str]]: + + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['type_suffix'] = _typeSuffix(data_in._type.referencedType) + operatorRepresentation['reduction_c'] = _REDUCTION_MAP[operatorRepresentation['reduction']] + + return ctxt, operatorRepresentation, [] + + +referenceTemplate = _ScatterTemplate(""" +// Scatter (Name: ${nodeName}, Op: ${nodeOp}) +BEGIN_SINGLE_CORE +Scatter_${type_suffix}(${data_in}, ${indices}, ${updates}, ${data_out}, ${ndim}, + (int32_t[]){${', '.join(str(s) for s in data_shape)}}, + (int32_t[]){${', '.join(str(s) for s in indices_shape)}}, + ${axis}, ${reduction_c} +); +END_SINGLE_CORE +""") diff --git a/Deeploy/Targets/Generic/TypeCheckers.py b/Deeploy/Targets/Generic/TypeCheckers.py index c2c8d436f8..4224cba538 100644 --- a/Deeploy/Targets/Generic/TypeCheckers.py +++ b/Deeploy/Targets/Generic/TypeCheckers.py @@ -2,59 +2,22 @@ # # SPDX-License-Identifier: Apache-2.0 -from typing import List, Optional, Sequence, Type +from typing import List, Optional import numpy as np -from Deeploy.AbstractDataTypes import Pointer from Deeploy.CommonExtensions.TypeCheckers.SignPropTypeChecker import SignPropTypeChecker from Deeploy.DeeployTypes import ConstantBuffer, OperatorRepresentation, VariableBuffer -class ConcatChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> Optional[List[int]]: - - maxNLevel = max(i.nLevels for i in inputs) - - return [maxNLevel] - - def _inferSignedness(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> Optional[List[bool]]: - assert (all([_inp._signed == True for _inp in inputs]) or all( - [[_inp._signed == False for _inp in inputs]])), "Some inputs in concat operation have different signs!" - - if inputs[0]._signed: - return [True] - else: - return [False] - - -class SliceChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) +class DummyChecker(SignPropTypeChecker): def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> Optional[List[int]]: - return [inputs[0].nLevels] - - def _inferSignedness(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> Optional[List[bool]]: - if inputs[0]._signed: - return [True] - else: - return [False] - + operatorRepresentation: OperatorRepresentation) -> List[int]: + return [2**(self.input_types[0].referencedType.typeWidth)] -class TransposeChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) +class PassThroughTypeChecker(SignPropTypeChecker): def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> Optional[List[int]]: @@ -62,75 +25,54 @@ def _inferNumLevels(self, inputs: List[VariableBuffer], def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> Optional[List[bool]]: - if inputs[0]._signed: - return [True] - else: - return [False] + return [bool(inputs[0]._signed)] -class PadChecker(SignPropTypeChecker): +SliceChecker = PassThroughTypeChecker +TransposeChecker = PassThroughTypeChecker +PadChecker = PassThroughTypeChecker +GatherChecker = PassThroughTypeChecker +ReshapeChecker = PassThroughTypeChecker +MaxPoolChecker = PassThroughTypeChecker +DebugPrintChecker = PassThroughTypeChecker - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [inputs[0].nLevels] +class SignedOutputTypeChecker(SignPropTypeChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: - if inputs[0]._signed: - return [True] - else: - return [False] - + return [True] -class AddChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) +class ConcatChecker(SignPropTypeChecker): def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [inputs[0].nLevels + inputs[1].nLevels] - - def _inferSignedness(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[bool]: - if inputs[0]._signed or isinstance(inputs[1], ConstantBuffer): - return [True] - else: - return [False] - - -class GatherChecker(SignPropTypeChecker): + operatorRepresentation: OperatorRepresentation) -> Optional[List[int]]: - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) + maxNLevel = max(i.nLevels for i in inputs) - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [inputs[0].nLevels] + return [maxNLevel] def _inferSignedness(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[bool]: + operatorRepresentation: OperatorRepresentation) -> Optional[List[bool]]: + assert (all([_inp._signed == True for _inp in inputs]) or all( + [[_inp._signed == False for _inp in inputs]])), "Some inputs in concat operation have different signs!" + if inputs[0]._signed: return [True] else: return [False] -class ReshapeChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) +class AddChecker(SignPropTypeChecker): def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: - return [inputs[0].nLevels] + return [inputs[0].nLevels + inputs[1].nLevels] def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: - if inputs[0]._signed: + if inputs[0]._signed or isinstance(inputs[1], ConstantBuffer): return [True] else: return [False] @@ -138,9 +80,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class MHSAChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [operatorRepresentation['n_levels']] @@ -150,28 +89,14 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [True] -class CLCAChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class CLCAChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: return [True] -class LinearAttentionChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class LinearAttentionChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: @@ -180,9 +105,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class GEMMChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [ @@ -195,14 +117,7 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [True] -class LayerNormChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class LayerNormChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: @@ -211,9 +126,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class MulChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [2**(self.input_types[1].typeWidth)] @@ -228,9 +140,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class DivChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [2**(self.output_types[0].referencedType.typeWidth)] @@ -245,9 +154,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class RQIntegerDivChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [2**(self.output_types[0].referencedType.typeWidth)] @@ -262,9 +168,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class MatMulChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [np.max(inputs[0].shape) * np.max(inputs[1].shape) * 2**(self.input_types[0].referencedType.typeWidth)] @@ -281,9 +184,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class RQMatMulChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [operatorRepresentation['n_levels']] @@ -295,9 +195,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class RQGEMMChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [operatorRepresentation['n_levels']] @@ -307,14 +204,7 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [bool(operatorRepresentation["signed"])] -class ReduceMeanChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class ReduceMeanChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: @@ -326,9 +216,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class ReduceSumChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [operatorRepresentation['axisLength'] * 2**(self.input_types[0].referencedType.typeWidth)] @@ -343,9 +230,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class ReluChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs, operatorRepresentation): return [2**(self.input_types[0].referencedType.typeWidth)] @@ -354,14 +238,7 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [False] -class SoftmaxChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class SoftmaxChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: @@ -370,9 +247,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class iNoNormChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [2**(4 * self.input_types[0].referencedType.typeWidth)] @@ -385,14 +259,7 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [False] -class GELUChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class GELUChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: @@ -404,9 +271,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class HardswishChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [2**(4 * self.input_types[0].referencedType.typeWidth)] @@ -419,31 +283,7 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [False] -class RQHardswishChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] - - def _inferSignedness(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[bool]: - if inputs[0]._signed: - return [True] - else: - return [False] - - -class MaxPoolChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [inputs[0].nLevels] +class RQHardswishChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: @@ -455,9 +295,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class ConvChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: weight = inputs[1] @@ -476,9 +313,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class RequantShiftChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [operatorRepresentation['n_levels']] @@ -488,38 +322,8 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [operatorRepresentation["signed"]] -class DummyChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] - - -class DebugPrintChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [inputs[0].nLevels] - - def _inferSignedness(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[bool]: - if inputs[0]._signed: - return [True] - else: - return [False] - - class RQAddChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [operatorRepresentation['rqsOut_n_levels']] @@ -540,9 +344,6 @@ def checkOutputType(self, inputs: List[VariableBuffer], operatorRepresentation: class QuantChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: # Calculate number of levels based on bit_width @@ -557,9 +358,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class DequantChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[int]: return [2**(self.output_types[0].referencedType.typeWidth)] @@ -571,9 +369,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class SoftmaxCrossEntropyLossChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> Optional[List[int]]: @@ -586,9 +381,6 @@ def _inferSignedness(self, inputs: List[VariableBuffer], class SGDChecker(SignPropTypeChecker): - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - def _inferNumLevels(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> Optional[List[int]]: return [2**(self.input_types[0].referencedType.typeWidth)] @@ -598,14 +390,7 @@ def _inferSignedness(self, inputs: List[VariableBuffer], return [True] -class BatchNormChecker(SignPropTypeChecker): - - def __init__(self, input_types: Sequence[Type[Pointer]], output_types: Sequence[Type[Pointer]]): - super().__init__(input_types, output_types) - - def _inferNumLevels(self, inputs: List[VariableBuffer], - operatorRepresentation: OperatorRepresentation) -> List[int]: - return [2**(self.input_types[0].referencedType.typeWidth)] +class BatchNormChecker(DummyChecker): def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> List[bool]: diff --git a/DeeployTest/Tests/Kernels/FP32/ScatterElements/inputs.npz b/DeeployTest/Tests/Kernels/FP32/ScatterElements/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..2efbcd30c94c23f2855243ff3d5f41ef4a09dfbf GIT binary patch literal 4050 zcmd5@uiyFSto^Ka-9Br5*M9b1&%3@-Pemnb zxd9p@*K|$&=XdW(MNUsHC}4Gf&C1BH<>j>H5>y6IgL3Dr`7@=y@^Z0qYsUozM+ZiY zb2b_0vdm$emC3ke5mBq70#<$*5fv0H`rQLoLSmmJ5P>^Ee#hwh?fg%`>_i^oihQ1VT71y9{^ur|ue zwfmS||7N$!`EcPaUt*eB(?*ZaA_<-*tiam^YA=phGcH{_KjUKh6K~G^Dy5V@tjn zO~#WdF~w*D_k-~Pv^I*Ph?EK3^A{F$JS`i(GtwpLn>Eb1@D*l^o`r`k6qrMo8>#Np zfp5opv8lNU^z*z<&VH^r{9*G75}!(~$r#H38ip7Q_Dh>ljt22XA*w zfg{;%sQuY)7Q8+Fc*d9X55C0g(a zjSb2JGbJtTjBkVe5!o<)l{5eSH$%zHKZ-WqPe+3xs%*5fD#Utu(CAxagD6INh3^&L7Xb!~IA)OrOesDg>Xe3ZbH<5c-S^r}`eN zf`;->Sx12Zd%3Cwsv4%_+1uUFI`akk?HGam(tK3S_2L|@PO+ST-4u~`6ILiCQmIll zhE1_y>FHy+6_Hj{_OK0$ zS@xA6ifTziUEdNeJ#QK7s0}7tBWG5XdD%5bZ4@Qn*$MZ5e9N8FL0o@)B6mJF78lR_ zmaDt>GjM0uu}%$l+EjR$)m!eNuFvcF?y0pkZCa%2z2Md%)W$H=%;^U`~>U9>}RlMu2S!Z$M!)LI~Gtc1U z;m1G{B99NG>ezm10@Kt{VvB_TZs=Vplj<#yWI5dcqfB=UT{9azb{F&Rv*S3o=sK9w zp+F;oN?2U430Y2?N|R?eF@^bW;9+$mXb-;tel4%gY|YRj53Q-3{_$3Pq-4y_O{wLV z=qb_7V>8jmG#R{tD$qP#k*b~>!JI~8xG?!3#+=SV?Ib;kB#{UO@qtKi6f zcgWUU!Ol3TvO_f&`Rk{q*5!qmknT>`x~pd4xheH1F(2)kNfGo>~a~BeIyEjsfe=uR$GNi6gr-}$hxJ(>J-4R=;=w1F;Nf$ zNslE}{MWB?xnI63g!R=Az~@hUlE%bPq3~PHE1L&O$xqRCLL76`-o+`EyRav%R@~@Y zg)AUEjZOFd2c|_EG5eHeFz;W;+yV{Bd&^DG@wH&DPo&YbIj$6MT0ncltWaz3L)hjY z$*9bg)A(W|>uK>L_i3qAXRpbBekleH2ba`M3NE5bvmkzWwUl;xUW1r@p%5RSPp={h zG4N{(@{cj$4_q-|3#v!aI?n@8SyqZ>HE*!$T`Iq+z>InBl5 zjO$i`LyZ}HY1lB_l6nqQLb{=(z7$nHtHC%GTl5)ML(VIs=#!HRSyxUH(^)EfrwujW zZ0`Z_vwK<77<;B#TfleSHz4KY1X?>u0hmfRCS?Q?JZeS<4Qndx+r(aI4P$v81!t0) zRxkr?ch0_9l`Kz2vyIW;L*8>UvOf5XpF2{6;S3sdd3rg?h z?A$-I&cRKci2de?0O|nC!51h<=V8t_agl9uVm7+wBVi2 zG78OV!N9U^N$O!$+C3^3Nq!7_omYd;DwWxf=s+1;N};%T8S^;ZK&A5U5KC*|dAB@n zO$~=QcfxOuEas$cjr_t^XD0pSW4NO21HBiMutH@IT83>!wdij^qk0oouXX}Qm15== zpw3Q|Nb!^%ur0ecVv|MzP4XN=S4wwN-wS_~{%vzPx?~6Z`*Sc@ernIXQrVu4GR^2NX1s;k^{}K}-Tapv!E*pTG$i zfy5lqC&ope$RGMdOoD)s<%{xto(wPA0?Q3D{5^N@OtGKA+Jm~7BgSR6AZ0xWK3Oe6 z$Rvo$`!ism_Fv};Ie+zv0~ipbNTQVCKBzxH1n=?qL9VC^@xO2)Mqn9MkYY^KWpNy^WpU9h_(c+X5HTW&KG7cRPnIY2CX(or z5ex_!1_fQPMP1At%#+m-Y%wN>13ZZN(3a^!7(@&VND-I7|9x2W4*2c=a9Cm@g+>2= zcUX?*xkw^LZDQxAc)?014P0HJ#5CK=X~d8E;BmAU+A>#=rQ9UO3Va~2Eg9FZYl5bd zO4fDZF&BGlG_=0W#QiUaQu+-gX7^Hsia(viow%NZL)~*>;3NkX_>0CKiYj*){JCm9Gk^j_Pjt%GY_(xkxnlcN3eszm!Z#7 zlh?Ab2NgXBk}EyS>_dvUbwkp)OOnUn(U}Vt)3xBpv)5RDI}3ix$YLRG3s~pl^{gqY z04I5mrknBy_}c?2TKdCkrUFg;$h=g%_;d`d2}~xPN=0_~_!RD9&@2jf zoJ}v;@kUh!f`uFut{Tql^?N(QJ&2IX*={5 zIZ#Dw3GT?<1B}poy)& z!-UEkTqcaC5%``G{LY)t0RqClcL@cT(d=3dxPfN7^4elo#&j0`b literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/ScatterElements/network.onnx b/DeeployTest/Tests/Kernels/FP32/ScatterElements/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..17031f8c197d7846e09d80e791a30d4a7db6c7a8 GIT binary patch literal 211 zcmd02D|q7GldUEh#81QQ{9yPAn-& zEpp9C%}vcKDHamX&&#WbPcKR=$S5u?N>9ztgK(mxAZ7^(aS3oR3h{6;ac}^!01&f8 o$-->_OS6HcS%6Z!K+Fu1hS`hI4VO-m;exsbo7qk*Tnqx-00F)$jQ{`u literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/ScatterElements/outputs.npz b/DeeployTest/Tests/Kernels/FP32/ScatterElements/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..c7dae2412b8b4b10dc7da896e4d8154b7881fb35 GIT binary patch literal 2314 zcmbW3e_YOo7RP&1Jkn2ErEoJUTBVd!>iM2y{jjBZVv>YX$?eC=ldNUvr$Wk&ekq1h zdo&59y3+TY=$fd-#$ppf!$M1{jb_~KzV7S(dq3xO&gXSLuk+ve=PmahrDm=2r>3fG z+F2F&`Y&my=&QuUZ-|ZGVDm}rXA%`%l_cq(cmMG{A1(EfFPErnRM}(^8WtC#uy8T8 za9!(QVP$HuHb${Q5&X&O7)5B<+uSoaDlY6z9=9$yHtfwl!^z6j&e6)$VWySoX4C&m z8fD!v^s&Jt{Cb`*G?qOPPpO5|7oh^{NK~=#wLfP2JBhWsO;Mo;hdHtBr0pFJCDsc# zxRW(}JB& zgJRkUKt#idChZXy9(~ghR~+aAzo{dTnxx0^iXgn|9mGp7oqs5PeX?I-4o_`^I@zTQ4ESwI3cRyz$;NV^lLtgZzw9xVWhRcNdj_?(APEb+I-V zWTBDA>d{CVN%sCNY2 zJC0oW{1qj{oq`J=SYv`m1^Gn>vRuo6tG_7&|G$?(rp{+PY+^h=Vb(Nish)$6OO5c? z^&N05EQ8N=^+Tu65C!fwq=$K*ierm>1xKp}EDX+Lv1gc~)RH;LCJQ6y*%~AH2kFe7>)X84>Xr}4)@7A~QC@^RGDHVd`& z)NsX2RgAbJN4fqoS-$g4Fv<0#h))-RS6;d3GtQ3P}l6D==Zulr25xv(rLn zR9!j(=A9Ezcl<>N?D*~MjvO5hcVC7ECpzhR$X(hS5X`Ie)%b_wKIGTS;2T;)wrjHR z^a~SMd|?t?bUIA&XYxrmRUanq%cOd{U>@jsKsLov%BiNN0_`Zg9QqM|S$|o)(J;Ts z`Sd!Pu*a?G+F2)h+T)KkofXt&TMn;W<8hngPH|T4O`)jsM|c^MMDj=-aPC);scIz( zgMmXd@G?=R9{2@x+N{IIO%u@uzrxA8UJ&Mv!<1u>1@Th-JyC}vvPJ}UUBX^zx!6_finrg*r&T8_n)V;? z$H*PFN4_x_gCa$3^_8(@MxmPsiZsEd0RlZv-8gY@gl^<}U+q zcIOlD+x`RCEzIUuC$rdNu^T5&E9L!>R;0840qhKlMXquaG(SwifsR1-^vL8Udu?$+ zTRa>Mt7tm5&sA1y7AlUflk*<$>tGBHm>6upzr~bM$Y(Rz@R9)>x@v^?bE9~(_aX4= zEdsOp5jy=kQ`}Z+#siioNM9*%uJwLAl2s$?eYgb^`lT?}Zwf0TwP<_hd60(pLq)Ta zr0>^Lg4CA$E$Z3j69vC_DggTmQ?X@r6>U^(1s8iSNL)0CtyArBvSY5$*KNdi*O0Jj zt}1k2&=XU0LKuE(CkIVyzA!x>hjg^CqhyFuTcglO*Hf@>AIp}f;xHxdE7+;s+m*MHpXgC$yV9qb*6=tlGjAadU(+-Nr4sd&33bksM@?7t!d{vpp!$UzN s_m-$ykNRJFdCMkmTjMXLd230MxAVU&BllJx{Wg)jDW^By_;0TH1NQ3@{Qv*} literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/Integer/ScatterElements/inputs.npz b/DeeployTest/Tests/Kernels/Integer/ScatterElements/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..c034759e7c9c165e65c79dcaf9573ce44f4ca9d9 GIT binary patch literal 4050 zcmd6qZD^KN7{|}%OH)&q3@h>F(X450Hg%bq<-Pfm`1(|uT9!^XnWb}X^DU=6NQ8)p z&fgh<5b(<)Jkh#?UYQvH7S^W0-fpYls*{P(}E>s;rY>%PwG zlb+wJcX`N_(V?R%^W)$7QWQ!;ZB0wfgoef)1)(@(igI2FzrSed!t{!Qur=%~t*vWb z+f+I&RXTlLWocQebls+=mZqA9Rhyb>>$K0Q+0a~vy}7=ou?~IQ)UwpXDP^h3@nxx9 zssB{^htPLs2q&sTXn|LPxgekqhgX0D$iIPN>fCrJkg}1@>@d&&Wj6*io{oqw}eU00Gi{QuLaxfS~;1F?J=QwNN+i+); z8~XyX2O($4gK_moc8!{Y@6oGZbM_LvQ#&@t z=#4xC)FVgK+n@8f8FtUPH!Feba6fkAI1C=f=GuD*xe~bUo6tL8zsnqbj_mmEzp=nM z@cS=;8tkqSW3)KuaxHekQ-5(RcN64>Ky+zvU6uYrJxJ@ z8rYnegM;YK$6v_iFreGl4s7;ke*Hf8*Kl<6atj~VvfpgJT#w6v{VfO9xgN^EW#mtR z`Oy|LLz!5o|G+ z=E1e+8g;Ilf$@J0jNuh<0@-~NqFiiQ_!De?*9h#MpU&BA*t|KPtW~37|W2&wQbGg5qK2b04&CH2K)*ZV{;!a1LnZ>IS81) zh`wAWlk;%QbF}ep=>$vAPvCP3+3z=J!$Ag{=auK^9N;%S1KhWkJ@_sFexLEX_9|%O zy3pPVc42d0+(6C%=j9!AyTZaHu-nv+6g<8Y6=kBqM!WTOy1hA-r0^ITG$zT@Tc z#fC*cCoh))yj<2d)UID!*PJ|Fx(ef{L&Ep;c&X&^GPx`@`JNsxA65`HZZGHC#s2CbB#7>8n~Z5ZN#BWV8|z~oj$^8Zyu)sa3?j`ho&y>qjC_}R(pJvYmiMjrbA=grbtojdWpSq~yV4J&)& zdZM(!9pEQW%qipvUkyB2Jc(_88F(7TryCQgEh0(25Il*708esnId24C2etTkJ1qg8 zNR@D!K90i?Z9RPhy0^EdvA4|u;0Zhw=;!U_&C!XUr?jWCW2^w4Xk%c<-Uz(aJ%#@S zj`uD+0X9BQ>UR1l!R9S<444CN5N{e!=|k9TYu|5z(cmijDA;)HzZLeDxP+0-gt)?doS?CU~Hc3p5krT=D~i`4q)D$AN?HvG;MzeX=Gn!jmW-k zXTiH+$CJ;)&Y@%ajSs-XfaCbx&a1B*`*Ti>`6Ao}u7UaJzRn%<9QsJO3iw*}do9zk zn{VUr1$2QvlRTKm6t;dd)AI`o%X{7VtJq(?BL5>e?k!UwCBKpHZsm>Sbt4AE{<(jd kf>U(T<9@okp}X_n8(ycM9(y%CzfWKL?8V02D|q7GldUEh#81QQ{9yPAn-& zEpp9C%}vcKDHamX&&#WbPcKR=$S5u?N>9ztgK(mxAZ7^(aS3oR3Gr|-ac}^!01&f8 o$-->_OS6HcS%6Z!K+Fu1hS`hI4VO-m;exsbo7qk*Tnqx-00MU_kN^Mx literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/Integer/ScatterElements/outputs.npz b/DeeployTest/Tests/Kernels/Integer/ScatterElements/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..41dbf1eddee473d0073074c548413483ee8a7619 GIT binary patch literal 2314 zcmbW3e`wTo7{@=iXZ8@BS?%%n}bNK;F5)7@BP+j+a$%qd4i z43P+w5Q&fw86q+yLSn=r5)vXI5z=2HLL?$aNJxnFdUdy=fBTLX@8@~Gzn;(YJfH6_ z-B~%SF_eylq2o^dkw42vRj3Jr!};uRzG)!4FBYmpAzr#}MvrXRv_8Ex7WRhyiQdf6 zu3TbelxXW~NhG60-(W7E>lxTFnCs0LU)!^LC_{XxzbBi)Z)i_!_Q)d3){aoIi&hVaKsw2G=kf-2nD{0DTI&x#kT1b@&0i z?<~yX+!W{Pv1`Gcisb$~_80ICxYiHAydHsTT>l6D8`$=u3)}c}(9F3Y*sk4LWS|eM z%|fVyEI6lC;0*W->!^JKMzLRkb=c0Vy=_L&M*7APn$4^-U z4*Tgoc^dN20?y%K;^yIAd5DtisEJ&4W$_n+6BOB_6mTZdo3eQd9Xz`lMC=I|;^V7qTZ zs`UIK`Ux@j=mO$?KkeCk)VeuS4dC2bKlfGzapz#19G#1J8TXIh6!W+Q=HWcw!mdZV zx!!ko<9okxzYEUiI@Eo!AMDwisJ*w>V^D|hOk3+S;CD2TyZd*f9J2-&vG<_;sC_fX zB=*l>-R#Rr)ZRJg9_His!MRc0hnv9}bUx>nZTm3~`$=%m+s6yUu7LI32ouD<#2>-7 zmbFkI=J(3)sI~Q+-#+*4`&?^}y5I!YnZGmCjBPEAkHP`$ng1aL{7G0#OnW)%_p}l9 zf2gxvftrVV+&P;<&qFa4!g198Xj|u+_H<{gqOtP7zh3-pBK$b`=P1N9|NSkK?woy> Okr;oi=;YtOYyJYIsgT. + * The matching definition lives in Scatter.c via DEFINE_SCATTER_FN. + */ +#define DECLARE_SCATTER_FN(SUFFIX, DATA_TYPE) \ + void Scatter_##SUFFIX( \ + const DATA_TYPE *data, const int32_t *indices, const DATA_TYPE *updates, \ + DATA_TYPE *output, int32_t ndim, const int32_t *data_shape, \ + const int32_t *indices_shape, int32_t axis, int32_t reduction) + +DECLARE_SCATTER_FN(fp32, float32_t); +DECLARE_SCATTER_FN(s8, int8_t); +DECLARE_SCATTER_FN(u8, uint8_t); + +#endif //__DEEPLOY_BASIC_MATH_SCATTER_KERNEL_HEADER_ diff --git a/TargetLibraries/Generic/src/Scatter.c b/TargetLibraries/Generic/src/Scatter.c new file mode 100644 index 0000000000..f7e5185013 --- /dev/null +++ b/TargetLibraries/Generic/src/Scatter.c @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "DeeployBasicMath.h" + +// clang-format off +#define DEFINE_SCATTER_FN(SUFFIX, DATA_TYPE) \ + DECLARE_SCATTER_FN(SUFFIX, DATA_TYPE) { \ + int32_t data_size = 1; \ + for (int32_t dim = 0; dim < ndim; dim++) { \ + data_size *= data_shape[dim]; \ + } \ + int32_t indices_size = 1; \ + for (int32_t dim = 0; dim < ndim; dim++) { \ + indices_size *= indices_shape[dim]; \ + } \ + memcpy(output, data, (size_t)data_size * sizeof(DATA_TYPE)); \ + int32_t stride_data[SCATTER_MAX_NDIM]; \ + int32_t stride_idx[SCATTER_MAX_NDIM]; \ + stride_data[ndim - 1] = 1; \ + stride_idx[ndim - 1] = 1; \ + for (int32_t dim = ndim - 2; dim >= 0; dim--) { \ + stride_data[dim] = stride_data[dim + 1] * data_shape[dim + 1]; \ + stride_idx[dim] = stride_idx[dim + 1] * indices_shape[dim + 1]; \ + } \ + for (int32_t fi = 0; fi < indices_size; fi++) { \ + int32_t out_idx = 0; \ + int32_t rem = fi; \ + for (int32_t dim = 0; dim < ndim; dim++) { \ + int32_t coord = rem / stride_idx[dim]; \ + rem -= coord * stride_idx[dim]; \ + if (dim == axis) { \ + int32_t scatter_idx = indices[fi]; \ + if (scatter_idx < 0) scatter_idx += data_shape[dim]; \ + out_idx += scatter_idx * stride_data[dim]; \ + } else { \ + out_idx += coord * stride_data[dim]; \ + } \ + } \ + switch (reduction) { \ + case SCATTER_REDUCTION_ADD: \ + output[out_idx] += updates[fi]; \ + break; \ + case SCATTER_REDUCTION_MUL: \ + output[out_idx] *= updates[fi]; \ + break; \ + case SCATTER_REDUCTION_MIN: \ + if (updates[fi] < output[out_idx]) \ + output[out_idx] = updates[fi]; \ + break; \ + case SCATTER_REDUCTION_MAX: \ + if (updates[fi] > output[out_idx]) \ + output[out_idx] = updates[fi]; \ + break; \ + default: \ + output[out_idx] = updates[fi]; break; \ + } \ + } \ + } +// clang-format on + +DEFINE_SCATTER_FN(fp32, float32_t) +DEFINE_SCATTER_FN(s8, int8_t) +DEFINE_SCATTER_FN(u8, uint8_t) \ No newline at end of file From e78af065e7ca6ced734e3779deac8bd6f627108e Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Fri, 5 Jun 2026 14:46:07 +0000 Subject: [PATCH 05/12] add support for Col2Im operator for Generic target --- Deeploy/Targets/Generic/Bindings.py | 33 ++++--- Deeploy/Targets/Generic/Layers.py | 13 +++ Deeploy/Targets/Generic/Parsers.py | 83 ++++++++++++++++++ Deeploy/Targets/Generic/Platform.py | 42 ++++----- .../Generic/Templates/Col2ImTemplate.py | 42 +++++++++ .../Tests/Kernels/FP32/Col2Im/inputs.npz | Bin 0 -> 4872 bytes .../Tests/Kernels/FP32/Col2Im/network.onnx | Bin 0 -> 228 bytes .../Tests/Kernels/FP32/Col2Im/outputs.npz | Bin 0 -> 1066 bytes .../Tests/Kernels/Integer/Col2Im/inputs.npz | Bin 0 -> 4872 bytes .../Tests/Kernels/Integer/Col2Im/network.onnx | Bin 0 -> 228 bytes .../Tests/Kernels/Integer/Col2Im/outputs.npz | Bin 0 -> 1066 bytes .../Generic/inc/DeeployBasicMath.h | 1 + TargetLibraries/Generic/inc/kernel/Col2Im.h | 44 ++++++++++ TargetLibraries/Generic/src/Col2Im.c | 80 +++++++++++++++++ 14 files changed, 305 insertions(+), 33 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/Col2ImTemplate.py create mode 100644 DeeployTest/Tests/Kernels/FP32/Col2Im/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/Col2Im/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/Col2Im/outputs.npz create mode 100644 DeeployTest/Tests/Kernels/Integer/Col2Im/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/Integer/Col2Im/network.onnx create mode 100644 DeeployTest/Tests/Kernels/Integer/Col2Im/outputs.npz create mode 100644 TargetLibraries/Generic/inc/kernel/Col2Im.h create mode 100644 TargetLibraries/Generic/src/Col2Im.c diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 14e9a14db6..d5d49b253a 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -11,19 +11,19 @@ int8_t, int32_t, uint8_t from Deeploy.DeeployTypes import CodeTransformation, NodeBinding from Deeploy.FutureExtension.CodeTransformationPasses.FutureCodeTransformation import FutureGeneration -from Deeploy.Targets.Generic.Templates import AddTemplate, BatchNormalizationTemplate, ConcatTemplate, ConvTemplate, \ - ConvTransposeTemplate, DebugPrintTemplate, DequantTemplate, DummyTemplate, DWConvTemplate, FloatAddTemplate, \ - FloatAveragePoolTemplate, FloatCeilTemplate, FloatClipTemplate, FloatConvTemplate, FloatDivTemplate, \ - FloatDWConvTemplate, FloatEluTemplate, FloatExpTemplate, FloatFloorTemplate, FloatGELUTemplate, FloatGemmTemplate, \ - FloatGlobalAveragePoolTemplate, FloatGlobalMaxPoolTemplate, FloatGroupNormTemplate, FloatHardSigmoidTemplate, \ - FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, FloatLeakyReluTemplate, \ - FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, FloatPowTemplate, \ - FloatReduceMeanTemplate, FloatReluTemplate, FloatSeluTemplate, FloatSigmoidTemplate, FloatSoftmaxTemplate, \ - FloatSqrtTemplate, FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, IntegerDivTemplate, \ - ITAMaxTemplate, ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, PadTemplate, QuantTemplate, \ - ReduceMeanTemplate, ReduceSumTemplate, RequantShiftTemplate, ReshapeTemplate, RQIntegerDivTemplate, \ - RQSiGELUTemplate, ScatterTemplate, SliceTemplate, SubTemplate, TransposeTemplate, iGELUTemplate, \ - iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate +from Deeploy.Targets.Generic.Templates import AddTemplate, BatchNormalizationTemplate, Col2ImTemplate, ConcatTemplate, \ + ConvTemplate, ConvTransposeTemplate, DebugPrintTemplate, DequantTemplate, DummyTemplate, DWConvTemplate, \ + FloatAddTemplate, FloatAveragePoolTemplate, FloatCeilTemplate, FloatClipTemplate, FloatConvTemplate, \ + FloatDivTemplate, FloatDWConvTemplate, FloatEluTemplate, FloatExpTemplate, FloatFloorTemplate, FloatGELUTemplate, \ + FloatGemmTemplate, FloatGlobalAveragePoolTemplate, FloatGlobalMaxPoolTemplate, FloatGroupNormTemplate, \ + FloatHardSigmoidTemplate, FloatHardSwishTemplate, FloatInstanceNormTemplate, FloatLayernormTemplate, \ + FloatLeakyReluTemplate, FloatMatMulTemplate, FloatMaxPoolTemplate, FloatMulTemplate, FloatPadTemplate, \ + FloatPowTemplate, FloatReduceMeanTemplate, FloatReluTemplate, FloatSeluTemplate, FloatSigmoidTemplate, \ + FloatSoftmaxTemplate, FloatSqrtTemplate, FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, \ + IntegerDivTemplate, ITAMaxTemplate, ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, \ + PadTemplate, QuantTemplate, ReduceMeanTemplate, ReduceSumTemplate, RequantShiftTemplate, ReshapeTemplate, \ + RQIntegerDivTemplate, RQSiGELUTemplate, ScatterTemplate, SliceTemplate, SubTemplate, TransposeTemplate, \ + iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate from Deeploy.Targets.Generic.TypeCheckers import AddChecker, BatchNormChecker, ConcatChecker, ConvChecker, \ DebugPrintChecker, DequantChecker, DivChecker, DummyChecker, GatherChecker, GELUChecker, GEMMChecker, \ LayerNormChecker, MatMulChecker, MaxPoolChecker, MulChecker, PadChecker, PassThroughTypeChecker, QuantChecker, \ @@ -437,6 +437,13 @@ FloatGlobalMaxPoolTemplate.referenceTemplate, BasicTransformer) ] +BasicCol2ImBindings = [ + NodeBinding( + PassThroughTypeChecker([PointerClass(type), PointerClass(int32_t), + PointerClass(int32_t)], [PointerClass(type)]), Col2ImTemplate.referenceTemplate, + BasicTransformer) for type in (int8_t, uint8_t, float32_t) +] + BasicScatterBindings = [ NodeBinding( PassThroughTypeChecker( diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index 6d2559a302..12a8d3f33e 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -816,6 +816,19 @@ def computeOps(self): return int(opRep['batch_size'] * opRep['num_channels'] * (opRep['spatial_size'] - 1)) +class Col2ImLayer(ONNXLayer): + + def computeOps(self): + # Col2Im iterates over every element of the input tensor and adds it + # into the corresponding output position. The total number of + # accumulations is exactly the number of input elements which is + # N × C × block_volume × L + rep = self.mapper.parser.operatorRepresentation + block_volume = int(np.prod(rep['block_shape'])) + L = int(np.prod(rep['col_dims'])) + return rep['batch_size'] * rep['channels'] * block_volume * L + + class ScatterLayer(ONNXLayer): def computeOps(self): diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index a0c0ef411a..00f38b9302 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -3194,4 +3194,87 @@ def parseNodeCtxt(self, self.operatorRepresentation['data_shape'] = list(data_in.shape) self.operatorRepresentation['indices_shape'] = list(indices.shape) + return ctxt, True + + +class Col2ImParser(NodeParser): + + def parseNode(self, node: gs.Node) -> bool: + + if not all([node.op == 'Col2Im', len(node.inputs) == 3, len(node.outputs) == 1]): + return False + + # Deeploy is a static ahead-of-time code generator: shape tensors that + # appear as C compound literals in the emitted code must be known at + # parse time. + # image_shape / block_shape are therefore assumed to be constant and + # are not supported as variables + if not isinstance(node.inputs[1], gs.Constant) or not isinstance(node.inputs[2], gs.Constant): + return False + + image_shape = node.inputs[1].values.astype(int).tolist() + block_shape = node.inputs[2].values.astype(int).tolist() + spatial_dims = len(image_shape) + + if spatial_dims <= 0: + return False + + dilations = list(node.attrs.get('dilations', [1] * spatial_dims)) + pads = list(node.attrs.get('pads', [0] * (2 * spatial_dims))) + strides = list(node.attrs.get('strides', [1] * spatial_dims)) + + if not all([ + len(dilations) == spatial_dims, + len(pads) == 2 * spatial_dims, + len(strides) == spatial_dims, + all(s > 0 for s in image_shape), + all(s > 0 for s in block_shape), + all(d > 0 for d in dilations), + all(p >= 0 for p in pads), + all(s > 0 for s in strides), + ]): + return False + + col_dims = [(image_shape[p] + pads[p] + pads[p + spatial_dims] - dilations[p] * + (block_shape[p] - 1) - 1) // strides[p] + 1 for p in range(spatial_dims)] + if any(d <= 0 for d in col_dims): + return False + + self.operatorRepresentation['col_dims'] = col_dims + self.operatorRepresentation['image_shape'] = image_shape + self.operatorRepresentation['block_shape'] = block_shape + self.operatorRepresentation['spatial_dims'] = spatial_dims + self.operatorRepresentation['dilations'] = dilations + self.operatorRepresentation['pads'] = pads + self.operatorRepresentation['strides'] = strides + + return True + + def parseNodeCtxt(self, + ctxt: NetworkContext, + node: gs.Node, + channels_first: bool = True) -> Tuple[NetworkContext, bool]: + + data_in: VariableBuffer = ctxt.lookup(node.inputs[0].name) + data_out: VariableBuffer = ctxt.lookup(node.outputs[0].name) + + image_shape = self.operatorRepresentation['image_shape'] + block_shape = self.operatorRepresentation['block_shape'] + col_dims = self.operatorRepresentation['col_dims'] + + N, C = data_out.shape[0], data_out.shape[1] + block_volume = int(np.prod(block_shape)) + L = int(np.prod(col_dims)) + + if list(data_in.shape) != [N, C * block_volume, L]: + return ctxt, False + + if list(data_out.shape) != [N, C] + image_shape: + return ctxt, False + + self.operatorRepresentation['data_in'] = data_in.name + self.operatorRepresentation['data_out'] = data_out.name + self.operatorRepresentation['batch_size'] = N + self.operatorRepresentation['channels'] = C + return ctxt, True \ No newline at end of file diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index de751b3cfc..faf3a5a935 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -7,28 +7,28 @@ from Deeploy.DeeployTypes import ConstantBuffer, DeploymentEngine, DeploymentPlatform, NodeMapper, NodeTemplate, \ StructBuffer, TopologyOptimizer, TransientBuffer, VariableBuffer from Deeploy.Targets.Generic.Bindings import BasicAddBindings, BasicAveragePool1DBindings, BasicAveragePool2DBindings, \ - BasicBatchNormBindings, BasicCeilBindings, BasicClipBindings, BasicConcatBindings, BasicConv1DBindings, \ - BasicConv2DBindings, BasicConvTransposeBindings, BasicDebugPrintBindings, BasicDequantBindings, BasicDivBindings, \ - BasicDWConv1DBinding, BasicDWConv2DBindings, BasicEluBindings, BasicExpBindings, BasicFloorBindings, \ - BasicGatherBindings, BasicGELUBindings, BasicGEMMBindings, BasicGlobalAveragePoolBindings, \ - BasicGlobalMaxPoolBindings, BasicGroupNormBindings, BasicHardSigmoidBindings, BasicHardSwishBindings, \ - BasicInstanceNormBindings, BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, BasicLayerNormBindings, \ - BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, BasicMaxPool2DBindings, BasicMulBindings, \ - BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, BasicQuantBindings, BasicReduceMeanBindings, \ - BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, \ - BasicRQSGELUBinding, BasicScatterBindings, BasicSeluBindings, BasicSigmoidBindings, BasicSliceBindings, \ - BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, \ - DummyBinding + BasicBatchNormBindings, BasicCeilBindings, BasicClipBindings, BasicCol2ImBindings, BasicConcatBindings, \ + BasicConv1DBindings, BasicConv2DBindings, BasicConvTransposeBindings, BasicDebugPrintBindings, \ + BasicDequantBindings, BasicDivBindings, BasicDWConv1DBinding, BasicDWConv2DBindings, BasicEluBindings, \ + BasicExpBindings, BasicFloorBindings, BasicGatherBindings, BasicGELUBindings, BasicGEMMBindings, \ + BasicGlobalAveragePoolBindings, BasicGlobalMaxPoolBindings, BasicGroupNormBindings, BasicHardSigmoidBindings, \ + BasicHardSwishBindings, BasicInstanceNormBindings, BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, \ + BasicLayerNormBindings, BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, \ + BasicMaxPool2DBindings, BasicMulBindings, BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, \ + BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, \ + BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, BasicScatterBindings, BasicSeluBindings, \ + BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, \ + BasicSwishBindings, BasicTransposeBindings, DummyBinding from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \ - ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, ExpLayer, \ - FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, GroupNormLayer, \ - InstanceNormLayer, ITAMaxLayer, LayerNormLayer, LeakyReluLayer, MatMulLayer, MaxPoolLayer, MulLayer, PadLayer, \ - PowLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, ReshapeLayer, \ - RQIntegerDivLayer, RQSiGELULayer, ScatterLayer, SeluLayer, SigmoidLayer, SliceLayer, SoftmaxLayer, SqrtLayer, \ - SubLayer, SwishLayer, TransposeLayer + Col2ImLayer, ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, \ + ExpLayer, FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, \ + GroupNormLayer, InstanceNormLayer, ITAMaxLayer, LayerNormLayer, LeakyReluLayer, MatMulLayer, MaxPoolLayer, \ + MulLayer, PadLayer, PowLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, \ + ReshapeLayer, RQIntegerDivLayer, RQSiGELULayer, ScatterLayer, SeluLayer, SigmoidLayer, SliceLayer, SoftmaxLayer, \ + SqrtLayer, SubLayer, SwishLayer, TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ - CeilParser, ClipParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, DummyParser, \ - EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ + CeilParser, ClipParser, Col2ImParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, \ + DummyParser, EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ GenericConv2DParser, GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, \ GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, LeakyReluParser, \ @@ -101,6 +101,7 @@ GlobalAveragePoolMapper = NodeMapper(GlobalAveragePoolParser(), BasicGlobalAveragePoolBindings) GlobalMaxPoolMapper = NodeMapper(GlobalMaxPoolParser(), BasicGlobalMaxPoolBindings) ScatterMapper = NodeMapper(ScatterParser(), BasicScatterBindings) +Col2ImMapper = NodeMapper(Col2ImParser(), BasicCol2ImBindings) # Dummy nodes are intended for development purposes only! # They should always generate compiler errors to not accidentally end up in production code @@ -166,6 +167,7 @@ 'GlobalMaxPool': GlobalMaxPoolLayer([GlobalMaxPoolMapper]), 'Scatter': ScatterLayer([ScatterMapper]), 'ScatterElements': ScatterLayer([ScatterMapper]), + 'Col2Im': Col2ImLayer([Col2ImMapper]), # # For example, you can use the DummpyMapper, in case you want to test # # deployment or optimizations with GlobalAveragePool nodes but did not yet # # implement the corresponding kernel diff --git a/Deeploy/Targets/Generic/Templates/Col2ImTemplate.py b/Deeploy/Targets/Generic/Templates/Col2ImTemplate.py new file mode 100644 index 0000000000..11d10988fe --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/Col2ImTemplate.py @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import Dict, List, Tuple + +from Deeploy.AbstractDataTypes import FloatImmediate +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + + +def _typeSuffix(ref_type) -> str: + if issubclass(ref_type, FloatImmediate): + return f'fp{ref_type.typeWidth}' + elif ref_type.signed: + return f's{ref_type.typeWidth}' + else: + return f'u{ref_type.typeWidth}' + + +class _Col2ImTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> Tuple[NetworkContext, Dict, List[str]]: + + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['type_suffix'] = _typeSuffix(data_in._type.referencedType) + + return ctxt, operatorRepresentation, [] + + +referenceTemplate = _Col2ImTemplate(""" +// Col2Im (Name: ${nodeName}, Op: ${nodeOp}) +Col2Im_${type_suffix}( + ${data_in}, ${data_out}, + ${batch_size}, ${channels}, ${spatial_dims}, + (int32_t[]){${', '.join(str(s) for s in image_shape)}}, + (int32_t[]){${', '.join(str(s) for s in block_shape)}}, + (int32_t[]){${', '.join(str(s) for s in dilations)}}, + (int32_t[]){${', '.join(str(s) for s in pads)}}, + (int32_t[]){${', '.join(str(s) for s in strides)}} +); +""") diff --git a/DeeployTest/Tests/Kernels/FP32/Col2Im/inputs.npz b/DeeployTest/Tests/Kernels/FP32/Col2Im/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..9c4058dbcb5ad20bd081677e826ef6a30db7bbe0 GIT binary patch literal 4872 zcmbVQc{JDSwObL~#L*LKd zs#8ZLbsD5jgGQxE(xBmXe&^o1*8TTBd%f?o-e>K9-nI97hsS-YoV>ZrzZfNR*JsX* z@Ba-YnL#ou)&z%#S*!{ERaQn#CR*d)wEy9Z8FQz*&zF^nkl8TCKOodEWXwdpF^Dk(tnCWfO`~RT- zUtRkk~tBXSA=5a?ZG5-YcQphZA94h9_Be@QQW?xU_NyhWh!RUu-n?utD3_e z-x7HDs}qzqa5PWRodSm^t8>o>U;Y#wgqB;>X!1QXq4K>S->@#H5xqNwrP#dt5J6Ftv2cZ!Zwzrky8cu`sy#YGP4#owp zecAi53t9J>19OJxVE5b{YAAduM#)+6et&^gW&7an?_QYVH9@RS)5DOEK=>)RnN+3* z!so#e_-u8pw0T|>`tG{IdNFc1_U0mCk&i%L&w_*%vio^JwlaC_^oPWVRd~F$h@LEz z{o!GlgFegUng?kHd|b{?+tj4`U=~k)j2$530|GLgy&wUhj_;dT(nV_ zb?mAsZul1Acb$V|K0K1w#*Gu+e=y-=$=R@PxF)-QU5`42pD9&$1|2r(gEnuRSt&yu zelwVbPC3#1*PKqldbSa~viJ-!9WMOTWj2Q?l)y)I35f1`p!vHG9LT#xA%%nBTbctL z$$ms?KWAdVfoy2Xlkk15tI*RmoIn2E3z|#p(P*X|Kh)Po`Ju6pwL=a)q}k}NJc885 zzol&+DxAHe4=?d`5$Aa>fWCvRVD)@?EZ0|%G^Q=bCB7!4w)Ykuu{{rFhDyxy#zq7bTD3gP2$ZKwGaHE_1_ zCwyL@g`d{7K+V}Hboy==v`+s(-f2Ur*FBGva%Ks(W+!pCZziwJy$!46W4Kg4n^rnl zU`mRCuqxP$%O5|Y;>cT2P}@Nn@%^E5c85fJTMK>Kb_9#snxL~!KF=?GB_yByi;nmy z;`F?)bmLCCn7aEQ%#G`h2Fkz{gFGp&H=7#Nu278QPSoBV1I?$V@PXSJ*m$-S|FAN| zfMKB=)}2e`Rlnfmuy(3cNP)iXA3$^FAWmGWCGIMgZG8SjpU)JIBI_bI{xf?kW?%K^ zkd|c9oL?fOOtZ&6bHBpl+H;_$@h5n*JvPQs95t@Ko02hTV(h>Hfv^X-c1 zbC`FEI-VXa`%(RWRa&x!+KpITV)lV zvRA@Gbr;23C!M6ZfqJZ&F-dy;)C96koB_TI<@v56@=$Z5R;y?)W; zzETzJyJRGN21W@z-XEyDH%g-5y&qaFmSe+)VQ7J6IAZ5}!t?>S_2_fKbX)~KnJvqG z7WQSKS)`>MrQ9~@5E*rLkx~o^(@!pd-l19Ot*(V#d3B^Ns=v7jcdH%o z-smj)uAIPo_j%#UJ?Y|mWmjw*zJ)Jc6v_O&=cK^IHxeI(G`c(U9MoSi8e z!nJ*cu(|djc)hh|_waBo{86hp<#Rwj_BC0Ki$rI&U4ne2BX+c!3A%R*(RXz+PVpQ` z$)Us1I<6UvdKaLxpEi3Y+y?deCivxeGP`(8;wb$B-n-I_RQEoHq$R<~<&y;EU$$a* zi#NNv#Iw{|MVx#o91aJRNbLfOxZ2QP)TnjmjHx#ue18x``3~XFYYWNmR})?mt|uP6 zs)h4vNARYp2cf#Wlnm>>QqA{xaeILwcbgugL8StxoA1S<Cl=tF-P=%o>5n~ase_5uZV)xI2b)G& zqf$eG_@-Tp6=I`#gIym$#V*>G>c{Z3nQWBJx%A_9{Gd7zbKMF~ZEISET57I>b+Zzi zo(RRQp=FT!-jL1n--)w_DRY$I!;Y4Nu#I-(oU@BL*r*B$ZCc3vK^}Faog%eK{n>fV zM@ii~c`h1i4$6CEM0lx1mw!^{;lI^U@yWNKKlK2VF4y6ezibxr*S&$2JL6dW{!uXK ze~s2<*MOCzo4)v^v4+i7ernYtc|E}hE?27YmiZUq)r(kGF*OnGPa_9qwUA$Vmn8nM z5@(KxAeJ44Uvlf{U9|#UARA6iD23wUW$0FamP=*76P4D%`z~2Zj9(3ru1p(`_z3RK z=fnlA6Vd(JXt=881w9wHQI+BzGF_QS{X;i`a_x4i4YLPZ#bR{!9e~G6-07qhU_$0r zYEmv>yQ!o2YH22a{IGIv8jw5 zVu2U%f=5Z3tg1f%Up?`{tZA`u&L9h0_Iw}}LpQdX zmcpNW)?$9Z75F$+MO3x02E{=(EK_k#qX9d3>F;5v2u&S9th(t(+DeFOhmbK1y?R z9+8Seybw7~pHd2f@s8#-9$9FC#f#lg@m>+XnYEDa)vcy-YhM}96MyFG#*g56^DruGUrRS*9>PG=6gVLtNJg9OaluD3 z4jFb9Za1BRp*pUVf6^WHm``S-R!yug>l7nhN@(THF?hr80qt7g%kiqevS%lQ-`#0! zxilG{?=0rkwL4IIU_W@4xn4ZGxSS$Fwm@nnNrfGjIKnnvc(9ZN`iGlr)7df)OXns7~b*Wt4vwj1qNNy>KgZ*u=BlVFR>u z$cB#F`q0;51U`3+!<=cOQQIYhqw1%z{@Zh;mK7&CP+SDB9iLIX>TBUs-CIhmI88Uz zd^x{XlVx1I_>8^|kIghj-={;_GW#m#-q{P<*`0!?rxGt6b|1i|j?B&W2=UDqv85wi zbZoanJNXAR!r+ATd+AOzIC!4AzNKOR4ec=b>j}EKs0ps+4ac(2f539b4Xj!5jKWsv z((z=87_s*cPz!%3RF%x-eX~PhoZo3s9DM@LcbZ|?Y6Vn$lMWa6TZ zQkTp(SE1l!#FFtMP9rkE?RPg@q~)KtoMaOL3dsWH*crV zinSkw_vgleXjpneABKHaLuR zR{Tvd%Q~bD4Z}g#Uz-};eL+Q-%sDcFm@~se3_dd+b-fmXy-7NF&aQze&Z*FLhdxfkA?J(|SfM~OF8LEb~l9Of{Udy{AP44t(r%wd}zrIC{(S5P|Mhne9mqHgz zviWsG8V&4VE?u>W$Ig8R6xItH4dcP9R+}9rcT2rfHBasHwCC|j!FYO!GM)_@NS8|z zBpx(29ldN>f$gz#Qo%HzEr`EK;B^7U}UxMC0YN*$~7IGdh#_w-5(Yr)TP*b=} zVQcO|@wE*w!a)_?@B);-9D|XrM=0XF2L`|VB6unMxC;~SP~!bWTt54&U>ep!FK^~j zmBMr?tKR_?GfE&PvzD~|f0Z8hJ%WR660u-^61>Q0l-w=I5dvnPfvh5Z&Zsk>+>LQu z8@&a7(Y_4%`*JDz#s>5m>C6|W1+itZDi|h=!J9SP@zTw7I-6yNf0>+>#`fG1YnBeg zU!*P2*M1{w39B%%#T$oNoTo7NP0&4LA2eMvg6~?DwDiRda=5Wo+MDlyC*K`~P^;CH z`nCs*+aL0}OLjQVJc(!b1X8)7HIFfx$?NOwaq!_x8h_dcqz-SS<4ShJ_fluU$898e<}+~Wl^5=>dk!n}XNcZ2rU*v2_w)HZ8dyd?u(^CBx}SK; z&(%xt_p;;S*qK(Cywr$;hn#^YNycoPcaD5U=HO+GZqdeg0DSss06Oj42hIs3*k-JN zZWlt87)?>5N(P#>jnS*!5>Izd!t?$YVZ#GkE-%}PGlJ~+LIr}`qPJ9SWgxCy?g3+u zOb1bJ2Q+KNVEm}xV84?N$)PsZxJ&R#kTc7V6@UDp??ZF2gx=|Rqn!qcj~WgL7V&?J zi91G0)&!2@jmwwuE8Q)au&p1j*{{oMT*}yes%#%~x&Lck{o_sjGnD?ds{S)a%lOED;mqk0zuFaLwn!zf`kIL8OAJQB?%i5vdCUQRA{9H z6o-tDk&lf*U~mTrlrYVRz31`~h!7-YB$b(?PBgSb$|QosrcKkIJ!j5&<~(!$JU?ZO z$jXm-tWC_qhn^oSKam~d$Y`>;%xum-BQwu}S;I7l9^d`r6+3ptDODCs4s%GBoTA;E zB@2_v!qvetxm2dsWN}$Z8E|TL#X^>nR_>8%|<Fe>fp$wG@P8Vr$s(m$PxcUY$dKV-6jDq|Jn+s^cp@gL`BpS z8_>#8OmE~!VfR-FwCe!N7j$}IW4sox9ehrBcx4IZH1WLK>lzS$`V=&5ZUj^4Ix1RL zQMv9kyUMf;(>|ZTIUgrsOIkKYk7kqQ*6;ZaQJ^pu{WMN9?YO$!2+?;sjlX$2pnBH> z;&AIMN=BT?mFG*zj#G)4w=V;{>iX~m696;DV>tgp3{2M!v3IL-jfRXe{uLK1Xo_m% zV_PI3>vE@sSOn!2j;Qb04$@9Ne>*b*^hXbq+NwW6KQjm$d}`74#c|AE_mFV%06w*k z(9rYO$=;$mqNcIX^Yb~ps;&TcNXGYP!)c3Wyub!E!P|EO1s(4xkByfI8|Udixy~gMT@R)%WIMBnFA%lYEGrnuN-gUvW6!0qX0? zWS$EL$*?<}$ghDy?FHh)jq@06jjJYYFv9;7TUeY4m8FGf5;cH_{UspZnD~sX?Wl~g nu=ErCmu@q==C*snyxDXX=J_w{${1@Kb7HYlp({T9H}`)50gSnY literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/Integer/Col2Im/inputs.npz b/DeeployTest/Tests/Kernels/Integer/Col2Im/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..5cccf345b240289282d562dbdc2d06d4a1f5b63b GIT binary patch literal 4872 zcmbW5zpEr!8HUewRag^?#Xq2ouEMO;!lELArxTMwsl>p@8r)eFWVV=D1rgzd9Tu2$ zSYd}1HW+w%q7Dm8ZdlQI>^Q-o^58*H2Z;xNRe)s#gk3Xv)fBxm? zk1y)SFTZ;G_1jl(e)rYeFJ2q}<*Prvdrkb^A71sZ@qh5yi~6&lzNmlv`43;zf3N?a z=Q|dXz;4LD+Vz`7Ac@=x&Pj13kvwHIR6-L{yy zh_7}4&adG_Y{9<3#+^4{ci8s!*ygPru`Ada6WoJoCT#n!_zSGw6}~^2yc#oj+MvF(5*UcNyZ1K&_o(0sj+^f+b#pFS?r6XB z)EJpD5)%`?dMDt!2=DP>wbbvheFrVJb?$9$q1SrzK5APNa(NHEcBBTr>H=J5AX=5T;}ib zvoZV`4&>D9K*U#bA?9w@$t~btM-EHxNFz*m37_KrM*d9~cv-NNRp zXN`~f0sjKKi+bwZ_ff~*B@+gSP<>c_G}k8eB>bDnqD0ngLI3D{$e z^UclY(r>N*|2437(5G&JJ)ie*g)gx72-H=h2kxGMXY%pg$9o(;nS&E-ui7b60~T;2 zcO|}u-N~5R@*dxv?|VdScl9pT`!Elzb9eWvfb*Z=7Iu#I5xWN7gSMLEW*^U`SDgrY zV2!gneQjosXf5~|T!{@}0vXtIf$edJh58Kao$(vEhTX?^`hEU|W~*y#vn{jpHZc?%c!P(Fxmm?&7=(p5PI7 zp8%^}0p}d?YuMR6wz&~Iz!~3}t0s0w%=2B~CwQ%irC#6p&UIege9txGJ2&Mr(>=7E zrN$8qAOqv;-R{ah#%pZPEgJk5*mv9F3-%2BmegK|t2G(Jcb5vg0lq`;N39XN#vZA; zpG%E_xM#FKkaIu#t*ds-#lFJpoLuhN+Grdkphg2P^k^INE}bdVSI5|d@0^t}=goK5 zOpV$;7ySGF)@b{TnK{%{K#SGHcNW1t7s%Nc$y+nxJEsOSwt4jv zxvbBly#UX1G6#Ey-q`_vzp>wj^Nt`A^BH012e^mT^4+L0;d{Oo1mZQZ6R<|jXW||3 z91Za7_S`^=og&)6y!!|247=wH+cRN*`2UBz`>SDJL(aYk&miYg8+gpGdT>jP`}jRM z*PL_AUt#BICotp3j1M|&XJ5%%@4Gs}73>bio!`NWJ@3mL<6_zH4BKx_0zJ0-sBwcU zcmY?C)dAkI^Y+AK>vAl+TYs+|sa?Qi9`qaRAAmb`yRx^wcjY^8fqj*+SoTua+0L4Q z--7z?p0tetYm5)XwCz{Zoy74>7B` z9)G*;U`+1t}Jt1rEgB$Jq_=ezmQg;S5@E!B@{627G7R z@Ba6F?`O8ob2M9>cbSPrdIIQ)TdTd+YV)LAYGm*ENW2E#O@{RsaxEiJUm za0bJUxx}qs_a&z0f}diUlkhXXkMU<}_OQLalOBY;eWTdcE@x)J*0jKLRNxBixq<=P z9o<>I8Tj7ZvB5q9d)=!$mt2SKd7NSG1nnZ-I+S$+5@bH=d;iF6?Tsb zR?qqF;(T=y{n{1rF}5!szVYct|NF-)|28E)X2|_X4_Y7ol-0ic*0&8j@b4$^Ki5BN F{TGw<(S85` literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/Integer/Col2Im/network.onnx b/DeeployTest/Tests/Kernels/Integer/Col2Im/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..9e31c06d27f17a13c35b5cf90e6f4383ee0acc60 GIT binary patch literal 228 zcmd2)?C{j9P=+L3V6@)}cC6x|E3Z8@_LaN-Mf!Z&e&U;o<$h_nhzix;cI+nVPq_U9rLO z%QrvI+my{%bE^|BeO|FKYYaNk~_xC(Kiic@*VU$eMUSDDe`5sfoi_D#Iq0(Yy1Gsp)ZQD=bYIA zaT@kVW_kKkXoz}f9bc@#Nx^I#&k^r{_NRTR#zQr?_OgQijQ+3aI;y%3v16@OROcO` zKcKH145IV;%fz!qvoZMr@i*!{)KRZ-KE4O5Mb^_8bPZqYSQOvs1phPsA>J4Qa8Sk5 zyjtssJdIwZR_8b*RxS;h$Dyj7xcD2KkVnKlbcq_>#VPE<08i`{&0ay7xO*#}uS@(M zuMgRg*(Vs|dE{CvfQWf@o)mo^D9@7T3ubrFBRuU%^NL0C2z=rk{dsf-jSB1+jC!3p zjc=&Fi}w4DF}gEDLp*(Vo$(aa{q;c24deOpBld$=rhg5Np$hvjH;Vs9+GEIg-Tf}H zlhW6NI_R7YbikOiJ7$dbq&j^c(WgHc;=>+siki^*rNo8#w. + * The matching definition lives in Col2Im.c via DEFINE_COL2IM_FN. + * + * Implements ONNX Col2Im semantics: + * input : (N, C * prod(block_shape), L) — column matrix + * output : (N, C, image_shape[0], ..., image_shape[P-1]) + * + * For each kernel position bk and output block ob the contribution is + * accumulated into the corresponding image location (with bounds checking + * to handle padding). The output is zero-initialised before accumulation. + * + * pads layout: [p_0_begin, ..., p_{P-1}_begin, p_0_end, ..., p_{P-1}_end] + */ +#define DECLARE_COL2IM_FN(SUFFIX, DATA_TYPE) \ + void Col2Im_##SUFFIX(const DATA_TYPE *input, DATA_TYPE *output, int32_t N, \ + int32_t C, int32_t spatial_dims, \ + const int32_t *image_shape, const int32_t *block_shape, \ + const int32_t *dilations, const int32_t *pads, \ + const int32_t *strides) + +DECLARE_COL2IM_FN(fp32, float32_t); +DECLARE_COL2IM_FN(s8, int8_t); +DECLARE_COL2IM_FN(u8, uint8_t); + +#endif //__DEEPLOY_BASIC_MATH_COL2IM_KERNEL_HEADER_ diff --git a/TargetLibraries/Generic/src/Col2Im.c b/TargetLibraries/Generic/src/Col2Im.c new file mode 100644 index 0000000000..94e715e620 --- /dev/null +++ b/TargetLibraries/Generic/src/Col2Im.c @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +// +// SPDX-License-Identifier: Apache-2.0 + +#include "DeeployBasicMath.h" + +// clang-format off +#define DEFINE_COL2IM_FN(SUFFIX, DATA_TYPE) \ + DECLARE_COL2IM_FN(SUFFIX, DATA_TYPE) { \ + if (spatial_dims < 1 || spatial_dims > COL2IM_MAX_SPATIAL_DIMS || \ + N <= 0 || C <= 0) \ + return; \ + for (int32_t p = 0; p < spatial_dims; p++) { \ + if (image_shape[p] <= 0 || block_shape[p] <= 0 || strides[p] <= 0 || \ + dilations[p] <= 0 || pads[p] < 0 || pads[p + spatial_dims] < 0) \ + return; \ + } \ + /* Compute per-dim sliding-window sizes, L, block_volume, image_volume. */ \ + int32_t col_dims[COL2IM_MAX_SPATIAL_DIMS]; \ + int32_t col_strides[COL2IM_MAX_SPATIAL_DIMS]; \ + int32_t blk_strides[COL2IM_MAX_SPATIAL_DIMS]; \ + int32_t img_strides[COL2IM_MAX_SPATIAL_DIMS]; \ + int32_t L = 1, block_volume = 1, image_volume = 1; \ + for (int32_t p = 0; p < spatial_dims; p++) { \ + col_dims[p] = (image_shape[p] + pads[p] + pads[p + spatial_dims] \ + - dilations[p] * (block_shape[p] - 1) - 1) \ + / strides[p] + 1; \ + if (col_dims[p] <= 0) return; \ + L *= col_dims[p]; \ + block_volume *= block_shape[p]; \ + image_volume *= image_shape[p]; \ + } \ + /* Row-major strides for flat-index decomposition. */ \ + col_strides[spatial_dims - 1] = 1; \ + blk_strides[spatial_dims - 1] = 1; \ + img_strides[spatial_dims - 1] = 1; \ + for (int32_t p = spatial_dims - 2; p >= 0; p--) { \ + col_strides[p] = col_strides[p + 1] * col_dims[p + 1]; \ + blk_strides[p] = blk_strides[p + 1] * block_shape[p + 1]; \ + img_strides[p] = img_strides[p + 1] * image_shape[p + 1]; \ + } \ + /* Zero-initialise output. */ \ + memset(output, 0, (size_t)(N * C * image_volume) * sizeof(DATA_TYPE)); \ + /* Accumulate each column entry into its image position. */ \ + for (int32_t n = 0; n < N; n++) { \ + for (int32_t c = 0; c < C; c++) { \ + for (int32_t bk = 0; bk < block_volume; bk++) { \ + /* Decompose kernel flat index once per bk. */ \ + int32_t k_coords[COL2IM_MAX_SPATIAL_DIMS]; \ + int32_t bk_rem = bk; \ + for (int32_t p = 0; p < spatial_dims; p++) { \ + k_coords[p] = bk_rem / blk_strides[p]; \ + bk_rem -= k_coords[p] * blk_strides[p]; \ + } \ + for (int32_t ob = 0; ob < L; ob++) { \ + /* Decompose output-block flat index and map to image coords. */ \ + int32_t ob_rem = ob, img_flat = 0, in_bounds = 1; \ + for (int32_t p = 0; p < spatial_dims; p++) { \ + int32_t o_coord = ob_rem / col_strides[p]; \ + ob_rem -= o_coord * col_strides[p]; \ + int32_t h = o_coord * strides[p] - pads[p] \ + + k_coords[p] * dilations[p]; \ + if (h < 0 || h >= image_shape[p]) { in_bounds = 0; break; } \ + img_flat += h * img_strides[p]; \ + } \ + if (in_bounds) { \ + int32_t in_flat = (n * C * block_volume \ + + c * block_volume + bk) * L + ob; \ + output[(n * C + c) * image_volume + img_flat] += input[in_flat]; \ + } \ + } \ + } \ + } \ + } \ + } +// clang-format on + +DEFINE_COL2IM_FN(fp32, float32_t) +DEFINE_COL2IM_FN(s8, int8_t) +DEFINE_COL2IM_FN(u8, uint8_t) From 0cda8b4ae2664d614d1fd765298669ac8512bf64 Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Fri, 19 Jun 2026 18:34:06 +0000 Subject: [PATCH 06/12] add support for Resize operator for Generic target --- .../TypeCheckers/SignPropTypeChecker.py | 4 +- Deeploy/DeeployTypes.py | 22 +- Deeploy/Targets/Generic/Bindings.py | 9 +- Deeploy/Targets/Generic/Layers.py | 14 + Deeploy/Targets/Generic/Parsers.py | 109 +++++++- Deeploy/Targets/Generic/Platform.py | 18 +- .../Generic/Templates/ResizeTemplate.py | 98 +++++++ .../Tests/Kernels/FP32/Resize/inputs.npz | Bin 0 -> 776 bytes .../Tests/Kernels/FP32/Resize/network.onnx | Bin 0 -> 298 bytes .../Tests/Kernels/FP32/Resize/outputs.npz | Bin 0 -> 394 bytes .../Tests/Kernels/Integer/Resize/inputs.npz | Bin 0 -> 776 bytes .../Tests/Kernels/Integer/Resize/network.onnx | Bin 0 -> 298 bytes .../Tests/Kernels/Integer/Resize/outputs.npz | Bin 0 -> 394 bytes .../Generic/inc/DeeployBasicMath.h | 1 + TargetLibraries/Generic/inc/kernel/Resize.h | 67 +++++ TargetLibraries/Generic/src/Resize.c | 257 ++++++++++++++++++ 16 files changed, 581 insertions(+), 18 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/ResizeTemplate.py create mode 100644 DeeployTest/Tests/Kernels/FP32/Resize/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/Resize/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/Resize/outputs.npz create mode 100644 DeeployTest/Tests/Kernels/Integer/Resize/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/Integer/Resize/network.onnx create mode 100644 DeeployTest/Tests/Kernels/Integer/Resize/outputs.npz create mode 100644 TargetLibraries/Generic/inc/kernel/Resize.h create mode 100644 TargetLibraries/Generic/src/Resize.c diff --git a/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py b/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py index c70628729b..46a4896c12 100644 --- a/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py +++ b/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py @@ -39,8 +39,8 @@ def typeInferOutput(self, ctxt: NetworkContext, node: gs.Node, operatorRepresentation: OperatorRepresentation) -> NetworkContext: ctxt = super().typeInferOutput(ctxt, node, operatorRepresentation) - inputs = [ctxt.lookup(inputNode.name) for inputNode in node.inputs] - outputs = [ctxt.lookup(outputNode.name) for outputNode in node.outputs] + inputs = [ctxt.lookup(inputNode.name) for inputNode in node.inputs if inputNode.name] + outputs = [ctxt.lookup(outputNode.name) for outputNode in node.outputs if outputNode.name] signProp = all([hasattr(_input, "_signed") and hasattr(_input, "nLevels") for _input in inputs]) diff --git a/Deeploy/DeeployTypes.py b/Deeploy/DeeployTypes.py index 44abe85112..ea9aaff67f 100644 --- a/Deeploy/DeeployTypes.py +++ b/Deeploy/DeeployTypes.py @@ -1110,6 +1110,10 @@ def parseInputs(cls, ctxt: NetworkContext, node: gs.Node) -> NetworkContext: for inputNode in node.inputs: data_in = inputNode.name + # Skip absent optional inputs (ONNX represents them as empty-name Variables) + if not data_in: + continue + # Hoist constant inputs if type(inputNode) == gs.ir.tensor.Constant and not ctxt.is_global(data_in): ctxt.hoistConstant(inputNode) @@ -1277,7 +1281,7 @@ def typeInferOutput(self, ctxt: NetworkContext, node: gs.Node, """ newCtxt = ctxt.copy() - inputs = [ctxt.lookup(inputNode.name) for inputNode in node.inputs] + inputs = [ctxt.lookup(inputNode.name) for inputNode in node.inputs if inputNode.name] outputNames = [node.name for node in node.outputs] outputTypes = self.output_types @@ -1348,7 +1352,7 @@ def annotateDict(self, ctxt: NetworkContext, node: gs.Node, operatorRepresentati The NodeParser's operatorRepresentation """ - env = [node.name for node in node.inputs + node.outputs] + env = [node.name for node in node.inputs + node.outputs if node.name] for key, value in operatorRepresentation.items(): # check if the referenced buffer is in the environment if isinstance(value, str) and value in env: @@ -1903,7 +1907,9 @@ def broadcast(self, ctxt: NetworkContext, default_channels_first: bool = True) - broadcast to the target shape """ - inputShapes = [ctxt.lookup(node.name).shape for node in self.node.inputs] + # Absent optional inputs are represented in ONNX as empty-name Variables; skip them. + validInputNodes = [node for node in self.node.inputs if node.name] + inputShapes = [ctxt.lookup(node.name).shape for node in validInputNodes] outputShapes = [ctxt.lookup(node.name).shape for node in self.node.outputs] if not "channels_first" in self.mapper.parser.operatorRepresentation: @@ -1914,7 +1920,7 @@ def broadcast(self, ctxt: NetworkContext, default_channels_first: bool = True) - newInputShapes, newOutputShapes = self.computeShapes(inputShapes, outputShapes, self.mapper.parser.operatorRepresentation, channels_first) - for node, newShape in zip(self.node.inputs + self.node.outputs, newInputShapes + newOutputShapes): + for node, newShape in zip(validInputNodes + self.node.outputs, newInputShapes + newOutputShapes): if ctxt.is_local(node.name): ctxt.localObjects[node.name].shape = newShape # Update shape of tensors in onnx graph @@ -2103,7 +2109,7 @@ def bind(self, ctxt: NetworkContext) -> Tuple[NetworkContext, bool]: npType = self._broadcastToNpType(ctxt.localObjects[node.name]._type) if npType is not None: node.dtype = npType - elif ctxt.is_global(node.name): + elif ctxt.is_global(node.name) and hasattr(ctxt.globalObjects[node.name], '_type'): npType = self._broadcastToNpType(ctxt.globalObjects[node.name]._type) if isinstance(ctxt.globalObjects[node.name], ConstantBuffer): if isinstance(node, gs.Constant): @@ -2954,6 +2960,8 @@ def generateBufferInitializationCode(self) -> str: callStack = '' for node in ctxt.globalObjects.values(): if isinstance(node, VariableBuffer) and not isinstance(node, StructBuffer): + if not hasattr(node, '_type'): + continue assert issubclass(node._type, Pointer), f"Global VariableBuffer {node.name} is not a Pointer!" if node._deploy: name = node.name @@ -2999,6 +3007,8 @@ def generateBufferAllocationCode(self) -> str: for node in ctxt.globalObjects.values(): if isinstance(node, VariableBuffer) and not isinstance(node, StructBuffer): + if not hasattr(node, '_type'): + continue assert issubclass(node._type, Pointer), f"Global VariableBuffer {node.name} is not a Pointer!" if node._deploy: name = node.name @@ -3535,6 +3545,8 @@ def _printMemorySummary(self): # We do not count structs for now, since they are not properly modeled if isinstance(_buffer, ConstantBuffer) or (isinstance(_buffer, VariableBuffer) and _buffer._deploy): # SCHEREMO: We only + if not hasattr(_buffer, '_type'): + continue if (hasattr(_buffer, "_memoryLevel") and _buffer._memoryLevel == level) or level == "None": staticSize += int((np.prod(_buffer.shape) * _buffer._type.referencedType.typeWidth // 8)) else: diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index d5d49b253a..4e68aee904 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -22,8 +22,8 @@ FloatSoftmaxTemplate, FloatSqrtTemplate, FloatSubTemplate, FloatSwishTemplate, GatherTemplate, GemmTemplate, \ IntegerDivTemplate, ITAMaxTemplate, ITAPartialMaxTemplate, MatMulTemplate, MaxPoolTemplate, MulTemplate, \ PadTemplate, QuantTemplate, ReduceMeanTemplate, ReduceSumTemplate, RequantShiftTemplate, ReshapeTemplate, \ - RQIntegerDivTemplate, RQSiGELUTemplate, ScatterTemplate, SliceTemplate, SubTemplate, TransposeTemplate, \ - iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate + ResizeTemplate, RQIntegerDivTemplate, RQSiGELUTemplate, ScatterTemplate, SliceTemplate, SubTemplate, \ + TransposeTemplate, iGELUTemplate, iLayernormTemplate, iRMSNormTemplate, iSoftmaxTemplate from Deeploy.Targets.Generic.TypeCheckers import AddChecker, BatchNormChecker, ConcatChecker, ConvChecker, \ DebugPrintChecker, DequantChecker, DivChecker, DummyChecker, GatherChecker, GELUChecker, GEMMChecker, \ LayerNormChecker, MatMulChecker, MaxPoolChecker, MulChecker, PadChecker, PassThroughTypeChecker, QuantChecker, \ @@ -450,3 +450,8 @@ [PointerClass(type), PointerClass(int32_t), PointerClass(type)], [PointerClass(type)]), ScatterTemplate.referenceTemplate, BasicTransformer) for type in (int8_t, uint8_t, float32_t) ] + +BasicResizeBindings = [ + NodeBinding(PassThroughTypeChecker([PointerClass(type)], [PointerClass(type)]), ResizeTemplate.referenceTemplate, + BasicTransformer) for type in (int8_t, uint8_t, float32_t) +] diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index 12a8d3f33e..b737e92f3f 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -839,3 +839,17 @@ def computeOps(self): else: # 1 op per index element return int(np.prod(opRep['indices_shape'])) + + +class ResizeLayer(ONNXLayer): + + def computeOps(self): + rep = self.mapper.parser.operatorRepresentation + size = rep['batch_size'] * rep['channels'] * int(np.prod(rep['output_shape'])) + spatial_dims: int = rep['spatial_dims'] + ops = 0 # default: Nearest-neighbour is a pure copy — no arithmetic operations. + if rep['mode'] == 'linear': # 2^spatial_dims multiply-accumulates per output element. + ops = size * (1 << spatial_dims) + elif rep['mode'] == 'cubic': # 4^spatial_dims multiply-accumulates per output element. + ops = size * (4**spatial_dims) + return ops diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index 00f38b9302..976e9a18ce 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -3277,4 +3277,111 @@ def parseNodeCtxt(self, self.operatorRepresentation['batch_size'] = N self.operatorRepresentation['channels'] = C - return ctxt, True \ No newline at end of file + return ctxt, True + + +class ResizeParser(NodeParser): + + @staticmethod + def _is_empty(input: gs.Variable | gs.Constant | None) -> bool: + if input is None: + return True + if isinstance(input, gs.Constant): + return input.values.size <= 0 + if isinstance(input, gs.Variable): + return input.shape is None + return True + + def parseNode(self, node: gs.Node) -> bool: + + if not all([node.op == 'Resize', len(node.inputs) >= 1, len(node.outputs) == 1]): + return False + + antialias = node.attrs.get('antialias', 0) + axes = node.attrs.get('axes', None) # None -> all axes + coord_mode = node.attrs.get('coordinate_transformation_mode', 'half_pixel') + cubic_coeff_a = node.attrs.get('cubic_coeff_a', -0.75) + exclude_outside = node.attrs.get('exclude_outside', 0) + extrapolation_value = node.attrs.get('extrapolation_value', 0.0) + keep_aspect_ratio_policy = node.attrs.get('keep_aspect_ratio_policy', 'stretch') + mode = node.attrs.get('mode', 'nearest') + nearest_mode = node.attrs.get('nearest_mode', 'round_prefer_floor') + + if not all([ + coord_mode in ('half_pixel', 'half_pixel_symmetric', 'pytorch_half_pixel', 'align_corners', + 'asymmetric', 'tf_crop_and_resize'), + keep_aspect_ratio_policy in ('stretch', 'not_larger', 'not_smaller'), + mode in ('nearest', 'linear', 'cubic'), + nearest_mode in ('floor', 'ceil', 'round_prefer_floor', 'round_prefer_ceil'), + ]): + return False + + self.operatorRepresentation['antialias'] = antialias + self.operatorRepresentation['axes'] = axes + self.operatorRepresentation['coord_mode'] = coord_mode + self.operatorRepresentation['cubic_coeff_a'] = cubic_coeff_a + self.operatorRepresentation['exclude_outside'] = exclude_outside + self.operatorRepresentation['extrapolation_value'] = extrapolation_value + self.operatorRepresentation['keep_aspect_ratio_policy'] = keep_aspect_ratio_policy + self.operatorRepresentation['mode'] = mode + self.operatorRepresentation['nearest_mode'] = nearest_mode + + return True + + def parseNodeCtxt(self, + ctxt: NetworkContext, + node: gs.Node, + channels_first: bool = True) -> Tuple[NetworkContext, bool]: + + data_in: VariableBuffer = ctxt.lookup(node.inputs[0].name) + data_out: VariableBuffer = ctxt.lookup(node.outputs[0].name) + + if not all([ + len(data_in.shape) == len(data_out.shape), # same ndims + all(s > 0 for s in data_in.shape), # no empty dim + all(s > 0 for s in data_out.shape), # no empty dim + len(data_in.shape) > 2, # at least batch, channels, one spatial dim + data_in.shape[:2] == data_out.shape[:2], # batch_size and channels are unchanged + ]): + return ctxt, False + + roi: gs.Variable | gs.Constant | None = node.inputs[1] if len(node.inputs) > 1 else None + scales: gs.Constant | None = node.inputs[2] if len(node.inputs) > 2 else None + sizes: gs.Constant | None = node.inputs[3] if len(node.inputs) > 3 else None + + has_scales = not self._is_empty(scales) + has_sizes = not self._is_empty(sizes) + + if any([ + # ONNX requires exactly one of scales / sizes to be non-empty + has_scales and has_sizes, + (not has_scales) and (not has_sizes), + # scales and sizes assumed constants otherwise output shape + # cannot be inferred at parsing time + has_scales and not isinstance(scales, gs.Constant), + has_sizes and not isinstance(sizes, gs.Constant), + ]): + return ctxt, False + + if not self._is_empty(roi): + if isinstance(roi, gs.Constant): + _roi = roi.values.tolist() + elif isinstance(roi, gs.Variable): + _roi = ctxt.lookup(roi.name).name + else: + return ctxt, False + else: + _roi = None + + self.operatorRepresentation['data_in'] = data_in.name + self.operatorRepresentation['data_out'] = data_out.name + self.operatorRepresentation['batch_size'] = data_in.shape[0] + self.operatorRepresentation['channels'] = data_in.shape[1] + self.operatorRepresentation['spatial_dims'] = len(data_in.shape[2:]) + self.operatorRepresentation['input_shape'] = list(data_in.shape[2:]) + self.operatorRepresentation['output_shape'] = list(data_out.shape[2:]) + self.operatorRepresentation['roi'] = _roi + self.operatorRepresentation['scales'] = scales.values.tolist() if has_scales else None + self.operatorRepresentation['sizes'] = sizes.values.tolist() if has_sizes else None + + return ctxt, True diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index faf3a5a935..d8fe1e4d4d 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -16,16 +16,16 @@ BasicLayerNormBindings, BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, \ BasicMaxPool2DBindings, BasicMulBindings, BasicPad1DBindings, BasicPad2DBindings, BasicPowBindings, \ BasicQuantBindings, BasicReduceMeanBindings, BasicReduceSumBindings, BasicReluBinding, BasicReshapeBindings, \ - BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, BasicScatterBindings, BasicSeluBindings, \ - BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, BasicSubBindings, \ - BasicSwishBindings, BasicTransposeBindings, DummyBinding + BasicResizeBindings, BasicRQIntegerDivBinding, BasicRQSBindings, BasicRQSGELUBinding, BasicScatterBindings, \ + BasicSeluBindings, BasicSigmoidBindings, BasicSliceBindings, BasicSoftmaxBindings, BasicSqrtBindings, \ + BasicSubBindings, BasicSwishBindings, BasicTransposeBindings, DummyBinding from Deeploy.Targets.Generic.Layers import AddLayer, AveragePoolLayer, BatchNormalizationLayer, CeilLayer, ClipLayer, \ Col2ImLayer, ConcatLayer, ConvLayer, ConvTransposeLayer, DebugPrintLayer, DequantLayer, DivLayer, EluLayer, \ ExpLayer, FloorLayer, GatherLayer, GELULayer, GEMMLayer, GlobalAveragePoolLayer, GlobalMaxPoolLayer, \ GroupNormLayer, InstanceNormLayer, ITAMaxLayer, LayerNormLayer, LeakyReluLayer, MatMulLayer, MaxPoolLayer, \ MulLayer, PadLayer, PowLayer, QuantLayer, ReduceMeanLayer, ReduceSumLayer, ReluLayer, RequantShiftLayer, \ - ReshapeLayer, RQIntegerDivLayer, RQSiGELULayer, ScatterLayer, SeluLayer, SigmoidLayer, SliceLayer, SoftmaxLayer, \ - SqrtLayer, SubLayer, SwishLayer, TransposeLayer + ReshapeLayer, ResizeLayer, RQIntegerDivLayer, RQSiGELULayer, ScatterLayer, SeluLayer, SigmoidLayer, SliceLayer, \ + SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ CeilParser, ClipParser, Col2ImParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, \ DummyParser, EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ @@ -33,9 +33,9 @@ GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, LeakyReluParser, \ MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, \ - ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, RQIntegerDivParser, RQSiGELUParser, ScatterParser, \ - SeluParser, SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, TransposeParser, \ - UnsqueezeParser, iLayerNormParser, iSoftmaxParser + ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, ResizeParser, RQIntegerDivParser, RQSiGELUParser, \ + ScatterParser, SeluParser, SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, \ + TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ @@ -102,6 +102,7 @@ GlobalMaxPoolMapper = NodeMapper(GlobalMaxPoolParser(), BasicGlobalMaxPoolBindings) ScatterMapper = NodeMapper(ScatterParser(), BasicScatterBindings) Col2ImMapper = NodeMapper(Col2ImParser(), BasicCol2ImBindings) +ResizeMapper = NodeMapper(ResizeParser(), BasicResizeBindings) # Dummy nodes are intended for development purposes only! # They should always generate compiler errors to not accidentally end up in production code @@ -165,6 +166,7 @@ 'AveragePool': AveragePoolLayer([AveragePool1DMapper, AveragePool2DMapper]), 'GlobalAveragePool': GlobalAveragePoolLayer([GlobalAveragePoolMapper]), 'GlobalMaxPool': GlobalMaxPoolLayer([GlobalMaxPoolMapper]), + 'Resize': ResizeLayer([ResizeMapper]), 'Scatter': ScatterLayer([ScatterMapper]), 'ScatterElements': ScatterLayer([ScatterMapper]), 'Col2Im': Col2ImLayer([Col2ImMapper]), diff --git a/Deeploy/Targets/Generic/Templates/ResizeTemplate.py b/Deeploy/Targets/Generic/Templates/ResizeTemplate.py new file mode 100644 index 0000000000..3a1ce42acd --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/ResizeTemplate.py @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +from typing import Dict, List, Tuple + +from Deeploy.AbstractDataTypes import FloatImmediate +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + +_TYPE_TAG_MAP = { + 'fp32': 'RESIZE_TYPE_FLOAT32', + 's8': 'RESIZE_TYPE_INT8', + 'u8': 'RESIZE_TYPE_UINT8', + 's16': 'RESIZE_TYPE_INT16', + 'u16': 'RESIZE_TYPE_UINT16', + 's32': 'RESIZE_TYPE_INT32', + 'u32': 'RESIZE_TYPE_UINT32', +} + +_MODE_MAP = { + "nearest": "RESIZE_MODE_NEAREST", + "linear": "RESIZE_MODE_LINEAR", + "cubic": "RESIZE_MODE_CUBIC", +} + +_COORD_MAP = { + "asymmetric": "RESIZE_COORD_ASYMMETRIC", + "half_pixel": "RESIZE_COORD_HALF_PIXEL", + "half_pixel_symmetric": "RESIZE_COORD_HALF_PIXEL_SYMMETRIC", + "align_corners": "RESIZE_COORD_ALIGN_CORNERS", + "pytorch_half_pixel": "RESIZE_COORD_PYTORCH_HALF_PIXEL", + "tf_crop_and_resize": "RESIZE_COORD_TF_CROP_AND_RESIZE", +} + +_NEAREST_MAP = { + "floor": "RESIZE_NEAREST_FLOOR", + "ceil": "RESIZE_NEAREST_CEIL", + "round_prefer_floor": "RESIZE_NEAREST_ROUND_PREFER_FLOOR", + "round_prefer_ceil": "RESIZE_NEAREST_ROUND_PREFER_CEIL", +} + + +def _typeSuffix(ref_type) -> str: + if issubclass(ref_type, FloatImmediate): + return f'fp{ref_type.typeWidth}' + elif ref_type.signed: + return f's{ref_type.typeWidth}' + else: + return f'u{ref_type.typeWidth}' + + +class _ResizeTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> Tuple[NetworkContext, Dict, List[str]]: + + rep = operatorRepresentation + + if rep.get('roi', None) is not None: + raise ValueError("Resize: 'roi' input is not supported.") + if rep.get('scales', None) is not None: + raise ValueError("Resize: 'scales' input is not supported; use 'sizes' instead. ") + if rep.get('antialias', 0) != 0: + raise ValueError(f"Resize: antialias={rep['antialias']} is not supported by this kernel.") + if rep.get('exclude_outside', 0) != 0: + raise ValueError(f"Resize: exclude_outside={rep['exclude_outside']} is not supported by this kernel.") + if rep.get('axes', None) is not None: + raise ValueError(f"Resize: axes={rep['axes']} is not supported; all axes must be resized.") + if rep.get('keep_aspect_ratio_policy', 'stretch') != 'stretch': + raise ValueError( + f"Resize: keep_aspect_ratio_policy='{rep['keep_aspect_ratio_policy']}' is not supported by this kernel." + ) + if rep.get('coord_mode', 'half_pixel') == 'tf_crop_and_resize': + raise ValueError( + "Resize: coordinate_transformation_mode='tf_crop_and_resize' is not supported by this kernel.") + if rep.get('mode', 'nearest') == 'cubic': + raise ValueError("Resize: mode='cubic' is not supported by this kernel.") + + data_in = ctxt.lookup(rep['data_in']) + type_suffix = _typeSuffix(data_in._type.referencedType) + rep['type_tag'] = _TYPE_TAG_MAP[type_suffix] + rep['mode'] = _MODE_MAP[rep['mode']] + rep['coord_mode'] = _COORD_MAP[rep['coord_mode']] + rep['nearest_mode'] = _NEAREST_MAP[rep['nearest_mode']] + + return ctxt, rep, [] + + +referenceTemplate = _ResizeTemplate(""" +// Resize (Name: ${nodeName}, Op: ${nodeOp}) +Resize( + ${data_in}, ${data_out}, ${type_tag}, + ${batch_size}, ${channels}, ${spatial_dims}, + (int32_t[]){${', '.join(str(s) for s in input_shape)}}, + (int32_t[]){${', '.join(str(s) for s in output_shape)}}, + ${mode}, ${coord_mode}, ${nearest_mode} +); +""") diff --git a/DeeployTest/Tests/Kernels/FP32/Resize/inputs.npz b/DeeployTest/Tests/Kernels/FP32/Resize/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..12d2c04d5d91939b28438feb2fe335e7935094e9 GIT binary patch literal 776 zcmbV~TS${(7{|YFOG_;el|sX0nlQB~o>KAjy|0-m!#v<=EtktpQ~Cl$P`R?T5A{)0Bp)5XcqkTviay7ewX-3xs?@ zUNKw3%8HH_vvP$ckCQ3Y3UjVLE>kJYw^x9WA1LI9ncG{)Kh6IS*}neLj@Z?H!=0EH zD6vnC`=_0OnsSEv-b##g%P2FV8EV?^AzzUWnzl5-u4x`JIkb}0AD2kW=Rs)L=0OtN z642dvC$jQQM!stcvHw{|h&KwE{^eXU5p*ARo9dVWuP`EIRVXzWfY%oe<5vK>{dCwT z#8y8M;Z07qaq(FCeiG7ah3uZ)Alh?j$Tp(=O^(bSfFMp9N^^0=OX;mJ z+V#m$&J7?Pa)vn29Ne;=h3m7z48sk4%(D4tpK2WJMfqseS%c?KJk*b8mSdUjCE-_b z@!oe*Mw-n))_fsTKz9(k>sukUPL3{Xlz3qBHq1$AvUs%RAW0OAY@9L%N&j~zEm ziK>vxY%}gDH8S>ZJ<*IV+Mv?5l31wKm-+LUK!>MzGHk_Ay@pWXqX&BXwQyqnJ_JkJ z7g2`$8al6aBBg8&ekIBw>CZ>d#mGJ^t#l)J|3TrGV7 literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/Resize/network.onnx b/DeeployTest/Tests/Kernels/FP32/Resize/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..569b3cf20a43b85b595e47192d32173e2e784a2e GIT binary patch literal 298 zcmZusO=|)%5KXpm4THiq6bn752woP7_Z~g==+R3^yR&OxO_n4J;(zEb@ZwK3`*AAH z@Obm_W|-k~K|aXWErCA@6K$F{0Es^F`ifqpwhghgNe|c+@>gJITi_WgXM1w3vEu|3d*6g9t-@X-Pq8iC$hoB_o3X0|O&eA(Wo%7wQ`j$;eQ~ zP_3SlTAW;@Zl$1ZlV+l>qoAIaUsO_*m=~X4l#&V(cT3DEP6dh=XCxM+0{I$-ItoTQ z3MN3LsiRPzcQpp-{xe>cT8L3HC(} z4Mw{U%vpVSzsm3b_LmwaIV`kpIq<;Z`+-v#2OK=U_t-tWTos?$v68`{G~VYY_1AOxz-@l-!sR{L(y zLa*jI+yb{?0aQ}}?dD@@&}+2j7Xn@iZ-C~i_X3{5+DkblGjp`|Pk?sNJmspF=9NJI zn|zC_9-cTxt5*qYhI(k1@OusaCf)2W Dweh=+ literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/Integer/Resize/network.onnx b/DeeployTest/Tests/Kernels/Integer/Resize/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..fab06b2c6460f58562f6d166b199c436f19910c1 GIT binary patch literal 298 zcmZusJ!``-5S1Op6-NrLC@FMEOdyLPbnlq4W5zB*sn52+mW(7r=zqvB=#rlr`C}@% z2)DGizQl7HpYyy57_*Kuuxih6y8ehN@H8;mC@56)Uqi>r!Lrpz5LB+ zeG!$FUF~{XVUUeUeqc0xI!*_EWf7Syo|*pR@9q2+U&AX~@DySe1;hbtz&V5Y%LfV? K6!b`er2GzyRYlDJ literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/Integer/Resize/outputs.npz b/DeeployTest/Tests/Kernels/Integer/Resize/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..9ee4676afce37877d7f2f68b3e3ae411c0dbefd4 GIT binary patch literal 394 zcmWIWW@gc4fB;2?Ap3g_|Dk}3L4+Z{w4|W4L@%$Pl954xfq@aK5K2$>3-t|%WMn8~ zs8&x&Elw^{w^C5INi$K`QBY6IFDfZY%!|)2N=XHYyCvonrvk-`GZG6@fqV@^9R(vD z1rs3B)KREaAOmnQFeErKFgP4!U?>3M2M!Dj4Im6;cL2o$K;l3eq~-un{J;UA8Xy~_ zW&uc@Jp+RRPz^{w15j=SNDe5r0LTWZ0m*^of#M)}5CEwK$u$7Q790rhW@OT3Mh!i1 g7%?(1038Ph4M08vQdkCfv$BCi7=h3NNZWwr0T~HULI3~& literal 0 HcmV?d00001 diff --git a/TargetLibraries/Generic/inc/DeeployBasicMath.h b/TargetLibraries/Generic/inc/DeeployBasicMath.h index d9b5cfe372..4b5a7a7b6c 100644 --- a/TargetLibraries/Generic/inc/DeeployBasicMath.h +++ b/TargetLibraries/Generic/inc/DeeployBasicMath.h @@ -63,6 +63,7 @@ #include "kernel/RQHardswish.h" #include "kernel/Relu.h" #include "kernel/RequantShift.h" +#include "kernel/Resize.h" #include "kernel/Scatter.h" #include "kernel/Selu.h" #include "kernel/Sigmoid.h" diff --git a/TargetLibraries/Generic/inc/kernel/Resize.h b/TargetLibraries/Generic/inc/kernel/Resize.h new file mode 100644 index 0000000000..153831f6bc --- /dev/null +++ b/TargetLibraries/Generic/inc/kernel/Resize.h @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __DEEPLOY_BASIC_MATH_RESIZE_KERNEL_HEADER_ +#define __DEEPLOY_BASIC_MATH_RESIZE_KERNEL_HEADER_ + +#include "DeeployBasicMath.h" + +/* Maximum number of spatial dimensions (excludes batch N and channels C). */ +#define RESIZE_MAX_SPATIAL_DIMS 4 + +/* Element type — passed as a compile-time constant from generated code. */ +typedef enum { + RESIZE_TYPE_FLOAT32 = 0, + RESIZE_TYPE_INT8, + RESIZE_TYPE_UINT8, + RESIZE_TYPE_INT16, + RESIZE_TYPE_UINT16, + RESIZE_TYPE_INT32, + RESIZE_TYPE_UINT32, +} resize_type_t; + +/* Interpolation mode (mirrors ONNX Resize `mode` attribute). */ +typedef enum { + RESIZE_MODE_NEAREST = 0, + RESIZE_MODE_LINEAR, + RESIZE_MODE_CUBIC, +} resize_mode_t; + +/* Coordinate transformation mode. */ +typedef enum { + RESIZE_COORD_ASYMMETRIC = 0, + RESIZE_COORD_HALF_PIXEL, + RESIZE_COORD_HALF_PIXEL_SYMMETRIC, + RESIZE_COORD_PYTORCH_HALF_PIXEL, + RESIZE_COORD_ALIGN_CORNERS, + RESIZE_COORD_TF_CROP_AND_RESIZE, +} resize_coord_mode_t; + +/* Nearest-neighbour rounding mode. */ +typedef enum { + RESIZE_NEAREST_FLOOR = 0, + RESIZE_NEAREST_CEIL, + RESIZE_NEAREST_ROUND_PREFER_FLOOR, + RESIZE_NEAREST_ROUND_PREFER_CEIL, +} resize_nearest_mode_t; + +/* + * Resize — single function for all element types. + * + * input / output – NCHW tensors (void* to stay type-agnostic) + * type_tag – element type; drives element size and float conversion + * N, C – batch size and number of channels + * spatial_dims – number of spatial dimensions (1..RESIZE_MAX_SPATIAL_DIMS) + * input_shape – spatial sizes of the input [d0, d1, …] + * output_shape – spatial sizes of the output [d0, d1, …] + * mode – interpolation mode + * coord_mode – coordinate transformation mode + * nearest_mode – rounding mode (only used when mode == RESIZE_MODE_NEAREST) + */ +void Resize(const void *input, void *output, resize_type_t type_tag, int32_t N, + int32_t C, int32_t spatial_dims, const int32_t *input_shape, + const int32_t *output_shape, resize_mode_t mode, + resize_coord_mode_t coord_mode, resize_nearest_mode_t nearest_mode); + +#endif // __DEEPLOY_BASIC_MATH_RESIZE_KERNEL_HEADER_ diff --git a/TargetLibraries/Generic/src/Resize.c b/TargetLibraries/Generic/src/Resize.c new file mode 100644 index 0000000000..e0b19791f3 --- /dev/null +++ b/TargetLibraries/Generic/src/Resize.c @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +// +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "DeeployBasicMath.h" + +/* Number of bytes per element. */ +static inline uint32_t _resize_element_size(resize_type_t type_tag) { + switch (type_tag) { + case RESIZE_TYPE_FLOAT32: + return sizeof(float32_t); + case RESIZE_TYPE_INT16: + case RESIZE_TYPE_UINT16: + return sizeof(int16_t); + case RESIZE_TYPE_INT32: + case RESIZE_TYPE_UINT32: + return sizeof(int32_t); + default: /* INT8, UINT8 */ + return sizeof(int8_t); + } +} + +/* Read one element as float for use in the linear interpolation path. */ +static inline float32_t _resize_read(const void *buf, int32_t idx, + resize_type_t type_tag) { + switch (type_tag) { + case RESIZE_TYPE_FLOAT32: + return ((const float32_t *)buf)[idx]; + case RESIZE_TYPE_INT8: + return (float32_t)((const int8_t *)buf)[idx]; + case RESIZE_TYPE_UINT8: + return (float32_t)((const uint8_t *)buf)[idx]; + case RESIZE_TYPE_INT16: + return (float32_t)((const int16_t *)buf)[idx]; + case RESIZE_TYPE_UINT16: + return (float32_t)((const uint16_t *)buf)[idx]; + case RESIZE_TYPE_INT32: + return (float32_t)((const int32_t *)buf)[idx]; + default: /* RESIZE_TYPE_UINT32 */ + return (float32_t)((const uint32_t *)buf)[idx]; + } +} + +/* Round val to the nearest integer, breaking ties toward the nearest even + * integer (banker's rounding). This matches numpy.round / the ONNX reference + * implementation. */ +static inline float32_t _round_half_to_even(float32_t val) { + float32_t f = floorf(val); + float32_t diff = val - f; + if (diff < 0.5f) + return f; + if (diff > 0.5f) + return f + 1.0f; + /* exactly 0.5: pick the even neighbour */ + return (fmodf(f, 2.0f) == 0.0f) ? f : f + 1.0f; +} + +/* Write a float result back as the element's native type. */ +static inline void _resize_write(void *buf, int32_t idx, float32_t val, + resize_type_t type_tag) { + switch (type_tag) { + case RESIZE_TYPE_FLOAT32: + ((float32_t *)buf)[idx] = val; + break; + case RESIZE_TYPE_INT8: + ((int8_t *)buf)[idx] = (int8_t)_round_half_to_even(val); + break; + case RESIZE_TYPE_UINT8: + ((uint8_t *)buf)[idx] = (uint8_t)_round_half_to_even(val); + break; + case RESIZE_TYPE_INT16: + ((int16_t *)buf)[idx] = (int16_t)_round_half_to_even(val); + break; + case RESIZE_TYPE_UINT16: + ((uint16_t *)buf)[idx] = (uint16_t)_round_half_to_even(val); + break; + case RESIZE_TYPE_INT32: + ((int32_t *)buf)[idx] = (int32_t)_round_half_to_even(val); + break; + default: /* RESIZE_TYPE_UINT32 */ + ((uint32_t *)buf)[idx] = (uint32_t)_round_half_to_even(val); + break; + } +} + +/* Map an output coordinate to its source coordinate in the input. */ +static float32_t _resize_get_coord(int32_t out_idx, int32_t in_size, + int32_t out_size, + resize_coord_mode_t coord_mode) { + float32_t x_scale = (float32_t)out_size / (float32_t)in_size; + switch (coord_mode) { + + case RESIZE_COORD_HALF_PIXEL: + return ((float32_t)out_idx + 0.5f) / x_scale - 0.5f; + + case RESIZE_COORD_HALF_PIXEL_SYMMETRIC: { + float32_t adjustment = + (float32_t)out_size / (floorf((float32_t)in_size + 0.5f) * x_scale); + return ((float32_t)out_idx + 0.5f) / x_scale * adjustment - 0.5f; + } + case RESIZE_COORD_ALIGN_CORNERS: + if (out_size == 1) + return 0.0f; + return (float32_t)out_idx * (float32_t)(in_size - 1) / + (float32_t)(out_size - 1); + + case RESIZE_COORD_PYTORCH_HALF_PIXEL: + if (out_size == 1) + return 0.0f; + return ((float32_t)out_idx + 0.5f) / x_scale - 0.5f; + + default: /* RESIZE_COORD_ASYMMETRIC */ + return (float32_t)out_idx / x_scale; + } +} + +/* Round a source coordinate to the nearest input index and clamp to [0, + * max_idx]. */ +static int32_t _resize_nearest_idx(float32_t x, int32_t max_idx, + resize_nearest_mode_t nearest_mode) { + int32_t in_idx; + switch (nearest_mode) { + case RESIZE_NEAREST_CEIL: + in_idx = (int32_t)ceilf(x); + break; + case RESIZE_NEAREST_ROUND_PREFER_FLOOR: + /* At exactly n+0.5 choose floor; otherwise standard round. */ + in_idx = (x - floorf(x) == 0.5f) ? (int32_t)floorf(x) : (int32_t)roundf(x); + break; + case RESIZE_NEAREST_ROUND_PREFER_CEIL: + in_idx = (int32_t)roundf(x); + break; + default: /* RESIZE_NEAREST_FLOOR */ + in_idx = (int32_t)floorf(x); + break; + } + return CLAMP(in_idx, 0, max_idx); +} + +void Resize(const void *input, void *output, resize_type_t type_tag, int32_t N, + int32_t C, int32_t spatial_dims, const int32_t *input_shape, + const int32_t *output_shape, resize_mode_t mode, + resize_coord_mode_t coord_mode, + resize_nearest_mode_t nearest_mode) { + + if (N <= 0 || C <= 0 || spatial_dims < 1 || + spatial_dims > RESIZE_MAX_SPATIAL_DIMS) + return; + + /* not implemented */ + if (mode == RESIZE_MODE_CUBIC || + coord_mode == RESIZE_COORD_TF_CROP_AND_RESIZE) + return; + + uint32_t elem_size = _resize_element_size(type_tag); + + /* Row-major strides for the spatial dimensions. */ + int32_t out_strides[RESIZE_MAX_SPATIAL_DIMS]; + int32_t in_strides[RESIZE_MAX_SPATIAL_DIMS]; + int32_t L_out = 1, L_in = 1; + out_strides[spatial_dims - 1] = 1; + in_strides[spatial_dims - 1] = 1; + for (int32_t d = spatial_dims - 2; d >= 0; d--) { + out_strides[d] = out_strides[d + 1] * output_shape[d + 1]; + in_strides[d] = in_strides[d + 1] * input_shape[d + 1]; + } + for (int32_t d = 0; d < spatial_dims; d++) { + L_out *= output_shape[d]; + L_in *= input_shape[d]; + } + + for (int32_t n = 0; n < N; n++) { + for (int32_t c = 0; c < C; c++) { + int32_t in_base = (n * C + c) * L_in; + int32_t out_base = (n * C + c) * L_out; + + for (int32_t oi = 0; oi < L_out; oi++) { + int32_t rem = oi; + + if (mode == RESIZE_MODE_NEAREST) { + /* Nearest-neighbour: map each spatial coord and copy the element. */ + int32_t in_flat = 0; + for (int32_t d = 0; d < spatial_dims; d++) { + int32_t out_idx = rem / out_strides[d]; + rem -= out_idx * out_strides[d]; + float32_t x = _resize_get_coord(out_idx, input_shape[d], + output_shape[d], coord_mode); + in_flat += + _resize_nearest_idx(x, input_shape[d] - 1, nearest_mode) * + in_strides[d]; + } + memcpy((char *)output + (uint32_t)(out_base + oi) * elem_size, + (const char *)input + + (uint32_t)(in_base + in_flat) * elem_size, + elem_size); + + } else { + + float32_t x_in[RESIZE_MAX_SPATIAL_DIMS]; // fractional input coord + int32_t lo[RESIZE_MAX_SPATIAL_DIMS]; // index lower than x_in + int32_t hi[RESIZE_MAX_SPATIAL_DIMS]; // index higher than x_in + float32_t w_lo[RESIZE_MAX_SPATIAL_DIMS]; // low interpolation weight + float32_t w_hi[RESIZE_MAX_SPATIAL_DIMS]; // high interpolation weight + + /* + prepares the data for the N-linear interpolation that follows. + For each spatial dimension d: + - Extract the per-dimension output coordinate from the flat index oi + - Map the output coordinate to a fractional input coordinate x_in + - Find the two bracketing input indices (lo, hi) along dimension d + - Compute interpolation weights (w_hi, w_lo) + */ + for (int32_t d = 0; d < spatial_dims; d++) { + int32_t out_idx = rem / out_strides[d]; + rem -= out_idx * out_strides[d]; // flat output index + x_in[d] = _resize_get_coord(out_idx, input_shape[d], + output_shape[d], coord_mode); + x_in[d] = CLAMP(x_in[d], 0.0f, (float32_t)(input_shape[d] - 1)); + lo[d] = (int32_t)floorf(x_in[d]); + hi[d] = (lo[d] + 1 < input_shape[d]) ? lo[d] + 1 : lo[d]; + w_hi[d] = x_in[d] - (float32_t)lo[d]; + w_lo[d] = 1.0f - w_hi[d]; + } + + /* + N-linear interpolation: weighted sum over the 2^spatial_dims corners. + + example: spatial_dims = 2 (bilinear), there are 2^2 = 4 corners + corner=0 (bits: 00) → (lo[0], lo[1]) weight = w_lo[0] * w_lo[1] + corner=1 (bits: 01) → (hi[0], lo[1]) weight = w_hi[0] * w_lo[1] + corner=2 (bits: 10) → (lo[0], hi[1]) weight = w_lo[0] * w_hi[1] + corner=3 (bits: 11) → (hi[0], hi[1]) weight = w_hi[0] * w_hi[1] + */ + float32_t result = 0.0f; + for (int32_t corner = 0, n_corners = 1 << spatial_dims; + corner < n_corners; corner++) { + float32_t weight = 1.0f; + int32_t in_flat = 0; + for (int32_t d = 0; d < spatial_dims; d++) { + if ((corner >> d) & 1) { + weight *= w_hi[d]; + in_flat += hi[d] * in_strides[d]; + } else { + weight *= w_lo[d]; + in_flat += lo[d] * in_strides[d]; + } + } + result += weight * _resize_read(input, in_base + in_flat, type_tag); + } + _resize_write(output, out_base + oi, result, type_tag); + } + } + } + } +} From 7078235922cce857dfae24616ad411b0a2865bc9 Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Fri, 19 Jun 2026 21:49:45 +0000 Subject: [PATCH 07/12] refactor of the generic Scatter --- TargetLibraries/Generic/inc/kernel/Scatter.h | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/TargetLibraries/Generic/inc/kernel/Scatter.h b/TargetLibraries/Generic/inc/kernel/Scatter.h index e820b836aa..8f4d2a41ca 100644 --- a/TargetLibraries/Generic/inc/kernel/Scatter.h +++ b/TargetLibraries/Generic/inc/kernel/Scatter.h @@ -17,11 +17,13 @@ #define SCATTER_MAX_NDIM 8 /* Reduction modes (mirrors ONNX ScatterElements `reduction` attribute). */ -#define SCATTER_REDUCTION_NONE 0 -#define SCATTER_REDUCTION_ADD 1 -#define SCATTER_REDUCTION_MUL 2 -#define SCATTER_REDUCTION_MIN 3 -#define SCATTER_REDUCTION_MAX 4 +typedef enum { + SCATTER_REDUCTION_NONE = 0, + SCATTER_REDUCTION_ADD, + SCATTER_REDUCTION_MUL, + SCATTER_REDUCTION_MIN, + SCATTER_REDUCTION_MAX, +} scatter_reduction_t; /* * DECLARE_SCATTER_FN(SUFFIX, DATA_TYPE) @@ -30,10 +32,11 @@ * The matching definition lives in Scatter.c via DEFINE_SCATTER_FN. */ #define DECLARE_SCATTER_FN(SUFFIX, DATA_TYPE) \ - void Scatter_##SUFFIX( \ - const DATA_TYPE *data, const int32_t *indices, const DATA_TYPE *updates, \ - DATA_TYPE *output, int32_t ndim, const int32_t *data_shape, \ - const int32_t *indices_shape, int32_t axis, int32_t reduction) + void Scatter_##SUFFIX(const DATA_TYPE *data, const int32_t *indices, \ + const DATA_TYPE *updates, DATA_TYPE *output, \ + int32_t ndim, const int32_t *data_shape, \ + const int32_t *indices_shape, int32_t axis, \ + scatter_reduction_t reduction) DECLARE_SCATTER_FN(fp32, float32_t); DECLARE_SCATTER_FN(s8, int8_t); From 930f19f3dc355fb56987305d1611db3ff8ad4649 Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Mon, 22 Jun 2026 14:10:01 +0000 Subject: [PATCH 08/12] about ConvTranspose: refactor parser, add tests (1D and 2D), add 2D kernel --- Deeploy/Targets/Generic/Bindings.py | 22 +- Deeploy/Targets/Generic/Layers.py | 61 ++-- Deeploy/Targets/Generic/Parsers.py | 261 +++++++++++------- Deeploy/Targets/Generic/Platform.py | 29 +- .../Templates/ConvTransposeTemplate.py | 45 ++- .../FP32/ConvTranspose/Regular_1D/inputs.npz | Bin 0 -> 296 bytes .../ConvTranspose/Regular_1D/network.onnx | Bin 0 -> 304 bytes .../FP32/ConvTranspose/Regular_1D/outputs.npz | Bin 0 -> 330 bytes .../FP32/ConvTranspose/Regular_2D/inputs.npz | Bin 0 -> 392 bytes .../ConvTranspose/Regular_2D/network.onnx | Bin 0 -> 356 bytes .../FP32/ConvTranspose/Regular_2D/outputs.npz | Bin 0 -> 778 bytes DeeployTest/test_generic_config.py | 11 + .../Generic/inc/DeeployBasicMath.h | 2 +- .../Generic/inc/kernel/ConvTranspose1d_fp32.h | 16 -- .../Generic/inc/kernel/ConvTranspose_fp32.h | 23 ++ .../Generic/src/ConvTranspose1d_fp32.c | 50 ---- .../Generic/src/ConvTranspose_fp32.c | 82 ++++++ .../Generic/src/GlobalAveragePool_fp32.c | 2 +- .../Generic/src/GlobalMaxPool_fp32.c | 4 - TargetLibraries/Generic/src/HardSwish_fp32.c | 2 +- TargetLibraries/Generic/src/Layernorm_fp32.c | 17 +- 21 files changed, 389 insertions(+), 238 deletions(-) create mode 100644 DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/outputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/inputs.npz create mode 100644 DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/network.onnx create mode 100644 DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/outputs.npz delete mode 100644 TargetLibraries/Generic/inc/kernel/ConvTranspose1d_fp32.h create mode 100644 TargetLibraries/Generic/inc/kernel/ConvTranspose_fp32.h delete mode 100644 TargetLibraries/Generic/src/ConvTranspose1d_fp32.c create mode 100644 TargetLibraries/Generic/src/ConvTranspose_fp32.c diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index 4e68aee904..f5483bf669 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -327,19 +327,35 @@ for type in FloatDataTypes ] -BasicConvTransposeBindings = [ +BasicConvTranspose1DBindings = [ NodeBinding( ConvChecker( [PointerClass(type), PointerClass(type), PointerClass(type)], # input, weight, bias [PointerClass(type)]), - ConvTransposeTemplate.referenceTemplate, + ConvTransposeTemplate.referenceTemplate1D, BasicTransformer) for type in FloatDataTypes ] + [ NodeBinding( ConvChecker( [PointerClass(type), PointerClass(type)], # input, weight [PointerClass(type)]), - ConvTransposeTemplate.referenceTemplate, + ConvTransposeTemplate.referenceTemplate1D, + BasicTransformer) for type in FloatDataTypes +] + +BasicConvTranspose2DBindings = [ + NodeBinding( + ConvChecker( + [PointerClass(type), PointerClass(type), PointerClass(type)], # input, weight, bias + [PointerClass(type)]), + ConvTransposeTemplate.referenceTemplate2D, + BasicTransformer) for type in FloatDataTypes +] + [ + NodeBinding( + ConvChecker( + [PointerClass(type), PointerClass(type)], # input, weight + [PointerClass(type)]), + ConvTransposeTemplate.referenceTemplate2D, BasicTransformer) for type in FloatDataTypes ] diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index b737e92f3f..bb91244447 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -662,45 +662,46 @@ def __init__(self, maps: List[NodeMapper]): def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, channels_first) -> Tuple[Shape, Shape]: - """ - Infers output shapes for ConvTranspose using only static info. - - inputShapes[0]: input tensor shape (e.g., [N, C_in, W] for 1D, [N, C_in, H, W] for 2D) - - inputShapes[1]: weight tensor shape (e.g., [C_in, C_out // group, kW] for 1D) - - outputShapes[0]: output tensor shape (to be updated) - """ newInputShapes = list(inputShapes) - newOutputShapes = list(outputShapes) - group = operatorRepresentation.get('group', 1) - weight_shape = inputShapes[1] - if newOutputShapes and len(newOutputShapes[0]) >= 2: - # For 1D: weight_shape = [C_in, C_out // group, kW] - # For 2D: weight_shape = [C_in, C_out // group, kH, kW] - ch_out = weight_shape[1] * group - if channels_first: - newOutputShapes[0][1] = ch_out - else: - newOutputShapes[0][-1] = ch_out + input_shape = inputShapes[0] # [N, C_in, d0, ...] + weight_shape = inputShapes[1] # [C_in, C_out//group, k0, ...] + group = operatorRepresentation.get('group', 1) - return newInputShapes, newOutputShapes + batch = input_shape[0] + spatial_in = list(input_shape[2:]) if channels_first else list(input_shape[1:-1]) + ndim = len(spatial_in) - def computeOps(self): - opRep = self.mapper.parser.operatorRepresentation + kernel_shape = list(weight_shape[2:]) + C_out = weight_shape[1] * group - groups = opRep.get('group', 1) - kernel_shape = np.prod(opRep['kernel_shape']) # es. [3, 3] -> 9 - ch_in = opRep['ch_im_in'] - ch_out = opRep['ch_im_out'] + strides = operatorRepresentation.get('strides') or [1] * ndim + dilations = operatorRepresentation.get('dilations') or [1] * ndim + output_padding = operatorRepresentation.get('output_padding') or [0] * ndim + pads = operatorRepresentation.get('pads') or [0] * (2 * ndim) - opsPerPx = int(kernel_shape * ch_in * ch_out / groups) * 2 + spatial_out = [(spatial_in[d] - 1) * strides[d] - pads[d] - pads[d + ndim] + dilations[d] * + (kernel_shape[d] - 1) + output_padding[d] + 1 for d in range(ndim)] - # ConvTranspose upscales spatial dims, quindi num pixel viene da output - if 'dim_im_out_y' in opRep: - numPx = opRep['dim_im_out_x'] * opRep['dim_im_out_y'] + if channels_first: + output_shape = [batch, C_out] + spatial_out else: - numPx = opRep['dim_im_out_x'] + output_shape = [batch] + spatial_out + [C_out] - return numPx * opsPerPx + return newInputShapes, [output_shape] + + def computeOps(self): + rep = self.mapper.parser.operatorRepresentation + + group = rep.get('group', 1) + kernel_shape = np.prod(rep['kernel_shape']) # es. [3, 3] -> 9 + channels = rep['channels'] + feature_maps = rep['feature_maps'] + + ops_per_px = int(kernel_shape * feature_maps * channels // group) * 2 + num_px = np.prod(rep['output_shape']) + + return num_px * ops_per_px class CeilLayer(SingleOperationPerElementLayer): diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index 976e9a18ce..f4b8d5bae4 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -2743,127 +2743,190 @@ def __init__(self): super().__init__() def parseNode(self, node: gs.Node) -> bool: + + if not all( + [node.op == 'ConvTranspose', + len(node.inputs) >= 2 and len(node.inputs) <= 3, + len(node.outputs) == 1]): + return False + # Extract ONNX attributes with defaults - strides = node.attrs.get('strides', [1]) + auto_pad: str = node.attrs.get('auto_pad', 'NOTSET') + group: int = node.attrs.get('group', 1) + + if not all([ + auto_pad in ('NOTSET', 'SAME_UPPER', 'SAME_LOWER', 'VALID'), + group >= 1, + ]): + return False - pads = node.attrs.get('pads', [0, 0]) - kernel_shape = node.attrs.get('kernel_shape', None) - dilations = node.attrs.get('dilations', [1]) - group = node.attrs.get('group', 1) + self.operatorRepresentation['auto_pad'] = auto_pad + self.operatorRepresentation['group'] = group - # Check for required attributes - wellFormed = (kernel_shape is not None and len(node.outputs) == 1) - if wellFormed: - self.operatorRepresentation['strides'] = strides - self.operatorRepresentation['pads'] = pads - self.operatorRepresentation['kernel_shape'] = kernel_shape - self.operatorRepresentation['dilations'] = dilations - self.operatorRepresentation['group'] = group - self.operatorRepresentation['nodeName'] = node.name - self.operatorRepresentation['nodeOp'] = node.op - return wellFormed + self.operatorRepresentation['dilations'] = node.attrs.get('dilations', None) # default: ones + self.operatorRepresentation['kernel_shape'] = node.attrs.get('kernel_shape', None) # default from weights + self.operatorRepresentation['output_padding'] = node.attrs.get('output_padding', None) # default: zeros + self.operatorRepresentation['output_shape'] = node.attrs.get('output_shape', None) # overwrite pads + self.operatorRepresentation['pads'] = node.attrs.get('pads', None) # default: zeros + self.operatorRepresentation['strides'] = node.attrs.get('strides', None) # default: ones + + self.operatorRepresentation['nodeOp'] = node.op + self.operatorRepresentation['nodeName'] = node.name + + return True def parseNodeCtxt(self, ctxt: NetworkContext, node: gs.Node, channels_first: bool = True): - # Register buffer names for codegen - self.operatorRepresentation['data_in'] = node.inputs[0].name - self.operatorRepresentation['weight'] = node.inputs[1].name - self.operatorRepresentation['data_out'] = node.outputs[0].name + + rep = self.operatorRepresentation + + # inputs/outputs + data_in: VariableBuffer = ctxt.lookup(node.inputs[0].name) + weight: ConstantBuffer = ctxt.lookup(node.inputs[1].name) + data_out: VariableBuffer = ctxt.lookup(node.outputs[0].name) + + # check inputs/outputs + if not all([ + len(data_in.shape) == len(data_out.shape) == len(weight.shape), # same ndims + all(s > 0 for s in data_in.shape), # no empty dim + all(s > 0 for s in weight.shape), # no empty dim + all(s > 0 for s in data_out.shape), # no empty dim + data_in.shape[0] == data_out.shape[0], # same batch size + len(data_in.shape) > 2, # at least batch, channels/feature_maps, one spatial dim + ]): + return ctxt, False + + # retrieve info from inputs/outputs + batch_size, channels = data_in.shape[:2] + input_shape = data_in.shape[2:] # spatial input shape + output_shape = data_out.shape[2:] # spatial output shape + + spatial_dims = len(input_shape) + + kernel_shape = list(weight.shape[2:]) + feature_maps = weight.shape[1] * rep['group'] # input channels + + # optional inputs if len(node.inputs) == 3: - self.operatorRepresentation['bias'] = node.inputs[2].name - self.operatorRepresentation['has_bias'] = "true" - else: - self.operatorRepresentation['has_bias'] = "false" - # Get output shape from context - data_out = ctxt.lookup(node.outputs[0].name) - out_shape = data_out.shape - if len(out_shape) == 3: - self.operatorRepresentation['dim_im_out_x'] = out_shape[2] - elif len(out_shape) == 4: - self.operatorRepresentation['dim_im_out_x'] = out_shape[2] - self.operatorRepresentation['dim_im_out_y'] = out_shape[3] - - stride_x, stride_y = 1, 1 - if "strides" in node.attrs: - stride_y = node.attrs["strides"][0] - stride_x = node.attrs["strides"][1] if len(node.attrs["strides"]) > 1 else stride_y - self.operatorRepresentation["stride_y"] = stride_y - self.operatorRepresentation["stride_x"] = stride_x - - if "kernel_shape" in node.attrs: - kernel_shape = node.attrs["kernel_shape"] - kernel_shape_x = kernel_shape[0] - # For 2D, kernel_shape may have two elements - kernel_shape_y = kernel_shape[1] if len(kernel_shape) > 1 else kernel_shape_x - else: - kernel_shape_x = 1 - kernel_shape_y = 1 + bias: ConstantBuffer = ctxt.lookup(node.inputs[2].name) + if not (len(bias.shape) == 1 and bias.shape[0] == feature_maps): + return ctxt, False + rep['bias'] = bias.name + + # attributes with possible inconsistences + kernel_shape_attr: list[int] = rep['kernel_shape'] or kernel_shape + output_shape_attr: list[int] = rep['output_shape'] or output_shape + # check possible inconsistences + if not all([ + kernel_shape_attr == kernel_shape, + output_shape_attr == output_shape, + ]): + return ctxt, False + + # other attributes + dilations: list[int] = rep['dilations'] or [1] * spatial_dims + output_padding: list[int] = rep['output_padding'] or [0] * spatial_dims + strides: list[int] = rep['strides'] or [1] * spatial_dims + + # auto_pad may lead to overwrite pads + if rep['auto_pad'] == 'NOTSET': + pads: list[int] = rep['pads'] or [0] * (2 * spatial_dims) + elif rep['auto_pad'] == 'VALID': + pads = [0] * (2 * spatial_dims) + else: # SAME_UPPER, SAME_LOWER + starts = [0] * spatial_dims + ends = [0] * spatial_dims + for i in range(spatial_dims): + total_padding = (strides[i] * (input_shape[i] - 1) + output_padding[i] + + ((kernel_shape[i] - 1) * dilations[i] + 1) - output_shape[i]) + half = total_padding // 2 + if rep['auto_pad'] == 'SAME_UPPER': + starts[i] = half + ends[i] = total_padding - half + else: # SAME_LOWER + ends[i] = half + starts[i] = total_padding - half + pads = starts + ends + + # check other attributes + if not all([ + len(dilations) == spatial_dims, + len(output_padding) == spatial_dims, + len(pads) == 2 * spatial_dims, + len(strides) == spatial_dims, + all(d > 0 for d in dilations), + all(p >= 0 for p in output_padding), + all(p >= 0 for p in pads), + all(s > 0 for s in strides), + ]): + return ctxt, False + + # fill operatorRepresentation + rep['data_in'] = data_in.name + rep['weight'] = weight.name + rep['data_out'] = data_out.name + rep['has_bias'] = int('bias' in rep) + + rep['kernel_shape'] = kernel_shape + rep['output_shape'] = output_shape + rep['pads'] = pads + rep['strides'] = strides + rep['dilations'] = dilations + rep['output_padding'] = output_padding + + rep['batch_size'] = batch_size + rep['channels'] = channels + rep['feature_maps'] = feature_maps + rep['input_shape'] = input_shape - data_in = ctxt.lookup(node.inputs[0].name) - data_out = ctxt.lookup(node.outputs[0].name) - in_shape = data_in.shape - out_shape = data_out.shape - - self.operatorRepresentation['ch_im_in'] = in_shape[1] - self.operatorRepresentation['dim_im_in_y'] = in_shape[2] - self.operatorRepresentation['ch_im_out'] = out_shape[1] - self.operatorRepresentation['dim_im_out_y'] = out_shape[2] - - self.operatorRepresentation[ - 'batchOffsetIn'] = self.operatorRepresentation['ch_im_in'] * self.operatorRepresentation['dim_im_in_y'] - self.operatorRepresentation[ - 'batchOffsetOut'] = self.operatorRepresentation['ch_im_out'] * self.operatorRepresentation['dim_im_out_y'] return ctxt, True class ConvTranspose1DParser(ConvTransposeParser): - def __init__(self): - super().__init__() + def parseNodeCtxt(self, + ctxt: NetworkContext, + node: gs.Node, + channels_first: bool = True) -> Tuple[NetworkContext, bool]: - def parseNode(self, node: gs.Node) -> bool: - # 1D ConvTranspose expects 3D input/output and 3D weight - wellFormed = super().parseNode(node) - ret = False - if wellFormed: - ret = all([ - # Make sure strides are 2D - len(node.attrs['strides']) == 1, - len(node.attrs['pads']) == 2, - len(node.attrs['dilations']) == 1, - ]) - if ret: + ctxt, ret = super().parseNodeCtxt(ctxt, node, channels_first) + if not ret: + return ctxt, False - self.operatorRepresentation['kernel_shape'] = node.attrs['kernel_shape'] - self.operatorRepresentation['dim_kernel_y'] = int(self.operatorRepresentation['kernel_shape'][0]) - self.operatorRepresentation['dilation_y'] = int(self.operatorRepresentation['dilations'][0]) - self.operatorRepresentation['padding_y'] = int(self.operatorRepresentation['pads'][0]) - self.operatorRepresentation['stride_y'] = int(self.operatorRepresentation['strides'][0]) + rep = self.operatorRepresentation + spatial_dims = len(rep['kernel_shape']) + if spatial_dims != 1: + return ctxt, False - return ret + rep['input_length'], = rep['input_shape'] + rep['output_length'], = rep['output_shape'] + rep['kernel_length'], = rep['kernel_shape'] + rep['stride'], = rep['strides'] + + return ctxt, True + + +class ConvTranspose2DParser(ConvTransposeParser): def parseNodeCtxt(self, ctxt: NetworkContext, node: gs.Node, channels_first: bool = True) -> Tuple[NetworkContext, bool]: - newCtxt, ret = super().parseNodeCtxt(ctxt, node, channels_first) + ctxt, ret = super().parseNodeCtxt(ctxt, node, channels_first) + if not ret: + return ctxt, False - if ret: - data_in = newCtxt.lookup(node.inputs[0].name) - data_out = newCtxt.lookup(node.outputs[0].name) - in_shape = data_in.shape - out_shape = data_out.shape - self.operatorRepresentation['batch'] = in_shape[0] - self.operatorRepresentation['ch_im_in'] = in_shape[1] - self.operatorRepresentation['dim_im_in_y'] = in_shape[2] - self.operatorRepresentation['ch_im_out'] = out_shape[1] - self.operatorRepresentation['dim_im_out_y'] = out_shape[2] - self.operatorRepresentation[ - "batchOffsetIn"] = self.operatorRepresentation["ch_im_in"] * self.operatorRepresentation["dim_im_in_y"] - self.operatorRepresentation["batchOffsetOut"] = self.operatorRepresentation[ - "ch_im_out"] * self.operatorRepresentation["dim_im_out_y"] - return newCtxt, True - return ctxt, False + rep = self.operatorRepresentation + spatial_dims = len(rep['kernel_shape']) + if spatial_dims != 2: + return ctxt, False + + rep['input_height'], rep['input_width'] = rep['input_shape'] + rep['output_height'], rep['output_width'] = rep['output_shape'] + rep['kernel_height'], rep['kernel_width'] = rep['kernel_shape'] + rep['stride_h'], rep['stride_w'] = rep['strides'] + + return ctxt, True class SqrtParser(UnaryElementWiseParser): diff --git a/Deeploy/Targets/Generic/Platform.py b/Deeploy/Targets/Generic/Platform.py index d8fe1e4d4d..413dd6f878 100644 --- a/Deeploy/Targets/Generic/Platform.py +++ b/Deeploy/Targets/Generic/Platform.py @@ -8,9 +8,9 @@ StructBuffer, TopologyOptimizer, TransientBuffer, VariableBuffer from Deeploy.Targets.Generic.Bindings import BasicAddBindings, BasicAveragePool1DBindings, BasicAveragePool2DBindings, \ BasicBatchNormBindings, BasicCeilBindings, BasicClipBindings, BasicCol2ImBindings, BasicConcatBindings, \ - BasicConv1DBindings, BasicConv2DBindings, BasicConvTransposeBindings, BasicDebugPrintBindings, \ - BasicDequantBindings, BasicDivBindings, BasicDWConv1DBinding, BasicDWConv2DBindings, BasicEluBindings, \ - BasicExpBindings, BasicFloorBindings, BasicGatherBindings, BasicGELUBindings, BasicGEMMBindings, \ + BasicConv1DBindings, BasicConv2DBindings, BasicConvTranspose1DBindings, BasicConvTranspose2DBindings, \ + BasicDebugPrintBindings, BasicDequantBindings, BasicDivBindings, BasicDWConv1DBinding, BasicDWConv2DBindings, \ + BasicEluBindings, BasicExpBindings, BasicFloorBindings, BasicGatherBindings, BasicGELUBindings, BasicGEMMBindings, \ BasicGlobalAveragePoolBindings, BasicGlobalMaxPoolBindings, BasicGroupNormBindings, BasicHardSigmoidBindings, \ BasicHardSwishBindings, BasicInstanceNormBindings, BasicITAPartialSoftmaxBinding, BasicITASoftmaxBinding, \ BasicLayerNormBindings, BasicLeakyReluBindings, BasicMatMulBindings, BasicMaxPool1DBindings, \ @@ -27,15 +27,15 @@ ReshapeLayer, ResizeLayer, RQIntegerDivLayer, RQSiGELULayer, ScatterLayer, SeluLayer, SigmoidLayer, SliceLayer, \ SoftmaxLayer, SqrtLayer, SubLayer, SwishLayer, TransposeLayer from Deeploy.Targets.Generic.Parsers import AddParser, AveragePool1DParser, AveragePool2DParser, BatchNormParser, \ - CeilParser, ClipParser, Col2ImParser, ConcatParser, ConvTranspose1DParser, DebugParser, DequantParser, DivParser, \ - DummyParser, EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, GenericConv1DParser, \ - GenericConv2DParser, GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, GenericMaxPool2DParser, \ - GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, HardSwishParser, \ - InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, LeakyReluParser, \ - MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, ReduceMeanParser, \ - ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, ResizeParser, RQIntegerDivParser, RQSiGELUParser, \ - ScatterParser, SeluParser, SigmoidParser, SliceParser, SoftmaxParser, SqrtParser, SubParser, SwishParser, \ - TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser + CeilParser, ClipParser, Col2ImParser, ConcatParser, ConvTranspose1DParser, ConvTranspose2DParser, DebugParser, \ + DequantParser, DivParser, DummyParser, EluParser, ExpParser, FlattenParser, FloorParser, GatherParser, GELUParser, \ + GenericConv1DParser, GenericConv2DParser, GenericDWConv1DParser, GenericDWConv2DParser, GenericGEMMParser, \ + GenericMaxPool2DParser, GlobalAveragePoolParser, GlobalMaxPoolParser, GroupNormParser, HardSigmoidParser, \ + HardSwishParser, InstanceNormParser, IntegerDivParser, ITAMaxParser, ITAPartialMaxParser, LayerNormParser, \ + LeakyReluParser, MatMulParser, MaxPool1DParser, MulParser, Pad1DParser, Pad2DParser, PowParser, QuantParser, \ + ReduceMeanParser, ReduceSumParser, ReluParser, RequantShiftParser, ReshapeParser, ResizeParser, \ + RQIntegerDivParser, RQSiGELUParser, ScatterParser, SeluParser, SigmoidParser, SliceParser, SoftmaxParser, \ + SqrtParser, SubParser, SwishParser, TransposeParser, UnsqueezeParser, iLayerNormParser, iSoftmaxParser from Deeploy.Targets.Generic.Templates import AllocateTemplate, FreeTemplate from Deeploy.Targets.Generic.TopologyOptimizationPasses.Passes import DequantPatternPass, ExtractPaddingFromConvPass, \ ExtractPaddingFromPoolPass, MatMulAddMergePass, MergeConstAddAndRequantPass, QuantPatternPass, \ @@ -81,7 +81,8 @@ QuantMapper = NodeMapper(QuantParser(), BasicQuantBindings) DequantMapper = NodeMapper(DequantParser(), BasicDequantBindings) BatchNormalizationMapper = NodeMapper(BatchNormParser(), BasicBatchNormBindings) -ConvTransposeMapper = NodeMapper(ConvTranspose1DParser(), BasicConvTransposeBindings) +ConvTranspose1DMapper = NodeMapper(ConvTranspose1DParser(), BasicConvTranspose1DBindings) +ConvTranspose2DMapper = NodeMapper(ConvTranspose2DParser(), BasicConvTranspose2DBindings) SliceMapper = NodeMapper(SliceParser(), BasicSliceBindings) CeilMapper = NodeMapper(CeilParser(), BasicCeilBindings) FloorMapper = NodeMapper(FloorParser(), BasicFloorBindings) @@ -151,7 +152,7 @@ 'Quant': QuantLayer([QuantMapper]), 'Dequant': DequantLayer([DequantMapper]), 'BatchNormalization': BatchNormalizationLayer([BatchNormalizationMapper]), - 'ConvTranspose': ConvTransposeLayer([ConvTransposeMapper]), + 'ConvTranspose': ConvTransposeLayer([ConvTranspose1DMapper, ConvTranspose2DMapper]), 'Ceil': CeilLayer([CeilMapper]), 'Floor': FloorLayer([FloorMapper]), 'Clip': ClipLayer([ClipMapper]), diff --git a/Deeploy/Targets/Generic/Templates/ConvTransposeTemplate.py b/Deeploy/Targets/Generic/Templates/ConvTransposeTemplate.py index 9bf864c91f..0461283dec 100644 --- a/Deeploy/Targets/Generic/Templates/ConvTransposeTemplate.py +++ b/Deeploy/Targets/Generic/Templates/ConvTransposeTemplate.py @@ -4,10 +4,35 @@ from Deeploy.DeeployTypes import NodeTemplate -referenceTemplate = NodeTemplate(""" +referenceTemplate2D = NodeTemplate(""" <% -batchOffsetIn = ch_im_in * dim_im_in_y -batchOffsetOut = ch_im_out * dim_im_out_y +batch_stride_input = channels * input_height * input_width +batch_stride_output = feature_maps * output_height * output_width +%> + +// 2D Transposed Conv (Name: ${nodeName}, Op: ${nodeOp}) +BEGIN_SINGLE_CORE + ${data_in_type.typeName} ref_${data_out}_${data_in} = ${data_in}; + ${data_out_type.typeName} ref_${data_out}_${data_out} = ${data_out}; + + for (uint32_t n=0; n<${batch_size}; ++n) { + ConvTranspose2d_fp32( + ref_${data_out}_${data_in}, ${channels}, ${input_height}, + ${input_width}, ${weight}, ${feature_maps}, ${kernel_height}, + ${kernel_width}, ${stride_h}, ${stride_w}, ${bias}, ${has_bias}, + ref_${data_out}_${data_out}, ${output_height}, ${output_width} + ); + + ref_${data_out}_${data_in} += ${batch_stride_input}; + ref_${data_out}_${data_out} += ${batch_stride_output}; + } +END_SINGLE_CORE +""") + +referenceTemplate1D = NodeTemplate(""" +<% +batch_stride_input = channels * input_length +batch_stride_output = feature_maps * output_length %> // 1D Transposed Conv (Name: ${nodeName}, Op: ${nodeOp}) @@ -15,17 +40,15 @@ ${data_in_type.typeName} ref_${data_out}_${data_in} = ${data_in}; ${data_out_type.typeName} ref_${data_out}_${data_out} = ${data_out}; - for (uint32_t n=0; n<${batch}; ++n) { + for (uint32_t n=0; n<${batch_size}; ++n) { ConvTranspose1d_fp32( - ref_${data_out}_${data_in}, ${ch_im_in}, ${dim_im_in_y}, - ${weight}, ${ch_im_out}, ${dim_kernel_y}, - ${stride_y}, - ${bias}, ${has_bias}, - ref_${data_out}_${data_out}, ${dim_im_out_y} + ref_${data_out}_${data_in}, ${channels}, ${input_length}, ${weight}, + ${feature_maps}, ${kernel_length}, ${stride}, ${bias}, ${has_bias}, + ref_${data_out}_${data_out}, ${output_length} ); - ref_${data_out}_${data_in} += ${batchOffsetIn}; - ref_${data_out}_${data_out} += ${batchOffsetOut}; + ref_${data_out}_${data_in} += ${batch_stride_input}; + ref_${data_out}_${data_out} += ${batch_stride_output}; } END_SINGLE_CORE """) diff --git a/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/inputs.npz b/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..f55ba85683a879442a4c523551202977aea0f951 GIT binary patch literal 296 zcmWIWW@gc4fB;1XxBZ)9|3d*Mg9t-rUO{PzUS2^ZBZC0L0;n<=J=rhRHz1Ocp^Twg zJteg`xk%kgLER?JL|sQgJuSbeq$n{jKEEg>6(sJKm{Xhz6fe$5EJy|NH4JqWjC2%C zG<6he703fzy53U`Oy!t*K=QVf!*BkX2ZR1~9-Q%R^MMJxN(VjVJRJhO8JTpMQ9TE8 fI|y?EF$^>?f>>~`26(fwfdm+V&>TqL25}ewcRWRf literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/network.onnx b/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..c4158441d2d0c9ec56bbce3a699789fa52e0669e GIT binary patch literal 304 zcmd7SQNqQNl$ls8#Fk%L0u)u^b zD=x?{PSq0N;!MfRNi50C&ntFdT)@b##lyv#UX)*2AOYktX$f=jWTzJ8rRKyJXCxM+ zIxvBh@N%&fB&HNQFgP%PxO`mf#U(|VDXGO^X(93ayu6C|^rFOqjN;Ow^wj)32uI6^ zg9!u$7@eSQ^HR9{X`VyTuMPW`Ih}X#U0>|D;kM`jhMS@X`qtDqI!yWMpd|uU5B7r> xN4WL01FiGcIz&lBJt-u_CBVTb#KXnJ!3e}mK+KXP!v*y$7FiA_7A^(>AppDoQzQTY literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/outputs.npz b/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_1D/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..532fbdaa5ac0d6410f3bc748b63aa422f8696ca0 GIT binary patch literal 330 zcmWIWW@gc4fB;2?+(k-<{zCy5g9t-@X-Pq8iC$hoB_o3X!vUx|7(Ll9)HfiKk)e#C zT0JGTIJrpONT#GJxIUY*Ya5^wO>)^S*JV&FzAC7(}svJ#puN_j#Npn13H|6lTcGtsm zgdQIf2)ub{(@n0!m(KDY`n;#}(5$yjhXcGBnRJ;^eGl>*2y+233^Xu;SctF)@MdKL Pi7*181(5y&;xGUJ{fJsE literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/inputs.npz b/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..c46f130fc3ff9fe41c8fc469570063214d16a9d8 GIT binary patch literal 392 zcmWIWW@gc4fB;2?fK7rw{zCyLg9t-rUO{PzUS2^ZBZB|~10z%&l%DJt>KhQr$WX>m zt)7xvoLr=CrJ!z;W}>d6pq`drR8o|f7oT60k_r-cOUx-w1&SAEBo?Fs`5J~g3Pw5# zCP1XAqfo0r2H;xdA$aiH_r!xg8?qb?>IDvX+}E++@HP41HRE*$WPkVCZ_=*>Iv+G!!|K3&Y|g=krjUbmzkl27 z`M+`a7J1u#A^Vj5SGG7DaGH>Fa7pB|1MTUP5A0Ibbj*1b?I6J2doaM8kx7>sHSoY; g1PmWeXi$Nu25@-6gEGLIl?^1!2!!T9+8Qhk0G7CNmjD0& literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/network.onnx b/DeeployTest/Tests/Kernels/FP32/ConvTranspose/Regular_2D/network.onnx new file mode 100644 index 0000000000000000000000000000000000000000..d9bc80466555c8db83027c0b27442284da3d0b76 GIT binary patch literal 356 zcmd7SQNqQNl$ls8#Fk%L0u)u^b zD=x?{PSq0P;!MfRNi50C&ntFdbYNV-$gai1#hPA}Us@mm3U_QSXT&< zd+d{8Z9`llfXK@?>hg_Ixw;}2v4tpOumAn=>LbSwt4^?p0-~6gr8Z>hc@mN*(+GKd zlBdbj8}%8v>3RArwL6z*w}jY2PL z>+Olfg_wlP{bebS=t21kGT!=3w+A+3$GAV;TPMWVt&P~u6*4*TlXOSE5fIb$bi^tf zPYhkAwn_?727#IrWxD6Ii}?N5Vo z+Hq88nnYd7LzGS2iP@59uxH7G`Ep^DQp{X}FP4WmcA7>HS^vA279f!OEEcs0aKu@(ue`KX7D zoEE?tHZiRgvv^9F%mgJhVW-9y1xZ-#Hj*4-b!@dp#;h+HAv}gh96{e1H+wL z;e1ChBTK)HOB~YxS|CVE-VOKLNKja61)~#f^oSi(vAqtktUZft#d=1SDx)Jqi*V># zHu~An4W)x}l;Bs1Zd#@Q-E|K%aJ4wUrxM*2`9RS~E3Hzn*aFVK^t#z}4|juUx0SKn V`!CBX#U@X8!g7_^<$b@o{splQG+zJ! literal 0 HcmV?d00001 diff --git a/DeeployTest/test_generic_config.py b/DeeployTest/test_generic_config.py index eaea3d6400..abe14bfff0 100644 --- a/DeeployTest/test_generic_config.py +++ b/DeeployTest/test_generic_config.py @@ -12,6 +12,9 @@ "Kernels/FP32/AveragePool/Regular_2D", "Kernels/FP32/Ceil", "Kernels/FP32/Clip", + "Kernels/FP32/Col2Im", + "Kernels/FP32/ConvTranspose/Regular_1D", + "Kernels/FP32/ConvTranspose/Regular_2D", "Kernels/FP32/Conv/DW_2D_Bias", "Kernels/FP32/Conv/DW_2D_NoBias", "Kernels/FP32/Conv/DW_2D_ZeroValuedBias", @@ -19,6 +22,7 @@ "Kernels/FP32/Conv/Regular_2D_NoBias", "Kernels/FP32/Conv/Regular_2D_ZeroValuedBias", "Kernels/FP32/Div", + "Kernels/FP32/Elu", "Kernels/FP32/Exp", "Kernels/FP32/Floor", "Kernels/FP32/GEMM/Regular", @@ -33,6 +37,7 @@ "Kernels/FP32/MaxPool/Regular_2D", "Kernels/FP32/Mul", "Kernels/FP32/LayerNorm", + "Kernels/FP32/LeakyRelu", "Kernels/FP32/RMSNorm", "Kernels/FP32/Pow/Scalar", "Kernels/FP32/Pow/Vector", @@ -55,6 +60,9 @@ "Kernels/FP32/ReduceMean/NoKeepDims/Axis2", "Kernels/FP32/ReduceMean/NoKeepDims/ReduceMean_Add", "Kernels/FP32/Reshape/SkipConnection", + "Kernels/FP32/Resize", + "Kernels/FP32/ScatterElements", + "Kernels/FP32/Selu", "Kernels/FP32/Sigmoid", "Kernels/FP32/Sqrt", "Kernels/FP32/Sub", @@ -64,6 +72,7 @@ "Kernels/Integer/Softmax/Regular", "Kernels/Integer/Add/MultIO", "Kernels/Integer/Add/Regular", + "Kernels/Integer/Col2Im", "Kernels/Integer/Conv/DW_1D", "Kernels/Integer/Conv/Regular_1D", "Kernels/Integer/Conv/DW_2D", @@ -77,6 +86,8 @@ "Kernels/Integer/Pad/Regular_2D", "Kernels/Integer/ReduceMean", "Kernels/Integer/ReduceSum", + "Kernels/Integer/Resize", + "Kernels/Integer/ScatterElements", "Kernels/Integer/Slice", "Kernels/Integer/Sub", # Special test from TinyViT model layers diff --git a/TargetLibraries/Generic/inc/DeeployBasicMath.h b/TargetLibraries/Generic/inc/DeeployBasicMath.h index 4b5a7a7b6c..36c6e4cd74 100644 --- a/TargetLibraries/Generic/inc/DeeployBasicMath.h +++ b/TargetLibraries/Generic/inc/DeeployBasicMath.h @@ -37,7 +37,7 @@ #include "kernel/Ceil.h" #include "kernel/Clip.h" #include "kernel/Col2Im.h" -#include "kernel/ConvTranspose1d_fp32.h" +#include "kernel/ConvTranspose_fp32.h" #include "kernel/Convolution.h" #include "kernel/DWConvolution.h" #include "kernel/Div.h" diff --git a/TargetLibraries/Generic/inc/kernel/ConvTranspose1d_fp32.h b/TargetLibraries/Generic/inc/kernel/ConvTranspose1d_fp32.h deleted file mode 100644 index 40ef065992..0000000000 --- a/TargetLibraries/Generic/inc/kernel/ConvTranspose1d_fp32.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef CONV_TRANSPOSE1D_FP32_H -#define CONV_TRANSPOSE1D_FP32_H - -#include -#include - -void ConvTranspose1d_fp32(const float32_t *input, uint32_t C_in, uint32_t W_in, - const float32_t *weight, uint32_t C_out, uint32_t K, - uint32_t stride, const float32_t *bias, bool has_bias, - float32_t *output, uint32_t W_out); - -#endif // CONV_TRANSPOSE1D_FP32_H diff --git a/TargetLibraries/Generic/inc/kernel/ConvTranspose_fp32.h b/TargetLibraries/Generic/inc/kernel/ConvTranspose_fp32.h new file mode 100644 index 0000000000..7ff06f171e --- /dev/null +++ b/TargetLibraries/Generic/inc/kernel/ConvTranspose_fp32.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef CONV_TRANSPOSE_FP32_H +#define CONV_TRANSPOSE_FP32_H + +#include +#include + +void ConvTranspose1d_fp32(const float32_t *input, uint32_t C_in, uint32_t W_in, + const float32_t *weight, uint32_t C_out, uint32_t K, + uint32_t stride, const float32_t *bias, bool has_bias, + float32_t *output, uint32_t W_out); + +void ConvTranspose2d_fp32(const float32_t *input, uint32_t C_in, uint32_t H_in, + uint32_t W_in, const float32_t *weight, + uint32_t C_out, uint32_t kH, uint32_t kW, + uint32_t stride_h, uint32_t stride_w, + const float32_t *bias, bool has_bias, + float32_t *output, uint32_t H_out, uint32_t W_out); + +#endif // CONV_TRANSPOSE_FP32_H diff --git a/TargetLibraries/Generic/src/ConvTranspose1d_fp32.c b/TargetLibraries/Generic/src/ConvTranspose1d_fp32.c deleted file mode 100644 index 362058734e..0000000000 --- a/TargetLibraries/Generic/src/ConvTranspose1d_fp32.c +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna -// -// SPDX-License-Identifier: Apache-2.0 - -#include "DeeployBasicMath.h" - -void ConvTranspose1d_fp32(const float32_t *input, uint32_t C_in, uint32_t W_in, - const float32_t *weight, uint32_t C_out, uint32_t K, - uint32_t stride, const float32_t *bias, bool has_bias, - float32_t *output, uint32_t W_out) { - /* - input: [C_in, W_in] - weight: [C_in, C_out, K] - output: [C_out, W_out] - bias: [C_out] optionally - - */ - - // Output initialization - for (uint32_t c = 0; c < C_out; ++c) { - for (uint32_t w = 0; w < W_out; ++w) { - output[c * W_out + w] = 0.0f; - } - } - - // For each output channel - for (uint32_t cout = 0; cout < C_out; ++cout) { - // For each input channel - for (uint32_t cin = 0; cin < C_in; ++cin) { - // For each input width - for (uint32_t w_in = 0; w_in < W_in; ++w_in) { - float32_t val = input[cin * W_in + w_in]; - // Transposed convolution: output width is calculated based on stride - for (uint32_t k = 0; k < K; ++k) { - uint32_t w_out = w_in * stride + k; - if (w_out < W_out) { - // weight indexing: weight[cin, cout, k] - float32_t wgt = weight[cin * (C_out * K) + cout * K + k]; - output[cout * W_out + w_out] += val * wgt; - } - } - } - } - if (has_bias) { - for (uint32_t w = 0; w < W_out; ++w) { - output[cout * W_out + w] += bias[cout]; - } - } - } -} diff --git a/TargetLibraries/Generic/src/ConvTranspose_fp32.c b/TargetLibraries/Generic/src/ConvTranspose_fp32.c new file mode 100644 index 0000000000..a64dcdb582 --- /dev/null +++ b/TargetLibraries/Generic/src/ConvTranspose_fp32.c @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +// +// SPDX-License-Identifier: Apache-2.0 + +#include "DeeployBasicMath.h" + +void ConvTranspose1d_fp32(const float32_t *input, uint32_t C_in, uint32_t L_in, + const float32_t *weight, uint32_t C_out, uint32_t K, + uint32_t stride, const float32_t *bias, bool has_bias, + float32_t *output, uint32_t L_out) { + /* + input: [C_in, L_in] + weight: [C_in, C_out, K] + output: [C_out, L_out] + bias: [C_out] (optional) + */ + + for (uint32_t cout = 0; cout < C_out; ++cout) { + float32_t b = has_bias ? bias[cout] : 0.0f; + float32_t *out_row = output + cout * L_out; + for (uint32_t l = 0; l < L_out; ++l) + out_row[l] = b; + } + + for (uint32_t cout = 0; cout < C_out; ++cout) { + float32_t *out_row = output + cout * L_out; + for (uint32_t cin = 0; cin < C_in; ++cin) { + const float32_t *in_row = input + cin * L_in; + const float32_t *wgt_row = weight + cin * (C_out * K) + cout * K; + for (uint32_t l_in = 0; l_in < L_in; ++l_in) { + float32_t val = in_row[l_in]; + uint32_t base = l_in * stride; + for (uint32_t k = 0; k < K; ++k) + out_row[base + k] += val * wgt_row[k]; + } + } + } +} + +void ConvTranspose2d_fp32(const float32_t *input, uint32_t C_in, uint32_t H_in, + uint32_t W_in, const float32_t *weight, + uint32_t C_out, uint32_t kH, uint32_t kW, + uint32_t stride_h, uint32_t stride_w, + const float32_t *bias, bool has_bias, + float32_t *output, uint32_t H_out, uint32_t W_out) { + /* + input: [C_in, H_in, W_in] + weight: [C_in, C_out, kH, kW] + output: [C_out, H_out, W_out] + bias: [C_out] (optional) + */ + + for (uint32_t cout = 0; cout < C_out; ++cout) { + float32_t b = has_bias ? bias[cout] : 0.0f; + float32_t *out_ch = output + cout * H_out * W_out; + for (uint32_t i = 0; i < H_out * W_out; ++i) + out_ch[i] = b; + } + + for (uint32_t cout = 0; cout < C_out; ++cout) { + float32_t *out_ch = output + cout * H_out * W_out; + for (uint32_t cin = 0; cin < C_in; ++cin) { + const float32_t *in_ch = input + cin * H_in * W_in; + const float32_t *wgt_ch = + weight + cin * (C_out * kH * kW) + cout * (kH * kW); + for (uint32_t h_in = 0; h_in < H_in; ++h_in) { + const float32_t *in_row = in_ch + h_in * W_in; + uint32_t h_base = h_in * stride_h; + for (uint32_t w_in = 0; w_in < W_in; ++w_in) { + float32_t val = in_row[w_in]; + uint32_t w_base = w_in * stride_w; + for (uint32_t kh = 0; kh < kH; ++kh) { + float32_t *out_row = out_ch + (h_base + kh) * W_out + w_base; + const float32_t *wgt_krow = wgt_ch + kh * kW; + for (uint32_t kw = 0; kw < kW; ++kw) + out_row[kw] += val * wgt_krow[kw]; + } + } + } + } + } +} diff --git a/TargetLibraries/Generic/src/GlobalAveragePool_fp32.c b/TargetLibraries/Generic/src/GlobalAveragePool_fp32.c index 907de4bb90..6df133b8fe 100644 --- a/TargetLibraries/Generic/src/GlobalAveragePool_fp32.c +++ b/TargetLibraries/Generic/src/GlobalAveragePool_fp32.c @@ -22,7 +22,7 @@ void GlobalAveragePool_fp32_fp32(float32_t const *__restrict__ src, for (uint32_t i = 0; i < spatial_size; ++i) { sum += x[i]; } - dst[n * C + c] = sum / spatial_size; + dst[n * C + c] = sum / (float32_t)spatial_size; } } } \ No newline at end of file diff --git a/TargetLibraries/Generic/src/GlobalMaxPool_fp32.c b/TargetLibraries/Generic/src/GlobalMaxPool_fp32.c index 209404494c..92164eaa72 100644 --- a/TargetLibraries/Generic/src/GlobalMaxPool_fp32.c +++ b/TargetLibraries/Generic/src/GlobalMaxPool_fp32.c @@ -15,17 +15,13 @@ void GlobalMaxPool_fp32_fp32(float32_t const *__restrict__ src, } for (uint32_t n = 0; n < N; n++) { for (uint32_t c = 0; c < C; c++) { - - float32_t sum = 0.0f; const float32_t *x = src + (n * C + c) * spatial_size; - float32_t max = x[0]; for (uint32_t i = 1; i < spatial_size; i++) { if (x[i] > max) { max = x[i]; } } - dst[n * C + c] = max; } } diff --git a/TargetLibraries/Generic/src/HardSwish_fp32.c b/TargetLibraries/Generic/src/HardSwish_fp32.c index 4776586fff..632123bc98 100644 --- a/TargetLibraries/Generic/src/HardSwish_fp32.c +++ b/TargetLibraries/Generic/src/HardSwish_fp32.c @@ -11,6 +11,6 @@ void HardSwish_fp32_fp32(float32_t *data_in, float32_t *data_out, int32_t size) { for (int i = 0; i < size; i++) { float32_t x = data_in[i]; - data_out[i] = x * fmaxf(0, fminf(1, x / 6 + 0.5)); + data_out[i] = x * fmaxf(0, fminf(1, x / 6.0f + 0.5f)); } } diff --git a/TargetLibraries/Generic/src/Layernorm_fp32.c b/TargetLibraries/Generic/src/Layernorm_fp32.c index fb68df8dfe..b0eec7d8df 100644 --- a/TargetLibraries/Generic/src/Layernorm_fp32.c +++ b/TargetLibraries/Generic/src/Layernorm_fp32.c @@ -42,7 +42,7 @@ void LayernormGrad_fp32_fp32(float32_t *grad_in, float32_t *data_in, float32_t *bias, float32_t epsilon, int32_t size, int32_t lastDimLength) { float32_t mean, variance, std, inv_std; - float32_t sum_dy, sum_dy_scaled, sum_dy_scaled_centered; + float32_t sum_dy, sum_dy_scaled; float32_t centered_input; for (int i = 0; i < (size / lastDimLength); i++) { @@ -53,26 +53,26 @@ void LayernormGrad_fp32_fp32(float32_t *grad_in, float32_t *data_in, for (int j = 0; j < lastDimLength; j++) { mean += data_in[j + i * lastDimLength]; } - mean = mean / lastDimLength; + mean = mean / (float32_t)lastDimLength; for (int j = 0; j < lastDimLength; j++) { centered_input = data_in[j + i * lastDimLength] - mean; variance += centered_input * centered_input; } - variance = variance / lastDimLength; + variance = variance / (float32_t)lastDimLength; variance += epsilon; std = sqrtf(variance); inv_std = 1.0f / std; // RW: Step 2: Compute intermediate values needed for gradient calculation sum_dy = 0.0f; - sum_dy_scaled_centered = 0.0f; + sum_dy_scaled = 0.0f; // RW: Calculate sum(dy) and sum(dy * scale * (x - mean) / std) for (int j = 0; j < lastDimLength; j++) { sum_dy += grad_in[j + i * lastDimLength]; centered_input = data_in[j + i * lastDimLength] - mean; - sum_dy_scaled_centered += + sum_dy_scaled += grad_in[j + i * lastDimLength] * scale[j] * centered_input * inv_std; } @@ -85,9 +85,10 @@ void LayernormGrad_fp32_fp32(float32_t *grad_in, float32_t *data_in, // (x-mean)/(N*std^2)*sum(dy*scale*(x-mean)/std)) grad_out[j + i * lastDimLength] = inv_std * scale[j] * - (grad_in[j + i * lastDimLength] - (sum_dy / lastDimLength) - - (centered_input * inv_std * inv_std / lastDimLength) * - sum_dy_scaled_centered); + (grad_in[j + i * lastDimLength] - + (sum_dy / (float32_t)lastDimLength) - + (centered_input * inv_std * inv_std / (float32_t)lastDimLength) * + sum_dy_scaled); } } } From 4f143ede9b894d28c5fe3f0409f34017e164c0d0 Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Tue, 23 Jun 2026 07:51:43 +0000 Subject: [PATCH 09/12] update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd1b4c8b8e..7567a9e0e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Fix for python error when using python 3.12.11 [#189]( https://github.com/pulp-platform/Deeploy/pull/189) - Add support for Operators for Generic target needed in MAGIA [#193]( https://github.com/pulp-platform/Deeploy/pull/193) - Fix GAP9 L3 Board Tests: readfs Flash Ordering and Duplicate Input Data [#196](https://github.com/pulp-platform/Deeploy/pull/196) +- Add support for Operators for Generic target needed in MAGIA (again) [#195]( https://github.com/pulp-platform/Deeploy/pull/195) ### Added - XDNA2 (AIE2p) platform beta: first MLIR backend for Deeploy, targeting AMD/Xilinx NPU2 with a single BF16 Add kernel @@ -36,6 +37,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Added GAP9 Platform Support: Deployer, Bindings, Templates, Tiler, DMA (L3Dma/MchanDma), target library, CI workflows - Per-layer microbenchmarking on PULPOpen via `--profileMicrobenchmark`: new `PULPMicrobenchmark` code-transformation pass + `perf_utils.h` helpers report cycles, instructions, stalls and cache misses per layer in `RunNetwork` - Add support for the Generic target for the following operators [Ceil](https://onnx.ai/onnx/operators/onnx__Ceil.html), [Floor](https://onnx.ai/onnx/operators/onnx__Floor.html), [Clip](https://onnx.ai/onnx/operators/onnx__Clip.html), [Sub](https://onnx.ai/onnx/operators/onnx__Sub.html), [Exp](https://onnx.ai/onnx/operators/onnx__Exp.html), [Sigmoid](https://onnx.ai/onnx/operators/onnx__Sigmoid.html), [Swish](https://onnx.ai/onnx/operators/onnx__Swish.html), [HardSigmoid](https://onnx.ai/onnx/operators/onnx__HardSigmoid.html), [HardSwish](https://onnx.ai/onnx/operators/onnx__HardSwish.html), [InstanceNormalization](https://onnx.ai/onnx/operators/onnx__InstanceNormalization.html), [GroupNormalization](https://onnx.ai/onnx/operators/onnx__GroupNormalization.html), [AveragePool](https://onnx.ai/onnx/operators/onnx__AveragePool.html), [GlobalAveragePool](https://onnx.ai/onnx/operators/onnx__GlobalAveragePool.html), [GlobalMaxPool](https://onnx.ai/onnx/operators/onnx__GlobalMaxPool.html). +- Add support for the Generic target for the following operators: [Elu](https://onnx.ai/onnx/operators/onnx__Elu.html), [LeakyRelu](https://onnx.ai/onnx/operators/onnx__LeakyRelu.html), [Selu](https://onnx.ai/onnx/operators/onnx__Selu.html), [Scatter](https://onnx.ai/onnx/operators/onnx__Scatter.html), [ScatterElements](https://onnx.ai/onnx/operators/onnx__ScatterElements.html), [Col2Im](https://onnx.ai/onnx/operators/onnx__Col2Im.html), [Resize](https://onnx.ai/onnx/operators/onnx__Resize.html) ### Changed - `aie.dialects` API: move `link_with` from `aie_d.core()` to `aie_d.external_func()` (mlir-aie v1.3.2) @@ -54,6 +56,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Aligned CLI commands across the project - Added @runwangdl as a code owner - Skip emitting duplicate `testInputVector` data for inputs placed in L3 (loaded at runtime from the readfs hex instead), reducing test binary size +- Allowing ONNX Operators with empty inputs. ### Fixed - Add missing `shell: bash` directive to CI cache generation steps to ensure correct shell execution @@ -67,6 +70,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Reduce RunNetwork stack usage by scoping per-layer variables with braces and moving tileIdxPtr allocation into per-layer execution blocks - Fix invalid escape sequence python error in DeeployTypes.py: appearing when using pytest to launch regressions - Fix GAP9 board tests with `--defaultMemLevel L3` reading garbage inputs: place all gapy `--flash-property` options before the positional subcommand and use `image flash run` so the readfs partition (input hex files) is flashed to the device +- Fix `ConvTranspose` layer: output buffer shape computation. ### Removed - `testDMA.py` was an old test; we now have `test_dmas.py` instead. From 11a95c29f4a49d4f1817a2791cbf46ddc40058af Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Thu, 2 Jul 2026 08:55:53 +0000 Subject: [PATCH 10/12] fix ConvTranspose.computeShapes --- Deeploy/Targets/Generic/Layers.py | 45 +++++++++++++------------------ 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index bb91244447..1f78da6554 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -657,38 +657,29 @@ def computeOps(self): class ConvTransposeLayer(ONNXLayer): - def __init__(self, maps: List[NodeMapper]): - super().__init__(maps) - def computeShapes(self, inputShapes: Shape, outputShapes: Shape, operatorRepresentation, channels_first) -> Tuple[Shape, Shape]: - newInputShapes = list(inputShapes) - - input_shape = inputShapes[0] # [N, C_in, d0, ...] - weight_shape = inputShapes[1] # [C_in, C_out//group, k0, ...] + """ + Infers output shapes for ConvTranspose using only static info. + - inputShapes[0]: input tensor shape (e.g., [N, C_in, W] for 1D, [N, C_in, H, W] for 2D) + - inputShapes[1]: weight tensor shape (e.g., [C_in, C_out // group, kW] for 1D) + - outputShapes[0]: output tensor shape (to be updated) + """ + assert channels_first, "ConvTransposeLayer only supports channels_first (NCHW) layout: " \ + "there is no NHWC lowering pass for ConvTranspose, and ConvTransposeParser/computeOps assume NCHW indexing." + + input_shapes = list(inputShapes) + output_shapes = list(outputShapes) group = operatorRepresentation.get('group', 1) + weight_shape = inputShapes[1] - batch = input_shape[0] - spatial_in = list(input_shape[2:]) if channels_first else list(input_shape[1:-1]) - ndim = len(spatial_in) - - kernel_shape = list(weight_shape[2:]) - C_out = weight_shape[1] * group - - strides = operatorRepresentation.get('strides') or [1] * ndim - dilations = operatorRepresentation.get('dilations') or [1] * ndim - output_padding = operatorRepresentation.get('output_padding') or [0] * ndim - pads = operatorRepresentation.get('pads') or [0] * (2 * ndim) - - spatial_out = [(spatial_in[d] - 1) * strides[d] - pads[d] - pads[d + ndim] + dilations[d] * - (kernel_shape[d] - 1) + output_padding[d] + 1 for d in range(ndim)] - - if channels_first: - output_shape = [batch, C_out] + spatial_out - else: - output_shape = [batch] + spatial_out + [C_out] + if output_shapes and len(output_shapes[0]) >= 2: + # For 1D: weight_shape = [C_in, C_out // group, kW] + # For 2D: weight_shape = [C_in, C_out // group, kH, kW] + ch_out = weight_shape[1] * group + output_shapes[0][1] = ch_out - return newInputShapes, [output_shape] + return input_shapes, output_shapes def computeOps(self): rep = self.mapper.parser.operatorRepresentation From 72e708effdcd8732c91a2d95a1e465712e92eccb Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Thu, 2 Jul 2026 13:07:57 +0000 Subject: [PATCH 11/12] minor fixes addressing coderabbit review --- .../TypeCheckers/SignPropTypeChecker.py | 3 ++ Deeploy/DeeployTypes.py | 8 +++- Deeploy/Targets/Generic/Bindings.py | 12 ++--- Deeploy/Targets/Generic/Layers.py | 2 +- Deeploy/Targets/Generic/Parsers.py | 47 ++++++++++++++----- .../Generic/Templates/FloatCeilTemplate.py | 18 +------ .../Generic/Templates/FloatClipTemplate.py | 17 +------ .../Generic/Templates/FloatEluTemplate.py | 18 +------ .../Generic/Templates/FloatExpTemplate.py | 18 +------ .../Generic/Templates/FloatFloorTemplate.py | 18 +------ .../Templates/FloatHardSigmoidTemplate.py | 18 +------ .../Templates/FloatHardSwishTemplate.py | 18 +------ .../Templates/FloatLeakyReluTemplate.py | 18 +------ .../Generic/Templates/FloatSeluTemplate.py | 18 +------ .../Generic/Templates/FloatSigmoidTemplate.py | 18 +------ .../Generic/Templates/FloatSqrtTemplate.py | 24 +++------- .../Generic/Templates/FloatSwishTemplate.py | 18 +------ .../Generic/Templates/FloatUnaryTemplate.py | 17 +++++++ Deeploy/Targets/Generic/TypeCheckers.py | 4 +- TargetLibraries/Generic/src/Layernorm_fp32.c | 25 ++++------ 20 files changed, 110 insertions(+), 229 deletions(-) create mode 100644 Deeploy/Targets/Generic/Templates/FloatUnaryTemplate.py diff --git a/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py b/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py index 46a4896c12..fa84927cfc 100644 --- a/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py +++ b/Deeploy/CommonExtensions/TypeCheckers/SignPropTypeChecker.py @@ -25,6 +25,9 @@ def typeInferGlobalCtxt(self, ctxt: NetworkContext, node: gs.Node) -> NetworkCon ctxt = super().typeInferGlobalCtxt(ctxt, node) for inputNode, _type in zip(node.inputs, self.input_types): + if not inputNode.name: + continue + if isinstance(ctxt.lookup(inputNode.name), ConstantBuffer): reference = ctxt.lookup(inputNode.name) if not _type.referencedType.checkPromotion(reference.values): diff --git a/Deeploy/DeeployTypes.py b/Deeploy/DeeployTypes.py index ea9aaff67f..bd0e712dd6 100644 --- a/Deeploy/DeeployTypes.py +++ b/Deeploy/DeeployTypes.py @@ -1310,6 +1310,9 @@ def typeCheckNodeInputs(self, ctxt: NetworkContext, node: gs.Node) -> bool: retCheck = True for inputNode, _type in zip(node.inputs, self.input_types): + if not inputNode.name: + continue + reference = ctxt.lookup(inputNode.name) if not isinstance(reference, VariableBuffer): @@ -1330,6 +1333,9 @@ def typeCheckNodeInputs(self, ctxt: NetworkContext, node: gs.Node) -> bool: def typeInferGlobalCtxt(self, ctxt: NetworkContext, node: gs.Node) -> NetworkContext: for inputNode, _type in zip(node.inputs, self.input_types): + if not inputNode.name: + continue + if isinstance(ctxt.lookup(inputNode.name), ConstantBuffer): reference = ctxt.lookup(inputNode.name) if not _type.referencedType.checkPromotion(reference.values): @@ -1920,7 +1926,7 @@ def broadcast(self, ctxt: NetworkContext, default_channels_first: bool = True) - newInputShapes, newOutputShapes = self.computeShapes(inputShapes, outputShapes, self.mapper.parser.operatorRepresentation, channels_first) - for node, newShape in zip(validInputNodes + self.node.outputs, newInputShapes + newOutputShapes): + for node, newShape in zip(validInputNodes + self.node.outputs, newInputShapes + newOutputShapes, strict = True): if ctxt.is_local(node.name): ctxt.localObjects[node.name].shape = newShape # Update shape of tensors in onnx graph diff --git a/Deeploy/Targets/Generic/Bindings.py b/Deeploy/Targets/Generic/Bindings.py index f5483bf669..b2f76e51b8 100644 --- a/Deeploy/Targets/Generic/Bindings.py +++ b/Deeploy/Targets/Generic/Bindings.py @@ -330,17 +330,17 @@ BasicConvTranspose1DBindings = [ NodeBinding( ConvChecker( - [PointerClass(type), PointerClass(type), PointerClass(type)], # input, weight, bias - [PointerClass(type)]), + [PointerClass(dtype), PointerClass(dtype), PointerClass(dtype)], # input, weight, bias + [PointerClass(dtype)]), ConvTransposeTemplate.referenceTemplate1D, - BasicTransformer) for type in FloatDataTypes + BasicTransformer) for dtype in FloatDataTypes ] + [ NodeBinding( ConvChecker( - [PointerClass(type), PointerClass(type)], # input, weight - [PointerClass(type)]), + [PointerClass(dtype), PointerClass(dtype)], # input, weight + [PointerClass(dtype)]), ConvTransposeTemplate.referenceTemplate1D, - BasicTransformer) for type in FloatDataTypes + BasicTransformer) for dtype in FloatDataTypes ] BasicConvTranspose2DBindings = [ diff --git a/Deeploy/Targets/Generic/Layers.py b/Deeploy/Targets/Generic/Layers.py index 1f78da6554..a3a3f88458 100644 --- a/Deeploy/Targets/Generic/Layers.py +++ b/Deeploy/Targets/Generic/Layers.py @@ -814,7 +814,7 @@ def computeOps(self): # Col2Im iterates over every element of the input tensor and adds it # into the corresponding output position. The total number of # accumulations is exactly the number of input elements which is - # N × C × block_volume × L + # N x C x block_volume x L rep = self.mapper.parser.operatorRepresentation block_volume = int(np.prod(rep['block_shape'])) L = int(np.prod(rep['col_dims'])) diff --git a/Deeploy/Targets/Generic/Parsers.py b/Deeploy/Targets/Generic/Parsers.py index f4b8d5bae4..cf107079e0 100644 --- a/Deeploy/Targets/Generic/Parsers.py +++ b/Deeploy/Targets/Generic/Parsers.py @@ -2804,9 +2804,17 @@ def parseNodeCtxt(self, ctxt: NetworkContext, node: gs.Node, channels_first: boo kernel_shape = list(weight.shape[2:]) feature_maps = weight.shape[1] * rep['group'] # input channels + if not all([ + channels % rep['group'] == 0, + weight.shape[0] == channels, + data_out.shape[1] == feature_maps, + ]): + return ctxt, False # optional inputs - if len(node.inputs) == 3: + rep['bias'] = 'NULL' + has_bias = len(node.inputs) == 3 and bool(node.inputs[2].name) + if has_bias: bias: ConstantBuffer = ctxt.lookup(node.inputs[2].name) if not (len(bias.shape) == 1 and bias.shape[0] == feature_maps): return ctxt, False @@ -2864,7 +2872,7 @@ def parseNodeCtxt(self, ctxt: NetworkContext, node: gs.Node, channels_first: boo rep['data_in'] = data_in.name rep['weight'] = weight.name rep['data_out'] = data_out.name - rep['has_bias'] = int('bias' in rep) + rep['has_bias'] = int(has_bias) rep['kernel_shape'] = kernel_shape rep['output_shape'] = output_shape @@ -3248,12 +3256,24 @@ def parseNodeCtxt(self, indices = ctxt.lookup(node.inputs[1].name) updates = ctxt.lookup(node.inputs[2].name) data_out = ctxt.lookup(node.outputs[0].name) + + ndim = len(data_in.shape) + axis = int(self.operatorRepresentation['axis']) + if axis < 0: + axis += ndim + if not all([ + 0 <= axis < ndim, + list(data_out.shape) == list(data_in.shape), + list(updates.shape) == list(indices.shape), + ]): + return ctxt, False + self.operatorRepresentation['data_in'] = data_in.name self.operatorRepresentation['indices'] = indices.name self.operatorRepresentation['updates'] = updates.name self.operatorRepresentation['data_out'] = data_out.name - self.operatorRepresentation['ndim'] = len(data_in.shape) + self.operatorRepresentation['ndim'] = ndim self.operatorRepresentation['data_shape'] = list(data_in.shape) self.operatorRepresentation['indices_shape'] = list(indices.shape) @@ -3321,9 +3341,12 @@ def parseNodeCtxt(self, data_in: VariableBuffer = ctxt.lookup(node.inputs[0].name) data_out: VariableBuffer = ctxt.lookup(node.outputs[0].name) - image_shape = self.operatorRepresentation['image_shape'] - block_shape = self.operatorRepresentation['block_shape'] - col_dims = self.operatorRepresentation['col_dims'] + image_shape: list = self.operatorRepresentation['image_shape'] + block_shape: list = self.operatorRepresentation['block_shape'] + col_dims: list = self.operatorRepresentation['col_dims'] + + if len(data_in.shape) != 3 or len(data_out.shape) != 2 + len(image_shape): + return ctxt, False N, C = data_out.shape[0], data_out.shape[1] block_volume = int(np.prod(block_shape)) @@ -3346,13 +3369,13 @@ def parseNodeCtxt(self, class ResizeParser(NodeParser): @staticmethod - def _is_empty(input: gs.Variable | gs.Constant | None) -> bool: - if input is None: + def _is_empty(tensor: gs.Variable | gs.Constant | None) -> bool: + if tensor is None: return True - if isinstance(input, gs.Constant): - return input.values.size <= 0 - if isinstance(input, gs.Variable): - return input.shape is None + if isinstance(tensor, gs.Constant): + return tensor.values.size <= 0 + if isinstance(tensor, gs.Variable): + return tensor.shape is None return True def parseNode(self, node: gs.Node) -> bool: diff --git a/Deeploy/Targets/Generic/Templates/FloatCeilTemplate.py b/Deeploy/Targets/Generic/Templates/FloatCeilTemplate.py index a7e4d7217b..91c746951c 100644 --- a/Deeploy/Targets/Generic/Templates/FloatCeilTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatCeilTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _CeilTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _CeilTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Ceil (Name: ${nodeName}, Op: ${nodeOp}) Ceil_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatClipTemplate.py b/Deeploy/Targets/Generic/Templates/FloatClipTemplate.py index cdac4e6fe8..ffe1408c11 100644 --- a/Deeploy/Targets/Generic/Templates/FloatClipTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatClipTemplate.py @@ -1,22 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _ClipTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _ClipTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Clip (Name: ${nodeName}, Op: ${nodeOp}) Clip_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${min_val}, ${max_val}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py b/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py index fc7a1886ae..7eb26f79a3 100644 --- a/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatEluTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _EluTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _EluTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // ELU (Name: ${nodeName}, Op: ${nodeOp}) Elu_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}, ${alpha}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatExpTemplate.py b/Deeploy/Targets/Generic/Templates/FloatExpTemplate.py index b6de9846dc..1d2249ead6 100644 --- a/Deeploy/Targets/Generic/Templates/FloatExpTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatExpTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _ExpTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _ExpTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Exp (Name: ${nodeName}, Op: ${nodeOp}) Exp_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatFloorTemplate.py b/Deeploy/Targets/Generic/Templates/FloatFloorTemplate.py index 9809ea896a..ad39bcc7c2 100644 --- a/Deeploy/Targets/Generic/Templates/FloatFloorTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatFloorTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _FloorTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _FloorTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Floor (Name: ${nodeName}, Op: ${nodeOp}) Floor_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatHardSigmoidTemplate.py b/Deeploy/Targets/Generic/Templates/FloatHardSigmoidTemplate.py index 4130fa32b5..0e3e09841d 100644 --- a/Deeploy/Targets/Generic/Templates/FloatHardSigmoidTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatHardSigmoidTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _hardSigmoidTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _hardSigmoidTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // HardSigmoid (Name: ${nodeName}, Op: ${nodeOp}) HardSigmoid_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${alpha}, ${beta}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatHardSwishTemplate.py b/Deeploy/Targets/Generic/Templates/FloatHardSwishTemplate.py index bfaf40f44e..2d02e8e98e 100644 --- a/Deeploy/Targets/Generic/Templates/FloatHardSwishTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatHardSwishTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _hardSwishTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _hardSwishTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // HardSwish (Name: ${nodeName}, Op: ${nodeOp}) HardSwish_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py b/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py index 35804bd3d7..cdee2d4950 100644 --- a/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatLeakyReluTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _LeakyReluTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _LeakyReluTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // LeakyRelu (Name: ${nodeName}, Op: ${nodeOp}) LeakyRelu_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}, ${alpha}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py b/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py index 2585a1966d..3ba11b76eb 100644 --- a/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatSeluTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _SeluTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _SeluTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // SELU (Name: ${nodeName}, Op: ${nodeOp}) Selu_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}, ${alpha}, ${gamma}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatSigmoidTemplate.py b/Deeploy/Targets/Generic/Templates/FloatSigmoidTemplate.py index 7d3ab9b39e..bbb0725cc1 100644 --- a/Deeploy/Targets/Generic/Templates/FloatSigmoidTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatSigmoidTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _SigmoidTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _SigmoidTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Sigmoid (Name: ${nodeName}, Op: ${nodeOp}) Sigmoid_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py b/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py index 99d7ba0475..85dc2b1c6b 100644 --- a/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py @@ -3,30 +3,20 @@ # SPDX-License-Identifier: Apache-2.0 from typing import Dict, List, Tuple -import numpy as np +from Deeploy.DeeployTypes import NetworkContext, OperatorRepresentation +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - -class _SqrtTemplate(NodeTemplate): +class _SqrtTemplate(_FloatUnaryTemplate): def alignToContext(self, ctxt: NetworkContext, operatorRepresentation: OperatorRepresentation) -> Tuple[NetworkContext, Dict, List[str]]: - # Get input and output tensors - data_in = ctxt.lookup(operatorRepresentation['data_in']) - data_out = ctxt.lookup(operatorRepresentation['data_out']) - - # Get data type (fp32) - data_type = data_in._type.typeName - operatorRepresentation['data_type'] = data_type + ctxt, operatorRepresentation, deps = super().alignToContext(ctxt, operatorRepresentation) - type_width = data_in._type.referencedType.typeWidth - operatorRepresentation['type_width'] = type_width - - # Calculate size - operatorRepresentation['size'] = int(np.prod(data_in.shape)) + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['data_type'] = data_in._type.typeName - return ctxt, operatorRepresentation, [] + return ctxt, operatorRepresentation, deps referenceTemplate = _SqrtTemplate(""" diff --git a/Deeploy/Targets/Generic/Templates/FloatSwishTemplate.py b/Deeploy/Targets/Generic/Templates/FloatSwishTemplate.py index 33932d157f..a0a774b5ad 100644 --- a/Deeploy/Targets/Generic/Templates/FloatSwishTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatSwishTemplate.py @@ -1,23 +1,9 @@ # SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -import numpy as np +from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate -from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation - - -class _SigmoidTemplate(NodeTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['size'] = int(np.prod(data_in.shape)) - operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth - return ctxt, operatorRepresentation, [] - - -referenceTemplate = _SigmoidTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Swish (Name: ${nodeName}, Op: ${nodeOp}) Swish_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${alpha}, ${size}); """) diff --git a/Deeploy/Targets/Generic/Templates/FloatUnaryTemplate.py b/Deeploy/Targets/Generic/Templates/FloatUnaryTemplate.py new file mode 100644 index 0000000000..06868e4186 --- /dev/null +++ b/Deeploy/Targets/Generic/Templates/FloatUnaryTemplate.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 +import numpy as np + +from Deeploy.DeeployTypes import NetworkContext, NodeTemplate, OperatorRepresentation + + +class _FloatUnaryTemplate(NodeTemplate): + + def alignToContext(self, ctxt: NetworkContext, + operatorRepresentation: OperatorRepresentation) -> tuple[NetworkContext, dict, list[str]]: + + data_in = ctxt.lookup(operatorRepresentation['data_in']) + operatorRepresentation['size'] = int(np.prod(data_in.shape)) + operatorRepresentation['type_width'] = data_in._type.referencedType.typeWidth + return ctxt, operatorRepresentation, [] diff --git a/Deeploy/Targets/Generic/TypeCheckers.py b/Deeploy/Targets/Generic/TypeCheckers.py index 4224cba538..a6418b80ca 100644 --- a/Deeploy/Targets/Generic/TypeCheckers.py +++ b/Deeploy/Targets/Generic/TypeCheckers.py @@ -55,8 +55,8 @@ def _inferNumLevels(self, inputs: List[VariableBuffer], def _inferSignedness(self, inputs: List[VariableBuffer], operatorRepresentation: OperatorRepresentation) -> Optional[List[bool]]: - assert (all([_inp._signed == True for _inp in inputs]) or all( - [[_inp._signed == False for _inp in inputs]])), "Some inputs in concat operation have different signs!" + assert (all([inp._signed for inp in inputs]) + or all([[not inp._signed for inp in inputs]])), "Some inputs in concat operation have different signs!" if inputs[0]._signed: return [True] diff --git a/TargetLibraries/Generic/src/Layernorm_fp32.c b/TargetLibraries/Generic/src/Layernorm_fp32.c index b0eec7d8df..90e73ed03f 100644 --- a/TargetLibraries/Generic/src/Layernorm_fp32.c +++ b/TargetLibraries/Generic/src/Layernorm_fp32.c @@ -42,7 +42,7 @@ void LayernormGrad_fp32_fp32(float32_t *grad_in, float32_t *data_in, float32_t *bias, float32_t epsilon, int32_t size, int32_t lastDimLength) { float32_t mean, variance, std, inv_std; - float32_t sum_dy, sum_dy_scaled; + float32_t sum_dy_scaled, sum_dy_scaled_centered; float32_t centered_input; for (int i = 0; i < (size / lastDimLength); i++) { @@ -65,30 +65,25 @@ void LayernormGrad_fp32_fp32(float32_t *grad_in, float32_t *data_in, inv_std = 1.0f / std; // RW: Step 2: Compute intermediate values needed for gradient calculation - sum_dy = 0.0f; sum_dy_scaled = 0.0f; + sum_dy_scaled_centered = 0.0f; - // RW: Calculate sum(dy) and sum(dy * scale * (x - mean) / std) + // RW: Calculate sum(dy * scale) and sum(dy * scale * (x - mean) / std) for (int j = 0; j < lastDimLength; j++) { - sum_dy += grad_in[j + i * lastDimLength]; + float32_t dy_scaled = grad_in[j + i * lastDimLength] * scale[j]; centered_input = data_in[j + i * lastDimLength] - mean; - sum_dy_scaled += - grad_in[j + i * lastDimLength] * scale[j] * centered_input * inv_std; + sum_dy_scaled += dy_scaled; + sum_dy_scaled_centered += dy_scaled * centered_input * inv_std; } // RW: Step 3: Calculate gradients for each element for (int j = 0; j < lastDimLength; j++) { centered_input = data_in[j + i * lastDimLength] - mean; - - // Gradient formula: - // dx = (1/std) * scale * (dy - (1/N)*sum(dy) - - // (x-mean)/(N*std^2)*sum(dy*scale*(x-mean)/std)) grad_out[j + i * lastDimLength] = - inv_std * scale[j] * - (grad_in[j + i * lastDimLength] - - (sum_dy / (float32_t)lastDimLength) - - (centered_input * inv_std * inv_std / (float32_t)lastDimLength) * - sum_dy_scaled); + inv_std * ((grad_in[j + i * lastDimLength] * scale[j]) - + (sum_dy_scaled / (float32_t)lastDimLength) - + (centered_input * inv_std / (float32_t)lastDimLength) * + sum_dy_scaled_centered); } } } From d9b5d14c077019478d273bf991249b92b0764976 Mon Sep 17 00:00:00 2001 From: Alex Marchioni Date: Thu, 2 Jul 2026 13:32:36 +0000 Subject: [PATCH 12/12] minor refactor --- .../Generic/Templates/FloatSqrtTemplate.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py b/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py index 85dc2b1c6b..d9384a4c34 100644 --- a/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py +++ b/Deeploy/Targets/Generic/Templates/FloatSqrtTemplate.py @@ -1,25 +1,9 @@ # SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna # # SPDX-License-Identifier: Apache-2.0 -from typing import Dict, List, Tuple - -from Deeploy.DeeployTypes import NetworkContext, OperatorRepresentation from Deeploy.Targets.Generic.Templates.FloatUnaryTemplate import _FloatUnaryTemplate - -class _SqrtTemplate(_FloatUnaryTemplate): - - def alignToContext(self, ctxt: NetworkContext, - operatorRepresentation: OperatorRepresentation) -> Tuple[NetworkContext, Dict, List[str]]: - ctxt, operatorRepresentation, deps = super().alignToContext(ctxt, operatorRepresentation) - - data_in = ctxt.lookup(operatorRepresentation['data_in']) - operatorRepresentation['data_type'] = data_in._type.typeName - - return ctxt, operatorRepresentation, deps - - -referenceTemplate = _SqrtTemplate(""" +referenceTemplate = _FloatUnaryTemplate(""" // Sqrt (Name: ${nodeName}, Op: ${nodeOp}) Sqrt_fp${type_width}_fp${type_width}(${data_in}, ${data_out}, ${size}); """)