From 1e55ba483634fdbb8f2887236424d041a738e013 Mon Sep 17 00:00:00 2001 From: viv-eth Date: Fri, 9 May 2025 15:08:50 +0200 Subject: [PATCH 01/15] [DeeployTest] Change order of typeMatching entries --- DeeployTest/testUtils/typeMapping.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/DeeployTest/testUtils/typeMapping.py b/DeeployTest/testUtils/typeMapping.py index 4cb18cb8f4..840fe7b859 100644 --- a/DeeployTest/testUtils/typeMapping.py +++ b/DeeployTest/testUtils/typeMapping.py @@ -84,7 +84,13 @@ def inferInputType(_input: np.ndarray, if _type.checkPromotion(_input - signPropOffset): matchingTypes.append(offsetType(PointerClass(_type), signPropOffset)) elif isInteger(_input): - for _type in sorted(IntegerDataTypes, key = lambda x: x.typeWidth): + sorted_types = sorted( + IntegerDataTypes, + key = lambda t: (t.typeWidth, t.typeMin < 0), + ) + + matchingTypes = [] + for _type in sorted_types: if _type.checkPromotion(_input): matchingTypes.append(offsetType(PointerClass(_type), 0)) else: From a01d5d0719c15dab639ae6be9065b69b7e99351f Mon Sep 17 00:00:00 2001 From: viv-eth Date: Sat, 7 Jun 2025 04:23:50 +0200 Subject: [PATCH 02/15] [Tests] Add negative values to correctly match type --- DeeployTest/Tests/Adder/inputs.npz | Bin 2494 -> 1530 bytes DeeployTest/Tests/Adder/network.onnx | 15 +++++---------- DeeployTest/Tests/Adder/outputs.npz | Bin 1258 -> 756 bytes 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/DeeployTest/Tests/Adder/inputs.npz b/DeeployTest/Tests/Adder/inputs.npz index e74c7e5ad2d9830973afd3947b3311c705e5d2d6..6b104c7770b2072f5db0e42578463283b5072ac3 100644 GIT binary patch literal 1530 zcmd6nPe_w-7{{MYjfkKfcJR>Q4TKp;OsI=^m@*=8$qohvfl8Z1$Q)C`YQc~Q5s_gY zI)q4sNO%Y_84)tyI&=_`5D^I(;z1%}hY*pFKHt3sDRk-7s~^7K=lT78pFLZ=t*Sa` z-gCj0R|cyG{bu5v1+71o8ft24NhG?$ms08bJ_}TaG+k5lJZ-;mJ$}bq`LYuiKOyZd-?{Frbu!lpe zJ1;QyT;L<{6~B(^9tGxZ;1|i2@p`hvb;R2LBYq!>_!1f>{~BFm|I_#xylT>kR_=^$ zGPZ!~yxapI512+21WP;Bd%FDd+DwgF`LB~@lRU4^H%Yr_#F8?_I`y| zJ+8r_%NlZeZ)5EQ@ptk&p!c33jziRqA8Xq7iNdtHQ5K@1`k zChJlVaPY(A49%gURP;W;7~^>uBd3C$MfHJHqbl$wYxH5384I8)^0ezUql!O;A3;~h zZ$KWeCL6~eq52Z(F6-!HP?O2OZS@-A2-V(dxLN!G-rk!vP>)cwHge*i{uSOks)l272D2L|Rw}jTi z5OlZ1+kF0D)!%+=sot+A=aV_{%X@P1@t*&^l#==0`2Sp7`3A(>YEEg2pT7s_-5Nzd E0VDNeApigX literal 2494 zcmd6pOHUI~6vs~iNp!`cOBbUtDVnAM6-BIi#fXmzRs|J&G>w#WVSu#6#Hh*n4ldpJ zQRWNyRb0DtruX;1aY)>_a)2Dpx%a&O=P}dmOwZIUK4)y~-+m2e%*wx6t6OiOwSV}r zW^)rsd8<4rpRXHR>)q{|eXviB-NE?XsIihY+I!25X4cp{7#)rJ`@MtF?x2*f_1}*N zg?v2h9}WtBezBP?#ozPI>`V3!fep&KUD9zy3M^_@t=3*=XkuOoj3AF6U&hJPDr z{yb896KV4Il6%Na_mPL!=q+;Z6?_K0f?RQshvK#;+`ANME_)A zwtCO;m*dCpRnG%?%WuG6*o^m_d|YwW(I;P9k(^U6W?%jrhf9Syzm+Yy2jvuplFUbG($opNW;=KtEPRd($Q=cgA8{EGi zsXcSWY22zT`h_j7Qw72=WIi^PMz>0!QXgLCG}T9Nk- za`iylU@AgvadeZekI;C57gC}YbPEjpH$b&)SZ6G)i=mf z->Gl4C*;?77O*4x!jXK^p4S}m9!U8yzpgwrfAoDW9R9Ljg{Lv6j=r-ObIq&rYZEW$ zdasyplRkJ0S5aesyZU4)+DrQC8`U$zpZseN(+|e}kbc^Gvd_E*^j&$`-6EgONOQe^)aSxYae{;1Yo@)Wxl=z2pH|hr=1lmc zc>(u?i|i0bvH!cB+EjhIZt*$(1% LGc0I0S#rMtht3rN diff --git a/DeeployTest/Tests/Adder/network.onnx b/DeeployTest/Tests/Adder/network.onnx index 851801aaa5..6c836a0ff3 100644 --- a/DeeployTest/Tests/Adder/network.onnx +++ b/DeeployTest/Tests/Adder/network.onnx @@ -1,7 +1,8 @@ -pytorch1.11.0:Å -) +pytorch2.7.1:¡ +( onnx::Add_0 - onnx::Add_12Add_0"Addtorch-jit-exportZ% + onnx::Add_12/Add"Add +main_graphZ% onnx::Add_0   @@ -19,10 +20,4 @@    -j -2 - - - - -B \ No newline at end of file +B \ No newline at end of file diff --git a/DeeployTest/Tests/Adder/outputs.npz b/DeeployTest/Tests/Adder/outputs.npz index 9a8d6680e38c6a38e1c31fdd30d71f11e2f9d0b5..f73b28d5511ef96e65451e8eb84f27a25390b8be 100644 GIT binary patch literal 756 zcmbV~y)Q#y6voeOweiuRkq#mfSCCuMP+tS_EhIGL4h%v2Lsn$XibNB$ajVU=+UJ_BfN9Mw$CL5!($sQ15>E-lBnUs@MSzhN>CON=?P~^w=v0r$ z23p`9km_Oi?E8oE%B_D;j_jj|g)Jb%5uKkXWeQr{9S6Xe+aZj%PT>KrkMDk}<+S1!tpPc_J zWbL|=SLK;JE?*w(R7ZvUkYDAno}Em~C$8KYJuW-09Gy+SPy3VJ*>qUv`gZ?xRxj$a zasRwt@CT1NF25gk+;4ZYap&5E?|H>{it)MPe5lxr6w@B{KKz4XdvwD5eud zf66?1?koHo>pm%l0B0Wu_hahEijDaIBj<4V@c5OA_3RT_KSwvBXZtMcUZ0D0&K>1` z7c`&n_=@^_MKS#Wb$q$c$|Lu*yqCLLPe&kmEPZj0@rBL@@{za7J$i}r2JR~!=)C3V z$RWOApMj#Fo6rlE8@hro&%EXC$xXIYC%#q;oHHGwe_t_K?%T@e1H}NE?wsd^e3IpB zeFV4jC7$>A4(t=iTeyd0die5iZ~Z}j5r3W>Jv#1-`i)(DTOW|G^^e`R)@#jWi<>My}g=Qv3PAR IuxVxX4-;*xfdBvi From 0d4c9b17f44ed7ed60e00ce1aa0c639ae423eeac Mon Sep 17 00:00:00 2001 From: viv-eth Date: Wed, 9 Jul 2025 08:18:11 +0200 Subject: [PATCH 03/15] [ci] Add tests for manual type inference --- .gitlab-ci.yml | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94318a4e9d..1a41fa5175 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -599,6 +599,59 @@ run_generic_test_models: # This job runs in the test stage. paths: - ./DeeployTest/TEST_*/build +test_generate_network_type_inference_fail_input0: + stage: test + parallel: + matrix: + - PLATFORM: ['Generic'] + TEST: ['testTypeInferenceDifferentTypes'] + MAP: [ + 'A=int8_t B=int8_t C=int8_t', + ] + script: + - !reference [.setup_test, script] + - python generateNetwork.py \ + -p $PLATFORM \ + -t ./Tests/$TEST \ + -v \ + --input-type-map $MAP \ + --shouldFail + +test_generate_network_type_inference_fail_input2: + stage: test + parallel: + matrix: + - PLATFORM: ['Generic'] + TEST: ['testTypeInferenceDifferentTypes'] + MAP: [ + 'A=int16_t B=int8_t C=int16_t', + ] + script: + - !reference [.setup_test, script] + - python generateNetwork.py \ + -p $PLATFORM \ + -t ./Tests/$TEST \ + -v \ + --input-type-map $MAP \ + --shouldFail + +test_generate_network_type_inference_pass: + stage: test + parallel: + matrix: + - PLATFORM: ['Generic'] + TEST: ['testTypeInferenceDifferentTypes'] + MAP: [ + 'A=int16_t B=int8_t C=int32_t', + ] + script: + - !reference [.setup_test, script] + - python generateNetwork.py \ + -p $PLATFORM \ + -t ./Tests/$TEST \ + -v \ + --input-type-map $MAP + test_deeploy_state_serialization: stage: test parallel: From 1bf6556f8af9d8e7671673c9edbfc57fbeee8946 Mon Sep 17 00:00:00 2001 From: viv-eth Date: Wed, 9 Jul 2025 08:20:17 +0200 Subject: [PATCH 04/15] [DeeployTest] Add manual type inference feature --- .../inputs.npz | Bin 0 -> 2224 bytes .../network.onnx | 31 +++++++++++ .../outputs.npz | Bin 0 -> 756 bytes DeeployTest/generateNetwork.py | 50 ++++++++++++++++-- DeeployTest/testUtils/testRunner.py | 16 ++++++ DeeployTest/testUtils/typeMapping.py | 38 ++++++++++++- 6 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 DeeployTest/Tests/testTypeInferenceDifferentTypes/inputs.npz create mode 100644 DeeployTest/Tests/testTypeInferenceDifferentTypes/network.onnx create mode 100644 DeeployTest/Tests/testTypeInferenceDifferentTypes/outputs.npz diff --git a/DeeployTest/Tests/testTypeInferenceDifferentTypes/inputs.npz b/DeeployTest/Tests/testTypeInferenceDifferentTypes/inputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..db396786bd431cdc9b6cc44c4ab44add452b915e GIT binary patch literal 2224 zcmd6pYfMx}6vzJ{C{|4X0ZmNmQmvq&h_Bix#s#_K19rMq6x3EBf~Yl$KoJzpYAvBc z6qO)WYeg$v3snSs8(bE5ik3U#i)<<4MyVy%HavXbi}YM+jGFXIKRLU<{Lh@3Gjq<} zxsx@9_i*t-ck{*OAeNNk3~0E*3n76k64RaF{=%V6(gw7)gU>b?=Q<%3YyINllNTlV zP0;xTFCOa`pz~Y2GASh~cE!S#NpbO-pT#aujwgTelGwy}>idrh(2eW<4iC_+)BTV3 z4$?N-&>A6dem%o7lYrN|3>9?@ro&-aa7!T9E^zO-K+_X}Ypo29I0gGY5!h^H*q1L5 zJ)hyDAp-6L6zKdI3fjp>YxqMz*K!`lfdURYL(E_s5=SX8@3q0PgQ0R0gYion`hG27 z{7JwPV8gVXHuOBgkl18{rH=xAmV!-31v+mi2$~~cuCbwUxIk?x!_XT7^G-0tg)oF| z5ojI35L9P_*}@Rr#4z(B!}1}t#~1~If~HDZyYM_rb{n=lpywOGpx>szGL!OdSFq+P!xGx3k)B^& zq=0Ey7&hq`MoSq!~gf>@_CWZi3QU_@SX&6yM3?+gHkY7*non)KN0db+geZoi- zkncxQGtG%4yPb%jei7lY0RE)fn%|SAkaiKxL>~3?iL9z_pN`^Nsoz2v$+pmbFXmI< zLBtT+9?hhBE3k>UMRqP}FkvLSfpj0OcbarR(M~>X4XqXzvMrR4?%(k})8e%}8_6dL2l>vDwphDk^^~g?r`0u-e6565QxM@E^7>J&-+S;vigWknsoknm z-qxcUdi$B|d|W3#8#7WiJ~hjl=f!g1G`l*wt<5mIccx(yKPBV6bJQn0i{#>SIR;zQ zhpJa<8OKIX!+~GFla_mJhCzc0)W~zU_|N6Fywh_Rm&K>Kn{x->wt9dZxa|)AAb%Zi zU*RL4%_`v|dUr_M$5+%fl?u-;?35|XXBZw=jp5$)5BSrC_tm(}&f1H*O4(MnB0TwS zA-{WQtRd{?efjzA-E!RG-}!;61{wRnEn?}IOxb14`^97*B6pMyk_Wdwl2a-!8gj2C z@w}x%9r^I0ynHZ$M~}=i_}u6h(JSx_Z%Nr8hpnH_^BbGhvjZzsc+|?sKOgh*K3~Xc z&s07+cZeE$Jc}PSyVZ`JUM|xv4CU!*OZe1@+xZ4na$EIPnV@SsCxO@;NtWhw-SVKk$lq4H4Jt^VP}q$HL1! z9?6>Elj^R&N(?C_4!-(wouPO^sbS|NAATu&hOBq_P3;#uK~3uNRXxsB^HKdBvdBA1 zE=r#%=VgqSe-t(Ir7mr9uJ2*#F|JOEg++2^@LqZKV3#4`gkG*XbxeMh?Jm=^&AjI7 zEZ%=ZvfL69r20OJRVQs9Esfz$&R#wK>n{LaHj|TPzW77%X2|PT@yiI9oQmnxzqpv+ f40-*Pcr|2>E4p70W4LQiZJ-Bzc9A-{X}x~{CGFSp literal 0 HcmV?d00001 diff --git a/DeeployTest/Tests/testTypeInferenceDifferentTypes/network.onnx b/DeeployTest/Tests/testTypeInferenceDifferentTypes/network.onnx new file mode 100644 index 0000000000..588abf9344 --- /dev/null +++ b/DeeployTest/Tests/testTypeInferenceDifferentTypes/network.onnx @@ -0,0 +1,31 @@ + add_chain_test:± + +A +BXAdd1"Add + +X +CYAdd2"Addadd_chain_graphZ +A + + + + +Z +B + + + + +Z +C + + + + +b +Y + + + + +B \ No newline at end of file diff --git a/DeeployTest/Tests/testTypeInferenceDifferentTypes/outputs.npz b/DeeployTest/Tests/testTypeInferenceDifferentTypes/outputs.npz new file mode 100644 index 0000000000000000000000000000000000000000..2269dd7292bd3e81b41c99ff754f67575a4e5388 GIT binary patch literal 756 zcmWIWW@Zs#fB;2?4_7>_N|+cJK$w+5gdtKdub`5VK>#cOQUsC!fysWMz5$Vp3}p<} z>M5zk$wlf`3hFkQCh9s0>S_5!B}IvO@%cq5sUUH;#GK+(pm=dcVnHg9uVJX8UD2E}qO|SgxOW~9s4b0H{O)_ryW-Z( zYrdq+5Zpa+))&{SR*_p8H8@W`658bd85*rnL2%p_`+h(K5nQC z7E!wOCDo&N^Y>JHKFgF_Vw_rGDdE*^rvkS<6@Hj+cuI49Gk^0kp0(;9bn5@VsubPjS1)eCstK*zMff(oVc1+Woq7JGMqw2u%eznaEVz2@ z-z|Qw>l#ypZ*GaOXgQY`CYji>)n=28@$($t)>*UWzO-EWtyRcF&YUU5c6zL{i|hXX z4fo9^O-y;rKBea6=Got8W|x1hnR8{&p^kqowhU6ozZ&^OyL{bn_wUss(aXw0jWoH> z%37J2$$EZ&l9nE~O?H~UQ7or=+gr6eHv})r@cnD?JGAc6sXe~ee;QrVyK3Rothm5r zwdJl0%+)T+kpkbg*X3|18$RLs7unQWD;S str: command += " --debug" if hasattr(self.args, 'profileUntiled') and self.args.profileUntiled: command += " --profileUntiled" + if self.args.input_types: + command += " --input-types=" + " ".join(self.args.input_types) + if self.args.input_offsets: + command += " --input-offsets=" + " ".join(self.args.input_offsets) if self.tiling_arguments: if self.args.defaultMemLevel: diff --git a/DeeployTest/testUtils/typeMapping.py b/DeeployTest/testUtils/typeMapping.py index 840fe7b859..9672ea5634 100644 --- a/DeeployTest/testUtils/typeMapping.py +++ b/DeeployTest/testUtils/typeMapping.py @@ -33,6 +33,19 @@ offsetType = namedtuple("offsetType", ("type", "offset")) +_ALL_DTYPES = {t.typeName: t for t in (*IntegerDataTypes, *FloatDataTypes)} + + +def parseDataType(name: str): + """ + Parses a data type from its name. + :param name: The name of the data type. + :return: The corresponding data type class. + """ + if name not in _ALL_DTYPES: + raise ValueError(f"Unknown data type: {name}") + return _ALL_DTYPES[name] + def isInteger(_input: np.array) -> bool: if np.abs((_input.astype(int) - _input)).max() > 0.001: @@ -61,13 +74,36 @@ def dataWidth(n): def inferInputType(_input: np.ndarray, signProp: Optional[bool] = None, defaultType = PointerClass(int8_t), - defaultOffset = 0) -> List[offsetType]: + defaultOffset = 0, + *, + autoInfer: bool = True) -> List[offsetType]: # WIESEP: We cannot do type inference for empty arrays. if np.prod(_input.shape) == 0: print(f"Warning: Empty input array for type inference for {_input}!") return [(defaultType, defaultOffset)] + # If the caller provided a manual override, skip all inference. + if not autoInfer: + rawType = defaultType.referencedType + vals = (_input.astype(np.int64) - defaultOffset) + if not rawType.checkPromotion(vals): + lo, hi = rawType.typeMin, rawType.typeMax + raise RuntimeError(f"Provided type {rawType.typeName} with offset {defaultOffset} " + f"does not match input values in range [{vals.min()}, {vals.max()}] " + f"(expected range [{lo}, {hi}])") + + smallest = rawType + for caand in sorted(IntegerDataTypes, key = lambda x: x.typeWidth): + if caand.checkPromotion(vals): + smallest = caand + break + if smallest is not rawType: + print(f"WARNING: Data spans [{int(vals.min())}, {int(vals.max())}], " + f"which would fit in {smallest.typeName}, " + f"but user forced {rawType.typeName}.") + return [(defaultType, defaultOffset)] + if signProp is None: signProp = False From d89332da5449505668d022a3aff27c1a3f637ef0 Mon Sep 17 00:00:00 2001 From: viv-eth Date: Wed, 9 Jul 2025 08:20:46 +0200 Subject: [PATCH 05/15] [CHANGELOG] Update with manual type inference feature --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9517069e4c..e2baba2b6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ This release containing major architectural changes, new platform support, enhan ### Added +- Add manual type inference feature due to ambiguous inference when test inputs are not representing the type well enough - ChimeraDeployer, currently mainly a placeholder - Allocate templates for Chimera - ChimeraPlatform, using appropriate allocation templates and using the generic Parser + Binding for the Add node From 9916b158166ba0549b25e5f9c9e3ed5cc585c8af Mon Sep 17 00:00:00 2001 From: viv-eth Date: Wed, 9 Jul 2025 08:26:51 +0200 Subject: [PATCH 06/15] [DeeployTest] Align command line args with network generator --- DeeployTest/testUtils/testRunner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DeeployTest/testUtils/testRunner.py b/DeeployTest/testUtils/testRunner.py index d3bd64d3fe..9e0add8538 100644 --- a/DeeployTest/testUtils/testRunner.py +++ b/DeeployTest/testUtils/testRunner.py @@ -256,10 +256,10 @@ def generate_cmd_args(self) -> str: command += " --debug" if hasattr(self.args, 'profileUntiled') and self.args.profileUntiled: command += " --profileUntiled" - if self.args.input_types: - command += " --input-types=" + " ".join(self.args.input_types) - if self.args.input_offsets: - command += " --input-offsets=" + " ".join(self.args.input_offsets) + if self.args.input_type_map: + command += " --input-type-map=" + " ".join(self.args.input_type_map) + if self.args.input_offset_map: + command += " --input-offset-map=" + " ".join(self.args.input_offset_map) if self.tiling_arguments: if self.args.defaultMemLevel: From 8af8650408b6d3adb9145044d170c091bb54ce64 Mon Sep 17 00:00:00 2001 From: viv-eth Date: Sat, 16 Aug 2025 21:43:38 +0200 Subject: [PATCH 07/15] [CHANGELOG] Add PR to list of PRs --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2baba2b6f..8914b31b41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid This release containing major architectural changes, new platform support, enhanced simulation workflows, floating-point kernel support, training infrastructure for CCT models, memory allocation strategies, and documentation improvements. ### List of Pull Requests +- Change order of typeMatching entries [#68](https://github.com/pulp-platform/Deeploy/pull/68) - Prepare v0.2.0 release [#102](https://github.com/pulp-platform/Deeploy/pull/102) - Add Luka as Code Owner [#101](https://github.com/pulp-platform/Deeploy/pull/101) - Fix CI, Docker Files, and Documentation Workflow [#100](https://github.com/pulp-platform/Deeploy/pull/100) From 9eb189261041f5f678338db62d9eaa3e8ddcfeeb Mon Sep 17 00:00:00 2001 From: viv-eth Date: Sat, 16 Aug 2025 21:48:49 +0200 Subject: [PATCH 08/15] [DeeployTest] Remove non-ASCII chars --- DeeployTest/generateNetwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DeeployTest/generateNetwork.py b/DeeployTest/generateNetwork.py index 71f6ed5ac9..d769c3f7ed 100644 --- a/DeeployTest/generateNetwork.py +++ b/DeeployTest/generateNetwork.py @@ -89,7 +89,7 @@ tensors = graph.tensors() - # build name→type and name→offset maps + # build {name, type} and {name, offset} maps manual_types = {} manual_offsets = {} for kv in args.input_type_map: From 4dd67a1c718be19814a1154374c3f76e07ac4e03 Mon Sep 17 00:00:00 2001 From: viv-eth Date: Sat, 16 Aug 2025 21:55:09 +0200 Subject: [PATCH 09/15] [DeeployTest] Move to Numpy-style docs --- DeeployTest/testUtils/typeMapping.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/DeeployTest/testUtils/typeMapping.py b/DeeployTest/testUtils/typeMapping.py index 9672ea5634..5b345f7081 100644 --- a/DeeployTest/testUtils/typeMapping.py +++ b/DeeployTest/testUtils/typeMapping.py @@ -37,10 +37,22 @@ def parseDataType(name: str): - """ - Parses a data type from its name. - :param name: The name of the data type. - :return: The corresponding data type class. + """Parses a data type from its name. + + Parameters + ---------- + name : str + The name of the data type. + + Returns + ------- + class + The corresponding data type class. + + Raises + ------ + ValueError + If the provided data type name is unknown. """ if name not in _ALL_DTYPES: raise ValueError(f"Unknown data type: {name}") From 38f3914890a5597746cdb7637aa2bc05c00822d8 Mon Sep 17 00:00:00 2001 From: viv-eth Date: Sat, 16 Aug 2025 23:43:36 +0200 Subject: [PATCH 10/15] [DeeployTest] Fix input/output indexing error by zipping input names and arrays --- DeeployTest/generateNetwork.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DeeployTest/generateNetwork.py b/DeeployTest/generateNetwork.py index d769c3f7ed..67e8b118c7 100644 --- a/DeeployTest/generateNetwork.py +++ b/DeeployTest/generateNetwork.py @@ -122,12 +122,11 @@ platform, signProp = mapPlatform(args.platform) - for index, name in enumerate(inputs.files): + for index, (name, num) in enumerate(zip(inputs.files, test_inputs)): # WIESP: Do not infer types and offset of empty arrays num = test_inputs[index] if np.prod(num.shape) == 0: continue - num = test_inputs[index] defaultType = manual_types.get(name, PointerClass(int8_t)) defaultOffset = manual_offsets.get(name, 0) autoInfer = name not in manual_keys From 948cf40a5da03f3dec94ae329e9750b8d4ff46e7 Mon Sep 17 00:00:00 2001 From: Philip Wiese Date: Fri, 22 Aug 2025 15:32:31 +0200 Subject: [PATCH 11/15] Fix CI Tests - Add GitHub tests - Remove outdated GitLab CI file - Add support for `--shouldFail` for network generation --- .github/workflows/CI.yml | 42 ++ .gitlab-ci.yml | 770 --------------------------------- DeeployTest/generateNetwork.py | 72 +-- 3 files changed, 87 insertions(+), 797 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4b74c66f97..5e078c0c56 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1025,3 +1025,45 @@ jobs: run: | grep -Lr "SPDX-License-Identifier: Apache-2.0" --exclude-dir="toolchain" --exclude-dir="install" --exclude-dir=".git" . --exclude-dir="third_party" --exclude-dir="TEST_*" --exclude-dir="runtime" | grep ".*\.h$" || [[ $? == 1 ]] shell: bash + + generate-network-type-inference: + runs-on: ${{ needs.select-docker-image-and-runner.outputs.runner }} + needs: select-docker-image-and-runner + container: + image: ${{ needs.select-docker-image-and-runner.outputs.image }} + strategy: + fail-fast: false + matrix: + include: + - name: fail-input0 + platform: Generic + test: testTypeInferenceDifferentTypes + map: A=int8_t B=int8_t C=int8_t + shouldFail: true + - name: fail-input2 + platform: Generic + test: testTypeInferenceDifferentTypes + map: A=int16_t B=int8_t C=int16_t + shouldFail: true + - name: pass + platform: Generic + test: testTypeInferenceDifferentTypes + map: A=int16_t B=int8_t C=int32_t + shouldFail: false + name: Test Type Inference (${{ matrix.name }}) + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Build Deeploy + run: pip install -e . + - name: Run Test + run: | + cd DeeployTest + python generateNetwork.py \ + -p ${{ matrix.platform }} \ + -t ./Tests/${{ matrix.test }} \ + -v \ + --input-type-map ${{ matrix.map }} \ + ${{ matrix.shouldFail && '--shouldFail' || '' }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 1a41fa5175..0000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,770 +0,0 @@ -variables: - GIT_SUBMODULE_STRATEGY: recursive - FF_USE_FASTZIP: "true" - # These can be specified per job or per pipeline - ARTIFACT_COMPRESSION_LEVEL: "fastest" - CACHE_COMPRESSION_LEVEL: "fastest" - TOOLCHAIN: "LLVM" - CMAKE_GENERATOR: "Ninja" - -stages: # List of stages for jobs, and their order of execution - - test - -.setup_test: - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - cd DeeployTest - - git lfs pull - -build_deeploy: # This job runs in the build stage, which runs first. - stage: test - resource_group: install - artifacts: - untracked: true - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - pip install -e . - - rm -f DeeployTest/out.txt - -gen_docs: - stage: test - resource_group: install - artifacts: - untracked: true - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - make docs - -run_cmsis_test_models: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - qemu-arm - parallel: - matrix: - - TEST: [simpleRegression, WaveFormer] - script: - - !reference [.setup_test, script] - - python testRunner_cortexm.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_QEMU/Tests/$TEST/*.c - - ./DeeployTest/TEST_QEMU/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_cmsis_test_kernels: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - qemu-arm - parallel: - matrix: - - TEST: [Adder, MultIO, test1DPad, test2DPad, testMatMul, testMatMulAdd, testMaxPool, testRQConv, testReduceSum, testReduceMean, testSlice] - script: - - !reference [.setup_test, script] - - python testRunner_cortexm.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_QEMU/Tests/$TEST/*.c - - ./DeeployTest/TEST_QEMU/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_test_models: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: [simpleRegression, miniMobileNet, miniMobileNetv2, Attention, MLPerf/KeywordSpotting, MLPerf/ImageClassification, MLPerf/AnomalyDetection] - script: - - !reference [.setup_test, script] - - python testRunner_siracusa.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_test_kernels: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: [Adder, MultIO, test1DPad, test2DPad, testMatMul, testMatMulAdd, testRequantizedDWConv, test2DRequantizedConv, iSoftmax, testConcat, testRMSNorm, trueIntegerDivSandwich, Hardswish, RQHardswish, testBacktracking] - script: - - !reference [.setup_test, script] - - python testRunner_siracusa.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_DMA_slice_L2: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - script: - - !reference [.setup_test, script] - - python testSlice_PULP.py --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/testSlice/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/testSlice/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_tiled_kernels_singlebuffer_L2: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: "testMatMul" - L1: [64000, 32000, 16000] - - TEST: "test2DRequantizedConv" - L1: [8000, 6000, 4000] - - TEST: "testRequantizedDWConv" - L1: [2561] # SCHEREMO: The implicit transpose after the conv is untiled; need at least 2560 - - TEST: "iSoftmax" - L1: [800, 500, 300] - - TEST: "testConcat" - L1: [32000, 16000, 8000] - - TEST: "testRMSNorm" - L1: [2048, 1024, 512] - - TEST: "Hardswish" - L1: [750] - - TEST: "RQHardswish" - L1: [750] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_tiled_kernels_doublebuffer_L2: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: "testMatMul" - L1: [64000, 32000, 16000] - - TEST: "test2DRequantizedConv" - L1: [8000, 6000, 5000] - - TEST: "testRequantizedDWConv" - L1: [5121] # SCHEREMO: The implicit transpose after the conv is untiled; need at least 2560 * 2 for DB - - TEST: "iSoftmax" - L1: [1600, 1000, 600] - - TEST: "testConcat" - L1: [64000, 32000, 16000] - - TEST: "testRMSNorm" - L1: [4096, 2048, 1024] - - TEST: "Hardswish" - L1: [750] - - TEST: "RQHardswish" - L1: [750] - - TEST: "microLlama/microLlama1" - L1: [60000, 20000, 10000] - - TEST: "microLlama/microLlama8" - L1: [60000, 20000, 10000] - - TEST: "microLlama/microLlama8_parallel" - L1: [60000, 20000, 10000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 --doublebuffer - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_tiled_models_singlebuffer_L2: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: "simpleRegression" - L1: [45000, 30000, 15000] - - TEST: "miniMobileNet" - L1: [60000, 12000, 6000, 3000] - - TEST: "miniMobileNetv2" - L1: [60000, 16000, 12000, 8000] - - TEST: "Attention" - L1: [60000, 10000, 5000] - - TEST: "microLlama/microLlama1" - L1: [60000, 10000, 5000] - - TEST: "microLlama/microLlama8" - L1: [60000, 10000, 5000] - - TEST: "microLlama/microLlama8_parallel" - L1: [60000, 10000, 5000] - - TEST: "MLPerf/KeywordSpotting" - L1: [64000] - - TEST: "MLPerf/ImageClassification" - L1: [64000] - - TEST: "MLPerf/AnomalyDetection" - L1: [64000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_tiled_models_singlebuffer_L3: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: "simpleRegression" - L1: [45000, 30000, 16000] # SCHEREMO: 15000 leads to non-2d transfers in L3! - - TEST: "miniMobileNet" - L1: [60000, 12000, 6000] # SCHEREMO: 3000 leads to non-2d transfers in L3! - - TEST: "miniMobileNetv2" - L1: [60000, 16000, 12000, 8000] - - TEST: "Attention" - L1: [60000, 10000, 5000, 2500] - - TEST: "Transformer" - L1: [60000, 30000, 15000] - - TEST: "microLlama/microLlama1" - L1: [60000, 10000, 5000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 --defaultMemLevel=L3 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - - -run_siracusa_tiled_models_doublebuffer_L3: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - PULP - parallel: - matrix: - - TEST: "simpleRegression" - L1: [60000, 45000, 30000] - - TEST: "miniMobileNet" - L1: [60000, 24000, 12000, 6000] - - TEST: "miniMobileNetv2" - L1: [60000, 32000, 24000, 16000] - - TEST: "Attention" - L1: [60000, 20000, 10000, 5000] - - TEST: "Transformer" - L1: [60000, 30000, 15000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 --doublebuffer --defaultMemLevel=L3 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - - -run_siracusa_w_neureka_tiled_kernels_singlebuffer_L2: - stage: test - tags: - - PULP - parallel: - matrix: - - TEST: "testRequantizedLinear" - L1: [16000] - - TEST: "testPointwise" - L1: [32000] - - TEST: "testPointwiseConvBNReLU" - L1: [32000] - - TEST: "testPointwiseUnsignedWeights" - L1: [32000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa_w_neureka.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --defaultMemLevel=L2 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - - -run_siracusa_w_neureka_tiled_kernels_doublebuffer_L2: - stage: test - tags: - - PULP - parallel: - matrix: - - TEST: "testRequantizedLinear" - L1: [16000] - - TEST: "testPointwise" - L1: [32000] - - TEST: "testPointwiseConvBNReLU" - L1: [32000] - - TEST: "testPointwiseUnsignedWeights" - L1: [32000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa_w_neureka.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --defaultMemLevel=L2 --doublebuffer - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_w_neureka_tiled_models_singlebuffer_L3: - stage: test - tags: - - PULP - parallel: - matrix: - - TEST: "miniMobileNet" - L1: [2000] # LMACAN: 1000 leads to non-2d transfers in L3! - - TEST: "Attention" - L1: [2500] - - TEST: "Transformer" - L1: [15000] - - TEST: "microLlama/microLlama1" - L1: [10000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa_w_neureka.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 --defaultMemLevel=L3 - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_w_neureka_tiled_models_doublebuffer_L3: - stage: test - tags: - - PULP - parallel: - matrix: - - TEST: "miniMobileNet" - L1: [2000] # LMACAN: 1000 leads to non-2d transfers in L3! - - TEST: "Attention" - L1: [5000] - - TEST: "Transformer" - L1: [30000] - script: - - !reference [.setup_test, script] - - python testRunner_tiled_siracusa_w_neureka.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 --defaultMemLevel=L3 --doublebuffer - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_w_neureka_tiled_kernels_singlebuffer_L2_wmem: - stage: test - tags: - - PULP - parallel: - matrix: - - TEST: "testRequantizedLinear" - L1: [16000] - - TEST: "testPointwise" - L1: [32000] - - TEST: "testPointwiseConvBNReLU" - L1: [32000] - - TEST: "testPointwiseUnsignedWeights" - L1: [32000] - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - cd DeeployTest - - python testRunner_tiled_siracusa_w_neureka.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --defaultMemLevel=L2 --neureka-wmem - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_siracusa_w_neureka_tiled_models_doublebuffer_L3_wmem: - stage: test - tags: - - PULP - parallel: - matrix: - - TEST: "miniMobileNet" - L1: [2000] # LMACAN: 1000 leads to non-2d transfers in L3! - - TEST: "Attention" - L1: [2500] - - TEST: "Transformer" - L1: [30000] - - TEST: "microLlama/microLlama1" - L1: [10000] - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - cd DeeployTest - - python testRunner_tiled_siracusa_w_neureka.py -t ./Tests/$TEST --l1 $L1 --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR --cores=8 --defaultMemLevel=L3 --doublebuffer --neureka-wmem - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.c - - ./DeeployTest/TEST_SIRACUSA_W_NEUREKA/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_mempool_test_kernels: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - banshee - retry: 2 - parallel: - matrix: - - TEST: [Adder, MultIO, test1DConvolution, test2DConvolution, test1DDWConvolution, test2DDWConvolution, test1DPad, test2DPad, testGEMM, testMatMul, testMatMulAdd, testMaxPool, testRQConv, testRQGEMM, testRQMatMul, testReduceSum, testReduceMean, testSlice, testRequantizedDWConv, test2DRequantizedConv] - script: - - !reference [.setup_test, script] - - python testRunner_mempool.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_MEMPOOL/Tests/$TEST/*.c - - ./DeeployTest/TEST_MEMPOOL/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_mempool_test_models: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - tags: - - banshee - retry: 2 - parallel: - matrix: - - TEST: [simpleRegression, simpleCNN, ICCT, ICCT_ITA, ICCT_8, ICCT_ITA_8, miniMobileNet, miniMobileNetv2] - script: - - !reference [.setup_test, script] - - python testRunner_mempool.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - # - python testRunner_mempool.py -t ./Tests/WaveFormer -DGCC_INSTALL_DIR=$MEMPOOL_GCC_INSTALL_DIR # Boken with ITA (heap is too small) - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_MEMPOOL/Tests/$TEST/*.c - - ./DeeployTest/TEST_MEMPOOL/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_generic_test_kernels: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - parallel: - matrix: - - TEST: [Adder, MultIO, test1DConvolution, test2DConvolution, test1DDWConvolution, test2DDWConvolution, test1DPad, test2DPad, testConcat, testGEMM, testMatMul, testMatMulAdd, testMaxPool, testRQConv, testRQMatMul, testReduceSum, testReduceMean, testSlice, testRequantizedDWConv, test2DRequantizedConv, iSoftmax] - script: - - !reference [.setup_test, script] - - python testRunner_generic.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_GENERIC/Tests/$TEST/*.c - - ./DeeployTest/TEST_GENERIC/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -run_generic_test_models: # This job runs in the test stage. - stage: test # It only starts when the job in the build stage completes successfully. - parallel: - matrix: - - TEST: [simpleRegression, WaveFormer, simpleCNN, ICCT, ICCT_ITA, ICCT_8, ICCT_ITA_8, miniMobileNet, miniMobileNetv2] - script: - - !reference [.setup_test, script] - - python testRunner_generic.py -t ./Tests/$TEST --toolchain=$TOOLCHAIN --toolchain_install_dir=$LLVM_INSTALL_DIR - artifacts: - name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA" - paths: - - ./DeeployTest/out.txt - - ./DeeployTest/TEST_GENERIC/Tests/$TEST/*.c - - ./DeeployTest/TEST_GENERIC/Tests/$TEST/*.h - expire_in: 4 weeks - cache: - key: $CI_PROJECT_DIR-$CI_COMMIT_REF_SLUG - paths: - - ./DeeployTest/TEST_*/build - -test_generate_network_type_inference_fail_input0: - stage: test - parallel: - matrix: - - PLATFORM: ['Generic'] - TEST: ['testTypeInferenceDifferentTypes'] - MAP: [ - 'A=int8_t B=int8_t C=int8_t', - ] - script: - - !reference [.setup_test, script] - - python generateNetwork.py \ - -p $PLATFORM \ - -t ./Tests/$TEST \ - -v \ - --input-type-map $MAP \ - --shouldFail - -test_generate_network_type_inference_fail_input2: - stage: test - parallel: - matrix: - - PLATFORM: ['Generic'] - TEST: ['testTypeInferenceDifferentTypes'] - MAP: [ - 'A=int16_t B=int8_t C=int16_t', - ] - script: - - !reference [.setup_test, script] - - python generateNetwork.py \ - -p $PLATFORM \ - -t ./Tests/$TEST \ - -v \ - --input-type-map $MAP \ - --shouldFail - -test_generate_network_type_inference_pass: - stage: test - parallel: - matrix: - - PLATFORM: ['Generic'] - TEST: ['testTypeInferenceDifferentTypes'] - MAP: [ - 'A=int16_t B=int8_t C=int32_t', - ] - script: - - !reference [.setup_test, script] - - python generateNetwork.py \ - -p $PLATFORM \ - -t ./Tests/$TEST \ - -v \ - --input-type-map $MAP - -test_deeploy_state_serialization: - stage: test - parallel: - matrix: - - TEST: [simpleRegression] - PLATFORM: ['QEMU-ARM', 'Siracusa', 'MemPool', 'Generic'] - script: - - !reference [.setup_test, script] - - python deeployStateEqualityTest.py -t ./Tests/$TEST -p $PLATFORM - -test_memory_level_extension: - stage: test - parallel: - matrix: - - TEST: [simpleRegression] - PLATFORM: ['QEMU-ARM', 'Siracusa', 'MemPool', 'Generic'] - script: - - !reference [.setup_test, script] - - python testMemoryLevelExtension.py -t ./Tests/$TEST -p $PLATFORM - -test_tiler_extension: - stage: test - parallel: - matrix: - - TEST: [simpleRegression, simpleCNN, testMatMul, testMaxPool] - PLATFORM: ['Siracusa'] - script: - - !reference [.setup_test, script] - - python testTilerExtension.py -t ./Tests/$TEST -p $PLATFORM - -test_tiler_extension_fails: - stage: test - parallel: - matrix: - - TEST: [simpleRegression, simpleCNN, testMatMul] - PLATFORM: ['Siracusa'] - script: - - !reference [.setup_test, script] - - python testTilerExtension.py -t ./Tests/$TEST -p $PLATFORM --l1 2000 --shouldFail - -test_memory_allocation_extension: - stage: test - parallel: - matrix: - - TEST: [simpleRegression, simpleCNN, miniMobileNet, miniMobileNetv2, testMatMul, testMaxPool] - PLATFORM: ['Siracusa'] - script: - - !reference [.setup_test, script] - - python testTilerExtension.py -t ./Tests/$TEST -p $PLATFORM - -test_deeploy_typing: - stage: test - script: - - !reference [.setup_test, script] - - python testTypes.py - -test_regex_matching: - stage: test - script: - - !reference [.setup_test, script] - - python testRegexMatching.py - -format_python: - stage: test - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - yapf -rpd -e "third_party/" -e "install/" -e "toolchain/" . - -format_python_imports: - stage: test - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - isort --sg "**/third_party/*" --sg "install/*" --sg "toolchain/*" ./ -c -v - - autoflake -c -r --remove-all-unused-imports --ignore-init-module-imports --exclude "*/third_party/**" ./ - -format_c: - stage: test - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - python scripts/run_clang_format.py -e "*/third_party/*" -e "*/install/*" -e "*/toolchain/*" -ir --clang-format-executable=${LLVM_INSTALL_DIR}/bin/clang-format ./ scripts - -lint_python_licenses: - stage: test - variables: - LICENSE_STRING: "SPDX-License-Identifier: Apache-2.0" - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - grep -Lr "$LICENSE_STRING" --exclude-dir="toolchain" --exclude-dir="install" --exclude-dir=".git" . --exclude-dir="third_party" --exclude-dir="TEST_*" --exclude "run_clang_format.py" | grep ".*\.py$" || [[ $? == 1 ]] - -lint_c_licenses: - stage: test - variables: - LICENSE_STRING: "SPDX-License-Identifier: Apache-2.0" - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - grep -Lr "$LICENSE_STRING" --exclude-dir="toolchain" --exclude-dir="install" --exclude-dir=".git" . --exclude-dir="third_party" --exclude-dir="TEST_*" --exclude-dir="runtime" | grep ".*\.c$" || [[ $? == 1 ]] - -lint_c_header_licenses: - stage: test - variables: - LICENSE_STRING: "SPDX-License-Identifier: Apache-2.0" - script: - - bash && source ~/.bashrc - - $CONDA activate dumpoci - - export PYTHONPATH=`pwd`:$PYTHONPATH - - grep -Lr "$LICENSE_STRING" --exclude-dir="toolchain" --exclude-dir="install" --exclude-dir=".git" . --exclude-dir="third_party" --exclude-dir="TEST_*" --exclude-dir="runtime" | grep ".*\.h$" || [[ $? == 1 ]] diff --git a/DeeployTest/generateNetwork.py b/DeeployTest/generateNetwork.py index 67e8b118c7..e25011033b 100644 --- a/DeeployTest/generateNetwork.py +++ b/DeeployTest/generateNetwork.py @@ -26,6 +26,7 @@ # limitations under the License. import os +import sys import numpy as np import onnx @@ -46,34 +47,8 @@ _TEXT_ALIGN = 30 -if __name__ == '__main__': - - parser = TestGeneratorArgumentParser(description = "Deeploy Code Generation Utility.") - parser.add_argument('--debug', - dest = 'debug', - action = 'store_true', - default = False, - help = 'Enable debugging mode\n') - parser.add_argument('--profileUntiled', - action = 'store_true', - dest = 'profileUntiled', - default = False, - help = 'Profile Untiled for L2\n') - parser.add_argument('--input-type-map', - nargs = '*', - default = [], - help = '(Optional) mapping of input names to data types. ' - 'If not specified, types are inferred from the input data. ' - 'Example: --input-type-map input_0=int8_t input_1=float32 ...') - parser.add_argument('--input-offset-map', - nargs = '*', - default = [], - help = '(Optional) mapping of input names to offsets. ' - 'If not specified, offsets are set to 0. ' - 'Example: --input-offset-map input_0=0 input_1=128 ...') - - args = parser.parse_args() +def generateNetwork(args): onnx_graph = onnx.load_model(f'{args.dir}/network.onnx') graph = gs.import_onnx(onnx_graph) @@ -193,3 +168,46 @@ print() print(f"{'Number of Ops:' :<{_TEXT_ALIGN}} {num_ops}") print(f"{'Model Parameters: ' :<{_TEXT_ALIGN}} {deployer.getParameterSize()}") + + +if __name__ == '__main__': + + parser = TestGeneratorArgumentParser(description = "Deeploy Code Generation Utility.") + parser.add_argument('--debug', + dest = 'debug', + action = 'store_true', + default = False, + help = 'Enable debugging mode\n') + parser.add_argument('--profileUntiled', + action = 'store_true', + dest = 'profileUntiled', + default = False, + help = 'Profile Untiled for L2\n') + parser.add_argument('--input-type-map', + nargs = '*', + default = [], + help = '(Optional) mapping of input names to data types. ' + 'If not specified, types are inferred from the input data. ' + 'Example: --input-type-map input_0=int8_t input_1=float32_t ...') + parser.add_argument('--input-offset-map', + nargs = '*', + default = [], + help = '(Optional) mapping of input names to offsets. ' + 'If not specified, offsets are set to 0. ' + 'Example: --input-offset-map input_0=0 input_1=128 ...') + parser.add_argument('--shouldFail', action = 'store_true') + parser.set_defaults(shouldFail = False) + + args = parser.parse_args() + + try: + generateNetwork(args) + except Exception as e: + if args.shouldFail: + print("\033[92mNetwork generation ended, failed as expected!\033[0m") + sys.exit(0) + else: + raise e + + if args.shouldFail: + raise RuntimeError("Expected to fail!") From aeac9d8549815da0eb7346fb53c0bf82d793a2e3 Mon Sep 17 00:00:00 2001 From: Philip Wiese Date: Fri, 22 Aug 2025 15:33:04 +0200 Subject: [PATCH 12/15] Fix Changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8914b31b41..b46590ac0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,13 @@ This file contains the changelog for the Deeploy project. The changelog is divid ## Unreleased (Planned Release Target: v0.2.1) ### List of Pull Requests +- Change order of typeMatching entries [#68](https://github.com/pulp-platform/Deeploy/pull/68) - Node Mangling to avoid duplication [#93](https://github.com/pulp-platform/Deeploy/pull/93) - Prepare Post v0.2.0 Release [#104](https://github.com/pulp-platform/Deeploy/pull/104) - Use Docker digests instead of arch-specific tags [#106](https://github.com/pulp-platform/Deeploy/pull/106) ### Added +- Add manual type inference feature (CLI: `--input-type-map`/`--input-offset-map`) to resolve ambiguities when test inputs are not representative enough - Added `_mangleNodeNames` function to avoid duplicate node mappings - Output Docker image digests per platform (`amd64`, `arm64`) after build, which is used to construct the multi-arch Docker manifest. This preventes registry clutter caused by unnecessary per-architecture Docker tags. @@ -25,7 +27,6 @@ This file contains the changelog for the Deeploy project. The changelog is divid This release containing major architectural changes, new platform support, enhanced simulation workflows, floating-point kernel support, training infrastructure for CCT models, memory allocation strategies, and documentation improvements. ### List of Pull Requests -- Change order of typeMatching entries [#68](https://github.com/pulp-platform/Deeploy/pull/68) - Prepare v0.2.0 release [#102](https://github.com/pulp-platform/Deeploy/pull/102) - Add Luka as Code Owner [#101](https://github.com/pulp-platform/Deeploy/pull/101) - Fix CI, Docker Files, and Documentation Workflow [#100](https://github.com/pulp-platform/Deeploy/pull/100) @@ -92,7 +93,6 @@ This release containing major architectural changes, new platform support, enhan ### Added -- Add manual type inference feature due to ambiguous inference when test inputs are not representing the type well enough - ChimeraDeployer, currently mainly a placeholder - Allocate templates for Chimera - ChimeraPlatform, using appropriate allocation templates and using the generic Parser + Binding for the Add node From 214f8da8ea7bfcb70d6fe6e7c0dd2ccd516c7d0a Mon Sep 17 00:00:00 2001 From: Philip Wiese Date: Fri, 22 Aug 2025 17:20:08 +0200 Subject: [PATCH 13/15] Improve debugging and implement CodeRabbit suggestions --- .vscode/launch.json | 13 ++++++++++-- DeeployTest/generateNetwork.py | 28 ++++++++++++++++++------- DeeployTest/testMVP.py | 3 ++- DeeployTest/testUtils/codeGenerate.py | 30 ++++++++++++++++++--------- DeeployTest/testUtils/testRunner.py | 6 +++--- DeeployTest/testUtils/typeMapping.py | 20 +++++++++--------- 6 files changed, 66 insertions(+), 34 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 6703ff81b6..09ee03fed4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -58,8 +58,17 @@ "command": "extension.commandvariable.file.pickFile", "args": { "description": "Select ONNX File", - "include": "DeeployTest/Tests/**/*.onnx", - "transform": { + "include": "**/*.onnx", + "display": "transform", + "fromFolder": { + "fixed": "${workspaceFolder}/DeeployTest/Tests" + }, + "labelTransform": { + "text": "${fileDirname}", + "find": ".*[\\/]", + "replace": "" + }, + "valueTransform": { "text": "${fileDirname}" } } diff --git a/DeeployTest/generateNetwork.py b/DeeployTest/generateNetwork.py index e25011033b..c160339d90 100644 --- a/DeeployTest/generateNetwork.py +++ b/DeeployTest/generateNetwork.py @@ -68,11 +68,25 @@ def generateNetwork(args): manual_types = {} manual_offsets = {} for kv in args.input_type_map: - name, tstr = kv.split('=', 1) - manual_types[name] = PointerClass(parseDataType(tstr)) + try: + name, tstr = kv.split('=', 1) + except ValueError: + raise ValueError(f"Invalid --input-type-map entry '{kv}'. Expected NAME=TYPE.") + name, tstr = name.strip(), tstr.strip() + try: + manual_types[name] = PointerClass(parseDataType(tstr)) + except ValueError as e: + raise ValueError(f"Invalid --input-type-map entry '{kv}': {e}") for kv in args.input_offset_map: - name, ostr = kv.split('=', 1) - manual_offsets[name] = int(ostr) + try: + name, ostr = kv.split('=', 1) + except ValueError: + raise ValueError(f"Invalid --input-offset-map entry '{kv}'. Expected NAME=OFFSET.") + name, ostr = name.strip(), ostr.strip() + try: + manual_offsets[name] = int(ostr) + except ValueError: + raise ValueError(f"Invalid --input-offset-map entry '{kv}': OFFSET must be an integer.") # Sanity check for unknown input names npz_names = set(inputs.files) @@ -98,8 +112,6 @@ def generateNetwork(args): platform, signProp = mapPlatform(args.platform) for index, (name, num) in enumerate(zip(inputs.files, test_inputs)): - # WIESP: Do not infer types and offset of empty arrays - num = test_inputs[index] if np.prod(num.shape) == 0: continue defaultType = manual_types.get(name, PointerClass(int8_t)) @@ -132,8 +144,8 @@ def generateNetwork(args): # Create input and output vectors os.makedirs(f'{args.dumpdir}', exist_ok = True) - - testInputStr = generateTestInputsHeader(deployer, test_inputs, inputTypes, inputOffsets) + print("=" * 80) + testInputStr = generateTestInputsHeader(deployer, test_inputs, inputTypes, inputOffsets, verbose = args.verbose) f = open(f'{args.dumpdir}/testinputs.h', "w") f.write(testInputStr) f.close() diff --git a/DeeployTest/testMVP.py b/DeeployTest/testMVP.py index ff7a3ccf3a..6f0342e7b4 100644 --- a/DeeployTest/testMVP.py +++ b/DeeployTest/testMVP.py @@ -358,7 +358,8 @@ def setupDeployer(graph: gs.Graph, memoryHierarchy: MemoryHierarchy, defaultTarg # Create input and output vectors os.makedirs(f'{args.dumpdir}', exist_ok = True) - testInputStr = generateTestInputsHeader(deployer, test_inputs, inputTypes, inputOffsets) + print("=" * 80) + testInputStr = generateTestInputsHeader(deployer, test_inputs, inputTypes, inputOffsets, args.verbose) f = open(f'{args.dumpdir}/testinputs.h', "w") f.write(testInputStr) f.close() diff --git a/DeeployTest/testUtils/codeGenerate.py b/DeeployTest/testUtils/codeGenerate.py index 7df306efdb..18a68e10da 100644 --- a/DeeployTest/testUtils/codeGenerate.py +++ b/DeeployTest/testUtils/codeGenerate.py @@ -24,7 +24,6 @@ # limitations under the License. import os -from pprint import pprint from typing import Dict, List, Optional, Tuple import numpy as np @@ -51,7 +50,11 @@ def _shapeBroadcast(ctxt, value, name): return broadcastNum -def generateTestInputsHeader(deployer: NetworkDeployer, test_inputs: List, inputTypes: Dict, inputOffsets: Dict) -> str: +def generateTestInputsHeader(deployer: NetworkDeployer, + test_inputs: List, + inputTypes: Dict, + inputOffsets: Dict, + verbose: Optional[bool] = None) -> str: retStr = "" inputNames = [deployer.ctxt.lookup(buf.name) for buf in deployer.graph.inputs] inputTypes = {buf.name: buf._type for buf in inputNames} @@ -97,6 +100,12 @@ def generateTestInputsHeader(deployer: NetworkDeployer, test_inputs: List, input ]) retStr += "};\n" + if verbose: + print('Input:') + for name in inputTypes.keys(): + buf = deployer.ctxt.lookup(name) + print(f" - '{name}': Type: {buf._type.typeName}, Offset: {inputOffsets[name]}") + return retStr @@ -123,9 +132,9 @@ def generateTestOutputsHeader(deployer: NetworkDeployer, data_type = output_data_type[f"output_{index}"] isdatafloat = (data_type.referencedType.typeName == "float32_t") + output_n_levels[f"output_{index}"] = deployer.ctxt.lookup(f'output_{index}').nLevels + output_signed[f"output_{index}"] = deployer.ctxt.lookup(f'output_{index}')._signed if signProp and not isdatafloat: - output_n_levels[f"output_{index}"] = deployer.ctxt.lookup(f'output_{index}').nLevels - output_signed[f"output_{index}"] = deployer.ctxt.lookup(f'output_{index}')._signed test_outputs[index] -= int( ((1 - output_signed[f"output_{index}"]) * (output_n_levels[f"output_{index}"] / 2))) @@ -159,13 +168,14 @@ def generateTestOutputsHeader(deployer: NetworkDeployer, retStr += "};\n" if verbose: + print('Output:') if signProp: - print('Output N Levels:') - pprint(output_n_levels, indent = 2, width = 120) - print('Output Signed:') - pprint(output_signed, indent = 2, width = 120) - print('Output Data Type:') - pprint(output_data_type, indent = 2, width = 120) + for (name, buf), (_, n_level), (_, signed) in zip(output_data_type.items(), output_n_levels.items(), + output_signed.items()): + print(f" - '{name}': Type: {buf.typeName}, nLevels: {n_level}, Signed: {signed}") + else: + for (name, buf) in output_data_type.items(): + print(f" - '{name}': Type: {buf.typeName}") return retStr diff --git a/DeeployTest/testUtils/testRunner.py b/DeeployTest/testUtils/testRunner.py index 9e0add8538..d68856ae43 100644 --- a/DeeployTest/testUtils/testRunner.py +++ b/DeeployTest/testUtils/testRunner.py @@ -182,7 +182,7 @@ def __init__(self, tiling_arguments: bool, description = None): default = [], help = '(Optional) mapping of input names to data types. ' 'If not specified, types are inferred from the input data. ' - 'Example: --input-type-map input_0=int8_t input_1=float32 ...') + 'Example: --input-type-map input_0=int8_t input_1=float32_t ...') self.add_argument('--input-offset-map', nargs = '*', default = [], @@ -257,9 +257,9 @@ def generate_cmd_args(self) -> str: if hasattr(self.args, 'profileUntiled') and self.args.profileUntiled: command += " --profileUntiled" if self.args.input_type_map: - command += " --input-type-map=" + " ".join(self.args.input_type_map) + command += " --input-type-map " + " ".join(self.args.input_type_map) if self.args.input_offset_map: - command += " --input-offset-map=" + " ".join(self.args.input_offset_map) + command += " --input-offset-map " + " ".join(self.args.input_offset_map) if self.tiling_arguments: if self.args.defaultMemLevel: diff --git a/DeeployTest/testUtils/typeMapping.py b/DeeployTest/testUtils/typeMapping.py index 5b345f7081..d8f24df0a7 100644 --- a/DeeployTest/testUtils/typeMapping.py +++ b/DeeployTest/testUtils/typeMapping.py @@ -33,29 +33,30 @@ offsetType = namedtuple("offsetType", ("type", "offset")) -_ALL_DTYPES = {t.typeName: t for t in (*IntegerDataTypes, *FloatDataTypes)} +_ALL_DTYPES: dict[str, type] = {t.typeName: t for t in (*IntegerDataTypes, *FloatDataTypes)} -def parseDataType(name: str): +def parseDataType(name: str) -> type: """Parses a data type from its name. - + Parameters ---------- name : str The name of the data type. - + Returns ------- class The corresponding data type class. - + Raises ------ ValueError If the provided data type name is unknown. """ if name not in _ALL_DTYPES: - raise ValueError(f"Unknown data type: {name}") + allowed = ", ".join(sorted(_ALL_DTYPES)) + raise ValueError(f"Unknown data type: {name}. Allowed: {allowed}") return _ALL_DTYPES[name] @@ -87,7 +88,6 @@ def inferInputType(_input: np.ndarray, signProp: Optional[bool] = None, defaultType = PointerClass(int8_t), defaultOffset = 0, - *, autoInfer: bool = True) -> List[offsetType]: # WIESEP: We cannot do type inference for empty arrays. @@ -106,9 +106,9 @@ def inferInputType(_input: np.ndarray, f"(expected range [{lo}, {hi}])") smallest = rawType - for caand in sorted(IntegerDataTypes, key = lambda x: x.typeWidth): - if caand.checkPromotion(vals): - smallest = caand + for t in sorted(IntegerDataTypes, key = lambda x: x.typeWidth): + if t.checkPromotion(vals): + smallest = t break if smallest is not rawType: print(f"WARNING: Data spans [{int(vals.min())}, {int(vals.max())}], " From 4b5b792923c812fdb4f1b756f4f4903037463679 Mon Sep 17 00:00:00 2001 From: Philip Wiese Date: Wed, 27 Aug 2025 11:02:49 +0200 Subject: [PATCH 14/15] Implement PR feedback for Luka --- .github/workflows/CI.yml | 8 ++- CHANGELOG.md | 3 +- DeeployTest/generateNetwork.py | 70 ++++++++++++++--------- DeeployTest/testUtils/codeGenerate.py | 6 +- DeeployTest/testUtils/testRunner.py | 2 + DeeployTest/testUtils/typeMapping.py | 80 +++++++++++++-------------- 6 files changed, 93 insertions(+), 76 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5e078c0c56..4422d11507 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1039,16 +1039,19 @@ jobs: platform: Generic test: testTypeInferenceDifferentTypes map: A=int8_t B=int8_t C=int8_t + offset_map: A=0 B=0 C=0 shouldFail: true - name: fail-input2 platform: Generic test: testTypeInferenceDifferentTypes map: A=int16_t B=int8_t C=int16_t + offset_map: A=0 B=0 C=0 shouldFail: true - name: pass platform: Generic test: testTypeInferenceDifferentTypes - map: A=int16_t B=int8_t C=int32_t + type_map: A=int16_t B=int8_t C=int32_t + offset_map: A=0 B=0 C=0 shouldFail: false name: Test Type Inference (${{ matrix.name }}) steps: @@ -1065,5 +1068,6 @@ jobs: -p ${{ matrix.platform }} \ -t ./Tests/${{ matrix.test }} \ -v \ - --input-type-map ${{ matrix.map }} \ + --input-type-map ${{ matrix.type_map }} \ + --input-type-offset ${{ matrix.offsetmap }} \ ${{ matrix.shouldFail && '--shouldFail' || '' }} diff --git a/CHANGELOG.md b/CHANGELOG.md index b46590ac0f..9e056c0213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid ### Added - Add manual type inference feature (CLI: `--input-type-map`/`--input-offset-map`) to resolve ambiguities when test inputs are not representative enough +- Added a `testTypeInferenceDifferentTypes` test case to validate type inference for different input types - Added `_mangleNodeNames` function to avoid duplicate node mappings - Output Docker image digests per platform (`amd64`, `arm64`) after build, which is used to construct the multi-arch Docker manifest. This preventes registry clutter caused by unnecessary per-architecture Docker tags. @@ -21,7 +22,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Resolved issue with missing `id` in the `Build Cache for Docker` step, used in the `Inject build-cache` step. ### Removed -- +- Delete outdated and unused `.gitlab-ci.yml` file ## Release v0.2.0 (2025-07-08) [#103](https://github.com/pulp-platform/Deeploy/pull/103) This release containing major architectural changes, new platform support, enhanced simulation workflows, floating-point kernel support, training infrastructure for CCT models, memory allocation strategies, and documentation improvements. diff --git a/DeeployTest/generateNetwork.py b/DeeployTest/generateNetwork.py index c160339d90..bf590f06b1 100644 --- a/DeeployTest/generateNetwork.py +++ b/DeeployTest/generateNetwork.py @@ -39,7 +39,7 @@ from testUtils.typeMapping import inferInputType, parseDataType from Deeploy.AbstractDataTypes import PointerClass -from Deeploy.CommonExtensions.DataTypes import int8_t +from Deeploy.CommonExtensions.DataTypes import IntegerDataTypes from Deeploy.CommonExtensions.OptimizationPasses.TopologyOptimizationPasses.DebugPasses import EmulateCMSISRequantPass from Deeploy.DeeployTypes import _NoVerbosity from Deeploy.Targets.CortexM.Platform import CMSISPlatform @@ -62,39 +62,38 @@ def generateNetwork(args): else: activations = None - tensors = graph.tensors() - # build {name, type} and {name, offset} maps manual_types = {} manual_offsets = {} for kv in args.input_type_map: try: name, tstr = kv.split('=', 1) - except ValueError: - raise ValueError(f"Invalid --input-type-map entry '{kv}'. Expected NAME=TYPE.") + except ValueError as exc: + raise ValueError(f"Invalid --input-type-map entry '{kv}'. Expected NAME=TYPE.") from exc name, tstr = name.strip(), tstr.strip() try: - manual_types[name] = PointerClass(parseDataType(tstr)) - except ValueError as e: - raise ValueError(f"Invalid --input-type-map entry '{kv}': {e}") + manual_types[name] = parseDataType(tstr) + except ValueError as exc: + raise ValueError(f"Invalid --input-type-map entry '{kv}': {exc}") from exc for kv in args.input_offset_map: try: name, ostr = kv.split('=', 1) - except ValueError: - raise ValueError(f"Invalid --input-offset-map entry '{kv}'. Expected NAME=OFFSET.") + except ValueError as exc: + raise ValueError(f"Invalid --input-offset-map entry '{kv}'. Expected NAME=OFFSET.") from exc name, ostr = name.strip(), ostr.strip() try: manual_offsets[name] = int(ostr) - except ValueError: - raise ValueError(f"Invalid --input-offset-map entry '{kv}': OFFSET must be an integer.") + except ValueError as exc: + raise ValueError(f"Invalid --input-offset-map entry '{kv}': OFFSET must be an integer.") from exc # Sanity check for unknown input names - npz_names = set(inputs.files) - bad_names = (set(manual_types) | set(manual_offsets)) - npz_names - if bad_names: - raise ValueError(f"Unknown input names in overrides: {bad_names}") - - manual_keys = set(manual_types) | set(manual_offsets) + manual_keys = set(manual_types) + assert manual_keys == set( + manual_offsets + ), f"Override inputs should have both type and offset specified. Inputs without both specified: {manual_keys ^ set(manual_types)}" + assert manual_keys <= set( + inputs.files + ), f"Unknown input names in overrides: {manual_keys - set(inputs.files)} (Valid names are: {set(inputs.files)})" if args.debug: test_inputs, test_outputs, graph = generateDebugConfig(inputs, outputs, activations, graph) @@ -114,15 +113,30 @@ def generateNetwork(args): for index, (name, num) in enumerate(zip(inputs.files, test_inputs)): if np.prod(num.shape) == 0: continue - defaultType = manual_types.get(name, PointerClass(int8_t)) - defaultOffset = manual_offsets.get(name, 0) - autoInfer = name not in manual_keys - - _type, offset = inferInputType(num, - signProp, - defaultType = defaultType, - defaultOffset = defaultOffset, - autoInfer = autoInfer)[0] + + if name in manual_keys: + _type = manual_types[name] + offset = manual_offsets[name] + + # Check if the provided values fit into the dereferenced type + vals = num.astype(np.int64) - offset + if not _type.checkPromotion(vals): + lo, hi = _type.typeMin, _type.typeMax + raise RuntimeError(f"Provided type '{_type.typeName}' with offset {offset} " + f"does not match input values in range [{vals.min()}, {vals.max()}] " + f"(expected range [{lo}, {hi}])") + + # Suggest a smaller fitting type if possible + fitting_types = [t for t in sorted(IntegerDataTypes, key = lambda x: x.typeWidth) if t.checkPromotion(vals)] + if fitting_types and fitting_types[0] is not _type: + print(f"WARNING: Data spans [{int(vals.min())}, {int(vals.max())}], " + f"which would fit in '{fitting_types[0].typeName}', " + f"but user forced '{_type.typeName}'.") + + _type = PointerClass(_type) + else: + _type, offset = inferInputType(num, signProp)[0] + inputTypes[f"input_{index}"] = _type inputOffsets[f"input_{index}"] = offset @@ -198,12 +212,14 @@ def generateNetwork(args): parser.add_argument('--input-type-map', nargs = '*', default = [], + type = str, help = '(Optional) mapping of input names to data types. ' 'If not specified, types are inferred from the input data. ' 'Example: --input-type-map input_0=int8_t input_1=float32_t ...') parser.add_argument('--input-offset-map', nargs = '*', default = [], + type = str, help = '(Optional) mapping of input names to offsets. ' 'If not specified, offsets are set to 0. ' 'Example: --input-offset-map input_0=0 input_1=128 ...') diff --git a/DeeployTest/testUtils/codeGenerate.py b/DeeployTest/testUtils/codeGenerate.py index 18a68e10da..5e572643a4 100644 --- a/DeeployTest/testUtils/codeGenerate.py +++ b/DeeployTest/testUtils/codeGenerate.py @@ -104,7 +104,7 @@ def generateTestInputsHeader(deployer: NetworkDeployer, print('Input:') for name in inputTypes.keys(): buf = deployer.ctxt.lookup(name) - print(f" - '{name}': Type: {buf._type.typeName}, Offset: {inputOffsets[name]}") + print(f" - '{name}': Type: {buf._type.referencedType.typeName}, Offset: {inputOffsets[name]}") return retStr @@ -172,10 +172,10 @@ def generateTestOutputsHeader(deployer: NetworkDeployer, if signProp: for (name, buf), (_, n_level), (_, signed) in zip(output_data_type.items(), output_n_levels.items(), output_signed.items()): - print(f" - '{name}': Type: {buf.typeName}, nLevels: {n_level}, Signed: {signed}") + print(f" - '{name}': Type: {buf.referencedType.typeName}, nLevels: {n_level}, Signed: {signed}") else: for (name, buf) in output_data_type.items(): - print(f" - '{name}': Type: {buf.typeName}") + print(f" - '{name}': Type: {buf.referencedType.typeName}") return retStr diff --git a/DeeployTest/testUtils/testRunner.py b/DeeployTest/testUtils/testRunner.py index d68856ae43..a3dc6a7189 100644 --- a/DeeployTest/testUtils/testRunner.py +++ b/DeeployTest/testUtils/testRunner.py @@ -180,12 +180,14 @@ def __init__(self, tiling_arguments: bool, description = None): self.add_argument('--input-type-map', nargs = '*', default = [], + type = str, help = '(Optional) mapping of input names to data types. ' 'If not specified, types are inferred from the input data. ' 'Example: --input-type-map input_0=int8_t input_1=float32_t ...') self.add_argument('--input-offset-map', nargs = '*', default = [], + type = str, help = '(Optional) mapping of input names to offsets. ' 'If not specified, offsets are set to 0. ' 'Example: --input-offset-map input_0=0 input_1=128 ...') diff --git a/DeeployTest/testUtils/typeMapping.py b/DeeployTest/testUtils/typeMapping.py index d8f24df0a7..a551b25150 100644 --- a/DeeployTest/testUtils/typeMapping.py +++ b/DeeployTest/testUtils/typeMapping.py @@ -24,7 +24,7 @@ # limitations under the License. from collections import namedtuple -from typing import List, Optional +from typing import List import numpy as np @@ -84,69 +84,63 @@ def dataWidth(n): return ret -def inferInputType(_input: np.ndarray, - signProp: Optional[bool] = None, - defaultType = PointerClass(int8_t), - defaultOffset = 0, - autoInfer: bool = True) -> List[offsetType]: +def inferInputType(values: np.ndarray, + signProp: bool = False, + defaultType = int8_t, + defaultOffset = 0) -> List[offsetType]: + """Infers the data type of the provided input array. - # WIESEP: We cannot do type inference for empty arrays. - if np.prod(_input.shape) == 0: - print(f"Warning: Empty input array for type inference for {_input}!") - return [(defaultType, defaultOffset)] + Parameters + ---------- + values : np.ndarray + The input array for which to infer the data type. - # If the caller provided a manual override, skip all inference. - if not autoInfer: - rawType = defaultType.referencedType - vals = (_input.astype(np.int64) - defaultOffset) - if not rawType.checkPromotion(vals): - lo, hi = rawType.typeMin, rawType.typeMax - raise RuntimeError(f"Provided type {rawType.typeName} with offset {defaultOffset} " - f"does not match input values in range [{vals.min()}, {vals.max()}] " - f"(expected range [{lo}, {hi}])") - - smallest = rawType - for t in sorted(IntegerDataTypes, key = lambda x: x.typeWidth): - if t.checkPromotion(vals): - smallest = t - break - if smallest is not rawType: - print(f"WARNING: Data spans [{int(vals.min())}, {int(vals.max())}], " - f"which would fit in {smallest.typeName}, " - f"but user forced {rawType.typeName}.") - return [(defaultType, defaultOffset)] + signProp : bool + Whether to consider signedness when inferring the data type. + + defaultType : type + The default data type to use if inference fails. + + defaultOffset : int + The default offset to use if inference fails. - if signProp is None: - signProp = False + Returns + ------- + List[offsetType] + A list of inferred data types and their corresponding offsets. + """ + + # WIESEP: We cannot do type inference for empty arrays. + if np.prod(values.shape) == 0: + print(f"Warning: Empty input array for type inference for {values}!") + return [(defaultType, defaultOffset)] signedPlatformTypes = [_type for _type in IntegerDataTypes if _type.typeMin < 0] matchingTypes = [] - # FIXME: this is okay for now (3 distinctions are fine), but there is implicit - # knowledge encoded in the order of the checks (i.e. first unsigned, signed - # and then float). It might be good to extract that implicit knowledge into an ordered list. - if signProp and isUnsigned(_input) and isInteger(_input): + # There is implicit knowledge encoded in the order of the checks (i.e. first unsigned, signed + # and then float). + if signProp and isUnsigned(values) and isInteger(values): for _type in sorted(signedPlatformTypes, key = lambda x: x.typeWidth): signPropOffset = (2**(_type.typeWidth - 1)) - if _type.checkPromotion(_input - signPropOffset): + if _type.checkPromotion(values - signPropOffset): matchingTypes.append(offsetType(PointerClass(_type), signPropOffset)) - elif isInteger(_input): + elif isInteger(values): sorted_types = sorted( IntegerDataTypes, key = lambda t: (t.typeWidth, t.typeMin < 0), ) - matchingTypes = [] for _type in sorted_types: - if _type.checkPromotion(_input): + if _type.checkPromotion(values): matchingTypes.append(offsetType(PointerClass(_type), 0)) else: for _type in sorted(FloatDataTypes, key = lambda x: x.typeWidth): - if _type.checkPromotion(_input): + if _type.checkPromotion(values): matchingTypes.append(offsetType(PointerClass(_type), 0)) - if matchingTypes == []: - raise Exception("Could not find a matching type!") + if not matchingTypes: + raise RuntimeError("Could not find a matching type!") return matchingTypes From 37d4ca2557c3b253b6865913fdcc04674ccbc685 Mon Sep 17 00:00:00 2001 From: Philip Wiese Date: Wed, 27 Aug 2025 16:47:35 +0200 Subject: [PATCH 15/15] Fix CI --- .github/workflows/CI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 4422d11507..4f2c5a4919 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1038,13 +1038,13 @@ jobs: - name: fail-input0 platform: Generic test: testTypeInferenceDifferentTypes - map: A=int8_t B=int8_t C=int8_t + type_map: A=int8_t B=int8_t C=int8_t offset_map: A=0 B=0 C=0 shouldFail: true - name: fail-input2 platform: Generic test: testTypeInferenceDifferentTypes - map: A=int16_t B=int8_t C=int16_t + type_map: A=int16_t B=int8_t C=int16_t offset_map: A=0 B=0 C=0 shouldFail: true - name: pass @@ -1069,5 +1069,5 @@ jobs: -t ./Tests/${{ matrix.test }} \ -v \ --input-type-map ${{ matrix.type_map }} \ - --input-type-offset ${{ matrix.offsetmap }} \ + --input-offset-map ${{ matrix.offset_map }} \ ${{ matrix.shouldFail && '--shouldFail' || '' }}