From b324b7e1fa60c1a1e68eccdd913a400399f71838 Mon Sep 17 00:00:00 2001 From: lucasdelimanogueira Date: Mon, 20 May 2024 16:42:45 -0300 Subject: [PATCH] broadcasted equal operation --- build/cpu.o | Bin 13776 -> 15032 bytes build/cuda.cu.o | Bin 134232 -> 144760 bytes build/tensor.o | Bin 48712 -> 50416 bytes norch/__pycache__/tensor.cpython-38.pyc | Bin 17692 -> 18194 bytes .../__pycache__/functions.cpython-38.pyc | Bin 9732 -> 9830 bytes norch/autograd/functions.py | 7 +- norch/csrc/cpu.cpp | 39 +++++++++++ norch/csrc/cpu.h | 1 + norch/csrc/cuda.cu | 61 ++++++++++++++++++ norch/csrc/cuda.h | 3 + norch/csrc/tensor.cpp | 48 ++++++++++++++ norch/csrc/tensor.h | 1 + norch/libtensor.so | Bin 181968 -> 194792 bytes norch/tensor.py | 60 +++++++++++++---- tests/test_operations.py | 15 +++++ 15 files changed, 221 insertions(+), 14 deletions(-) diff --git a/build/cpu.o b/build/cpu.o index 3299b62bd1e95cbf0cb55b237c31bd7fe046c036..64295093a522141da1cc4b87261cfd18857efdb7 100644 GIT binary patch delta 1050 zcmZ{hO=uHQ5Xaws&?I7{TVpp#H7^<}Noq=V-LyFcMFJj*RPZ2J6+thphXxGvAZ$_S zO^h885k&B26>L}!0X_8S!9zp^MLZ}TY(c06#e-1i<;{bz(1ADe<~Q^HZ}&}iuG4Jg z^-iT-Y}xHEhjgvJZ2W~$k}Ida}e-8L9w z^*w`2`ib~}v1IIyMA?2sn_zK%a8OjyP70^Ge9^;tU1d1Uxi$?A^z(2BeGA&?SK%$s zY46eVAd$Un`n_xLP!k1*n8ALeg(729GO+u^y%7m|7vlC6@$1AdQvWyNpNT&t9yO)@ zg7^ot?=rY-oC-4sB$<(cog&^L-XJ~))~KDDr)nW1v8vt*j*nh22C7#Y%}ZCIe0VH) zl+S9xTA^(CPi@(YN7+7H|F)&A?Z7_~FJkX-U;id?9a*H0|L)TfTcy7(xzEJK$-cs4 zdTS+g1Z%=R5EuO^TuSKc4m?M19oDhvafHqU!>>?6H~I?Ipj0}i`)ck9oS!hEC6=1I z7vgH}XF^=f{aT2tx!(Y4Hd258`7PSl(XfI>}v~QZHxI zCTuwq`c?+4>@Xa2^J=>&w<~Thwo`B=>|D|Kk3jhEB delta 749 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 DTdWP@ delta 24529 zcmaK!34B!5_4x07Gm}h0l9^2Q5Hd*!5FjBlNhTXxAnYL!WEW_Nf`WqVn}8D(m8z@{ zxS&u$q1uWX4Hl}PVAX01RxNEs@mFvwDkz{;K&bz7-n%zBdBgnghqv7CIrl7gdGF0+ z9+$s%ez?uKAy50z@j^Fm`{vhjMre9fqO_0lR7rW^feF1HGH@#_4pJsP;6W1Qj z(|2?YXk+pK1@sMtH|dQ7f6`8L)-P5Cw4I%SPHS!|jpbUmPDb1Bb6eZmIvQ<{SK&%c`?`wQi^VVafrsXKSUc%gVBKdHTM|S zfot9G!tIwsPr{^T&KM;y>#x<0gXJ3V7$to^|Qzx>i1U)R&F?;7=t zK733k{uh1zn6BDSf7f@8xwZM1G0VAF@CDD--y82}K0R)_O<#I*fj;x*%;vo}bFTkk zQhm4j`Vr&mYexKyPI2?slW(!>8H=0r3wJo1-M8+HH)Htjj;Q90#c#yx|9&tst;B3j z_Q&Ad#yaS~JeZQ!p@pZ)vu_M(re3%vB`we*RVNTJwDs!sAT3p}<+x5mX_meoq-8Bs zoh-7UG+TcSq~!{>9H-h*nxlWWCM8(WLe-0*+W5ahKH zV5LtqHs~9|A2ax{5Z<#L>URy{+YMe7!h6K9Aj^SC=9BZUN*))*FoVy9LB`^SjIg{3 z+{U_@6|&EbMc$7P^YjnEbAYS)t?XZuyiSw-?~@K{;?Pj{5JSH+c>fUI(}fQDw>YSi z39OQQs!SkD+J6tYjpeu6sgtKY+$gAS709zA1}_NVx$&qEOWRW3)^|UglGf9M(t#Ek zV^{U}LE4aj(m^58Gdh1H#qUo<>EIA)qrq!J_@}!6k(A)zBy2S#L>iHdyeNdvHu%sG ze%|24A-qEh+JSXzDQMZ_F46n0rI(OKSTR`6!f<%prTQF@wq7I}Xf0mu!=nHDnkc%ji86+8Y~KryPsKsz2d82&U)Ofb5`*Pm{J}$nQD-j z;r~ZNK<1Rp*bcMF<0i8S#u`gsm`5HrnMYDT9%hQi-8zF*54oY8$4%ylv{RD|1M6`Q zZ63Lv+eBfG=Fs2VG)ZsTv-O%Z`>M;x&T9k#jT&*QqL#o%fxu?NTkTl(Gv8%TkqsP zeblzwwQWxQZJMfIfaBapr|MH5b?c|$_#PbV9-FFH zJ?55?KIYXI?wH1%`pF%IJVuY&S;#x;t6(3aUx9tJKJPb$Lvd*B9i6PR5f7Yu$ay?D z2OW85FmzhZLB}}PcN1$l#9GTaXttb#wQQo(at=Dix&Hod%KBvzjh1uJXgLQf>fViU zx158Hajx^n3wbx)4||{90DG6d5%zBVDD2@;$=GM_s^<~Uu{rfKyHaK6bV2-SL_} z=(jiNzNeksqsQ(U$rJVXr^f61_qcho-tV^+`v3OyfN52*SLf~ZqkDtg3-opvWK)*8 z=_xnw0!?n$-A`45a;E;?Q%-#;oUhxRzG+VkFVJJ3e4H18{&>SnksiCdT3_+I7+zvX zN)1Wl?-PR<+CkW`Xs}^Ac^qqI9lK*2sbl9kQ4tCr@anUa)bXlwmelcTGXbu8SPSsy4wt4KncG=CB-)&52Ui#DN722$Laq~i68odwh&{%3y#G_CzP|rnm zBs;t($>3S4p$%gQ?YQ1BIU%afXCk*qTg1zvr^E*2qAJO&Kf8)MA{PY_GuWk zxh-lFQSC*uUqh^YDAhKL?WnfV?C%h@Svd2cG>Vg;mOkE`Ga+i{1`5woh&{)gGa>RZ zI6xA6cw+WU zc&@#X&gZxcpzIP8mqBt9;K=|=sHq`h(=tdd!h8}5k$;Y>2TGkdiDupmqgFChc<$z% z)Azd#?GQ63hKid&?idO)J<6Er<+$pg91#tD%O)Jf*7#rZqOls{<{^#+bDgEw*=~`m15ohm14TTapuJQ2id5?KiT1XuQ>W z_7YGZ1hf7|>g5xy>J6yg8D{^D)c4slK{J{S2t59fn17BkM;* z{5{ySFpaxD04w4~I=$*c0$vIcjDVGKBlUw*t?Ii`JsF;bZ=_ywi&gy$sHa^BquwOq zgBUQZsjlC^n!1t6{OP7ibw6gJVHucgg0*!c^*6*uG`R;&-V6)pMmkk9Op|0vfXS|d zVIAE_eg8~r1lf0kdJNt`Kw_Pr!|ycJTjys3sEehmcxn=xZOYUKC%l)Gi@mF0r8O=L z6;+5YP{dZ4BEub=VyjBg>0T7Al&vrlC@AVFrlY9w9)p-cN>MMd7erBw#^(@VV&c7U zBf5MqPwm#&7^)7>F)<2DDI4GrS6A`OoJN^9dW7ej5WRB_areDEGpIgRp?>UwBKSVLZ;^B(=gDVF`w1IG`aX9;EOw2 z8uyqrnm{#U-K_2N-o%VR;Vv1H=^?qKLma&x z+I~=I`yg8_7rE6y<1y0!onLS#i*A%y9fr$g9Mb-HX!{`{QvyaJU#{^G1Mf-$@{=mg zm7$DvY?N3Xd~(r;wEs+MkW~P;y0&u?x|E%6F<=?%&rJh#QrEyUW23aI4r^TE?)zcI zq&)!l`CT@CwckX%=CKmiN##0$Ej@M-^OCAMY)w>qLJpKQwqY#TY16Qkd z=oM~xu%hrOHc5Q=AkTt3I-Doxyw`*R#?(=@#O^Voy{;d9&?`o*;VIF1`GzX%oG<3D z;hnXvrw%;2hM&!z5%+xgoV%tksGl|S&gm0llH-b*t3D>e)suCOn;sL_lezAUN$pVN zu8-|%_XhIp5pf5ZD=Q{D?g(>rj@jV4drp+M=+`!f>$xR1=b9B!?7sBU`QE$*3->Q$ zA7~v*raWE4-q&1R=ZMqK@Pe4Ke`%w=zIU}a*SE7p+CJVjm=?`5O46cZ+uI8L#X~*q zT?2TkDB5;oJ8kFdjogX*dS3(ry5tp`UeG^>c6CADy zof0~v`1dW2`g3E%>v>wY6j$EXl#~T|TfP3STRYjdr?`&X6&DPMFZc1B;L`ayaX&NH zN68s+cJBI5Wc#=n?)ofpt+%4n{Bez;|3=qy8=dK8rytMq z)t7l)SLa3-mi0Wg-tP7P6?PTT>sjh;$+v2MT^})LfUSRaL2=K4Ui-Avn1SAi*I4w4 znL4MqUr~|2^vhIxdh!06 zm@iY;7nM~P6_->89-pS=6%}u~EozT1`Z-=cYx*MH7hm)ozq_QYdSFRe`Ms$*MZQZ} z5g8e_jof*u&>l6WYfr>no{ia$5te_?afq#{Suo~ZA+{Z)Hp_lSs#qqUbFicaTQ{y@%5 z#}pos8#SaTKdW?Zrv0LBuiYFQ;X(U-O#37`0`{M6v^SK*v}ZPFou~Bj zy2xuRp?DxKEcTxsr)Bz`PeKgkUyV-)yj~x9h9w7{8r9Wbd@t`wv@nc zH>LYuTbo=wOD_nNeat!q%0FgZ%csm@k;_I#&tkUcH+f5DF_v6%W@J+7yN)j9mBH~W zx76R8buIRdWr;;cS@eP}&MtadQT^V;ilHp8sOMDHu{ghsMR%H?TUs)er4`>@r`=-9 zEiHbc-Zi-gODcVnC&R`P@+RChXJL<99^Pd8Uk~T0^^Vu)Mem-#`$gIocHrGJ`TR`@ z{>Qp_%WRx!53|(3{w|#=N{Ee|{G0R$4Z!OLF;g*0Ie0bV1~c@$BvV z`znh3WxlfVvh~i0w;Y|y^PgFmUsm)DORSi~ZG)Z8uN&GICFFJX`QGcob6COL3az{L zf6TFVQWw8(OJqjzx=FSl7NnQI$|Cc5QotRY7PV)7m$K5^SmZb@rTlq4YTmTmii!`o zqo?|1@0qr; z9eg#}u4o#&g{7BX&DKtKT-L9Dsjt-E@BSREu4GnOb@iY?^V zPOu)Om8WvnR{P4!iz^29ygx?-UgRBv&RlQc-#Hp6-^`+_a|?@0BXXT7CGi!uhvv46 zMSa`qDOH>Wgtdp$>qJ6W1^#Pk*~7-T&H82*3Pf7 z-^Ik~6>;5jV`j}wEZbCJTM09$@?@8YSGCyh?An1T$>pzVk;9|A6_u2(WtslKuOhBi zIB%(TJ;iu>+2VP&r{Su9BG-`wSLnDakJ)$Z@cKGsX}J|sPF2{ZG1uMsBDR@#xXs_- z8RRW$@ML@8EIV!~cQts@;#T1y-qnYt6c$Z!?O6#cy20}dsubX^6Yk=0_dncq@s>1r zB5+sgEpG5+CWAfXN#3#sPi%7F_bbGPW}a2f?|vip%2QyOIR<-T8$7lX@lY8N)tr+b zfOhF66MhSII~vNwU8FbA;3>=Si?a}EaM`pNFk1l%w#50F6|e?#J>CGW!N9nAu=4J= zMT5&C?tw!}vCk88;wf9wd<+O~>AhS-J*-1%gC{j`5?v>jyoSDc2XK=Cq1x`>9R=3Q z8$9hXXh)aQ{L6cgU7#qZ-}b}Dus~&DS;=%yRA$9==oAon1JgY*j$Nz8FZ+4EHf*mbcnNMq zoAwq~l+A#isdBnzc;X^{ywf(rquCQ88$F3-(>$)A$B|hU1PejWm?YTY$?!~hvbeLq zet8V&%toApyW$MkISy;Cd$8d>pns2N1n%nC{jR3U==swZ_KaQ`9Su*+FI3+$=)j4W z_%xd%3!ZeQiQ|9d9r+qOcpVu)28d_jlpiw%7tBMVxCk%;odem zKoG{p!_c@%ynwa2;w03x%MoJOA>NrE6xpwWuuAlM64=GRG8}?N;XexYcSRq#=_nt>we{KJ(bxH} z(tno?LMWZh1M|ZJt_IKBf6Er0Kk;?i%e4odfFtJ*3jdohTmK3VzZ{-`<>RsOxQ$KS zq5;MDl`)EsNOS73d9Bz8;>y2B@p~5WRp99LrXi-l$O2AtpgrRF^YEg1_@j9^Vjli%9MV1n;~p^nl?+A&b-~oS zVrnt?$cb8ilpl<;jGdDaj+cC3 z7gOJ79@rAePs_nk8Q~un(EfLMrlQK>Ws}2)PdLG%!o#nOu|aZoldFKT;X^X=_V7^= zi*kV9kRks}eqK@zR!ELNQNWTT6L>;$y$4e8ul~mN7b%$HMnP+X3z83!sVe={7}IeO zzHg%XeI&mQk3U$H{%w-)hC2fmBLVQw)=9w&#Rg|8g9DNuO)&WgnZd`BKP451nPR5F zr+DlMSCR=Dq<#;{-@re&KpABo*sp*GvB6xJL0D85t(FZo;NRb%s0{W=zA44zO8*1N z2g*NYD*daH@0WkuNtFSm!Vnrm)(iU|ikd{_QgAHIbfCs~lH_ag4|q^i4+SMZmu_<9 z;7Q3lSD9Sde@AjVUc8{Ba??NiRtjG3X9`qEKUdFo13@RkQwc6B|Bgdvn zJ~-3lDj@tl4BELmpsoEq)1;u@+qS{$lCK-ow!x>8FUe}FkKd7@gPb94^|OE*7Y9eB zI_pp)bML}7dC3$XF)049E zrsOlbw~ZhMe`&#NL6%xz3YJR;!%YD@-lHvlMDlxmZTT_D%Zl1Mh=Zksfqp3m?5MO~ zC;3HVX2GQRyAdrs)t5Po`8RnhN60CgXFKvnQiFf zq&zMKn`%m7}d0^eYxjW*( zl`r^PTpali&rQSpul-_{d(4pVyI9soT>lR*6xaU4V?_B`*yY16_FbaUPfR%r-`7tD z&ZET{Jl_WAY1nhh;vm@@XmNlze3lpTzT!Npp2KEa#VS0{`-p!$rJI}4T{;x1>ac2 zqK$ZpRs4@O;ac}Vyf(z;d@d<#7PTbQ>05ZQP> z1J2uIS_)5iSj^sE3p#CLT|&e|#99Xz zX-$G>9V}?1x9AOG(dz>E-qD87Zs8xb;X7M6J_BJfyXrW)p~E_dh=+*lD8@e^t0FRuwYpI#d#FdupT1f zO3~pGi0ShB7ZFbqwJ4^u?K>i_5i2hlv+Y^bqCM<^CkiYWLaq1&#NyN?h&(`ee-Zn= zCA4RVxKi{+F%9hs5l<1bQA|UNhX)BP=(I-c{N4!dIB1C---GWGBCZwJkzYeTp78#n z;0H@g$BDR7OhGY?sXaV(U_qz#Vl9ekOvOZ8BM$#y#B?6C#GxO+_c$W16&?NyJmbH> zml57y)B?zIEraZi-@>JT>sICso*E@eGPqYBjQ>y z1^MKkfWJa`f3X&Lo0uMk=N&8<6I`rNJRMHe3VfA_;bMhW&w*9pzHGTzeMc1GVs#l5 zhhGNwQSe-Y1?~42D}lFh{{;~b5znIdcFuaDUPF<4#*%Ph5@-^4#-vTeGPrjBw`p4 z2J^$m;!7GG~0Gle={}E0*XPo@hZ;TYtpA&`)!fEFm zG`ZOc3y7h^qyb$F?-5Qr=Of9jo%1=-r=9aH;luH(6P5AzL{LitikDwoV8*nEk_o3h zw2$ygV&??mEj^RDb4>=(tz^lY-SWp zhvwg%@Z}&=>H7$$LtRaH4IACspjHa3(|kN>KzpcxaN0w+2|sr1Y9gi`@@v9rhdd!U zy2l)2V8gQC!sR0rI}(P!ZE~|?SAYT4A^(vL(D+KiXXZc!N;lYs!lv}+O0EKumofID zhFqvXLHjVCraoQ(g%qd^ zWR;ziCw5S#8FcIf07@G}VCUvAe4*rMhbFKFINCMFk!J8u(x4IKDzm|zQQLGp7LJOK zkX$8@Z)h>LL2_#XAGO*U$EczBE@n&2jPmZ1TQlfOI8ER#!JRO-FA{U(NxYXOSMip} zcq{YF6KlMyCC9L+omax}f0*2y+qB2OA`WQ09lEwUz86ka$EKo3D0r9%o8n%{RRXNp4NxC&Fn0z9KUM)PE4?pPEF?Qh<(cg-!97C07|tGuyLk#1PJ6r9Y_HRIu7v zc|#tATZ@pQtQBv%Q{GUH>HkYnWNm}bx`U}{(mEeONc zN^VVHH{mpauL%$S8hoj)d#kk7@$G~kmt1AA&{SitGV{cmz(mP00cvMMD2M)s@_S;4 zuH#n;rx`?-w>o|ZPE`V%BuB?H3EwWcN&ugwuzXyO{SL*NL8l6nTMbPP!xu|#9fF4l zrwRN?arFN%Y$}7n_?!#{9p4F?;v*zi8LUPnC>tcVCh$=hJMlfucFKNGIp{70)(rX* zPBU0a_#A9Xf|TA)F=<)7$iK zjQv_TR~f7~1m+OTA$+UkDud0~9?FN3TNB9XW6CiBYG)$lbloqI0_zYwNI1>l6~fnn z6O}+--&V)-2rraeC9nfz=%3A#0&51ZgfVm}43DWoYvyXALy%Qv;E-qNkyWODL$Drn zR0fA+1MIp5g#Sr$mBAj6VR7^`kJbc6N^Z6DKnsUVsDoX^5Y1pe;dBT-6RSM%5Ir3k z6!fzQHkHRL$yL1jPzlO&l3U}w9L7#zwb{;Uf2herAEKwo2Iv57s({uLzLD^2#K986 zZ3CbJMJ2G`tg?ZUn+d>$s>Oj6V5HR0TVeQVVu*J54}{YMItrc$9ll|p83?*r2Ags( zBv|dJq?#)Lx$D`3NN!s0;jkBI|15k3p!AY!O#4--TCJIpGJZNodZ z3KFHjI-xT}zho_CXx}#N7*hi{C=9P9=IE7TtZ8nkrP(IKiKAT;kYmkmRSr|OhE_`le+Fnz6j&MSqn-~)DWze3An=WWaPO1 zu!}~+rn)FoHn5IyKZ~J5(!r20e5B-PAFtZV{&-?PNbl71h@nK%U@>X1mGG5BAA`j< zSk{N(kB9OIqrp?+Xo{B7shRlM1_BkZ9iPgeppWgsk)stS-6B;D4m-SLjuf>y+7dqa zz?(T*J6kYUb`OpRZt92Y1%yWf{|PoMLkLHG^}F>M7OsAuK96v;6Cp3#kC@!Nv!NmN z`}8fA21&9(6XB@8Bnx&>{zy1F$d&ph2uJ<(*{BUKr?lZPv(&3ZiP&*aP#L$s$p$4B zu71?sn{aeoEFDiK9POxgYgSme@zZyl+4mCx8mg9t_7IMS)C)9kSh)JF{Bgq3&S+`p z9N}n3y;u|DAa^zts2|rSfdC6KG<2&pR6;n))EhTrEL{B#*#xx!k;-h6cj{@V%*S8oRd z2uDLt%7DfYjsdARdKOx^dYfQnkOj!TVj&}Ye?R-u+ z>Z><^?2%A`5>yV<`wFpAfE*3|T^hW=Sd@5)PjO~xT$woIuVYi z_enzo2uGw|lA31W>P6Ajif2m)>Q22!2_%R2SU)Pz9^&vzm+6ICUp`puE!2v&xDn!` zLaj77AB2R^M&^e%}% zwTX1URvLBhP2dlTE++c5az0jU@@svg@Oci(+}`3NP|~(-5}qQhk8Q>kE=Ct=y|t!I zViST!0Dnht5HyMbY(tG=t#{OsO=f(##(xPb_7!Wnw$C@2|80#C-R9Y{B7v$CMpC1Sm2ZDjF zsIm!5zf*Lj2u_n%yH5LDoc*xR2G?ho>+`MlxjFY?pEIt{RoCa9_6f~@*e6WEgb0?& zt39_sHiMu`@xYvE|^{l*3`cwO-7b*VlxM?fB=-?dbkf)yHTd6R^Gt)`P%t#x~%? z!2b{6g~0E)8QQZL@sNcT@yirEk)mG-d%m`M^RK5ATN^-&`LCxh+1lu%-R5eWeY9Ul zt#d)Vv=TjNKj!9J@Um_zKpT_S7n#dq`81 z`i8rzW!_D`o4VP$7lPs@KTN^nBGCcXI+(HtIQI5BaO|x;kHtJ>so2}6>GpA9SArEA zxyrR})K+i)+o?1y?46*+{P$3dt^0knZLaovAMJUmbw*UWDc}D_Hf?)Sb8p)!fn(ch zfv@C{rfn(UxQ0!N=N{7CRFn~RTFcCCGT~Y&nh{PH1jYT^13Upq^a95u?g7U}j@ADd zyp5c$c;u~BE%Gomvo#A^EV9kjcKc|5ky_`nk9Pcm|L^t- zntQul2OPVd0{$8-bd%z_hjcHcTf~C2mU%b%InBKb@iR<=+x{ZOT1283tPPMvuu2sv zi@|P}0LN}u0>@+f5?yKGUqG;8Yj?QT1KL{7bK!rnb}Bj{>@Lt^{y$Nat=&G_>#p{O zkM@qMUA9owXjaS|+XQu=5aT}9R=EP$woie-&Ng$Zj?(E?5j;)fT7})CeY_R*Qi832 zK?JPmA8KwD&NGWtR&w4qApSJqYk)5Sz83g0&AmBn(A-o#M^40}QE_!HF)arsz%%q2 z@Gu8>un@Of#e%C~#SL*2_&`t+#l2q)MH94Dw+tjaTqn!UDEbJ+y^Wq$|wh=B88b+>>a#v+r@9^lxG z3~*fUGn#u>^jFPIqHcVLADS*hpg}NkFL12*U*Jo@e`qz|@Q@W_qGy4xW1A`L5_7I! z3Ggc?v_w+Hv9eJvX5@eCbeazU delta 3615 zcmZ`+Z%kEX9KPoO;iag6@Nkx!u>3&O)~+;hIS2?6kgfu)^h%7Iz%**g(v1(&1#@m; ztJp7^rcSM`4~zo3CIS8Nav5SD8no8T%FVXc`p`mV^@VA__uqIpxBG7IIh^PDJ@5Pb z_a5$9d?RIU*?y+e%`g0+Cq8OY3d2Bx4wv; z32j;9cWj>=r)ca&$NE}a-CxsvzHFc68!cCyvAHE}T&mdPxTR~9&E?9~HZ_8^I;73( z+U9!IYMZyD&9Jn&qHX?uTG+xpX^|Gb=S^Xk#N_BWQ+&Xg%Wm2da+T)v~mJmB{Lz65v{;F+{A?6?I_ zGQ^{ZXC_ax+Geso=3Hr3>~$}&TPeV7Fp0KZYF|pCJt(x1SrwD$K9;xgdn$51GCR+ z)*Dg_^)cI?M0;3j`;usn3vJ{V&6*^dYX1f*thT>Wh9RTCYOe!4#3rT~#eid4>lCii zfq81CiV??sRm*H%N;IKauA90>oX8Pa^gdt*0pAVyFyQD!3~;RN65v?d+X}BtXwm$q zcsCy2+HL_HgWOI7qmJ95S(}v@WGBU#Jp@_|^03rW67310jeL+qd&{FT)m{dBtag5t z3@f49#el!SCZ;@f3RhLdOoypqjH{|;MJ&NrT0f03`!+0!ue6hNW6TK;1KR>y#sSA2 zuL6#>^;fTigtg73g-;!~1Xw(zjgsxqtk?e@vZ9WA1hnY?n-pM{l4ysdb|i^*LTDp* z>wjdXQB2u#G@i;KVcALm$3t2Ncq`;E3^?YnTj7a^w4ZKrNawZ8xMjNFBPzJyL`Gpz z?B6)xVQ}Ia;ON9(fMX%^x3ZXr2p$XhsKOHuX%GZh+kJrJA?=}x&m8w%&3c18NnOl- z2wDtsNNQtAw6j7RnNOnKSR>=|nH^2FpU}9s+9ANP+OGhk7?GpZmRJ( zEihXKTI{20sclZ84NGlj67542o^aepwyk)D?}%+Qz-$Y!4QyzCQExqEPj9kkgVYsUORdv|=FfBd=UkA!nJ%-dnnVrR5c3JjA1VmV|h4rf%*3F7`QC#)`S|zD9sNjO#g)*=oTt?Ak zwzdJ~!!P8BnrK?EO^i+2v>9I-W1?@qYJ6z6@xjDC_N{&K!StR16`Rh(@1FagbI$*M zw+~sme;=6gLqyOo-U)}RAD4IXG24pB=Y56v2!;j1Vfp&bd$!8|K8Itb> zqR@+f1#Ww~MIrAY_y|e-ZgomMPT^9)+KT%O68L*%qrIr41~c1>roE~OJa_FpB(jRO zoc?8+^C4`n-VB;KR(%g(7~S=oy%}nYz49 z!Vn=(2orV?tYzuuk)R21f*Lzdl1z;`xAxRkUoAVWdaEEzB3}9MqRS9DA+~I8sj-Gb zYJn=P&EZcCoy+G^-uR2GP!H^@4q$K8VZPk-Bfyfu?JZs4JWFLOT-SV*V9Wpcb_1MK z@Bhcrrh~Y@q5{X74mA!))6%5W4=FjNOq3Lu!3;^W8=%?8SvQ-O3bG;30(ao^rd=J1 z4&ze6W+;X&SvDmtcoroQ+4b@%X&gl46}3Jw`cgDwhE21_r5~1Ieig_4Nta%-uu69CGO>oTMOJvvlh*deRm9$vo?$@McS zWZTb~*mVLA)c&?%#kJB&Xawu-{1nm6UT3(cZmsKsgX!#Oj&Dtmoah_IOjRWd1+XjJ zqOMs|9@Is)94WQpq;941?AvBmTdU)Fp-l+;(N=sB4y;q;(lReoSrvGg7(oRGI9Q1_ zdS&xfI0o?8+!?VmXHn@pH8{#Ii3ZEJ?S?UST8Sqw(}w#;BQC}TbvGoS4a0&A18IkM3V#zfq;6lYt02FPH| z-ap|CK8)4k`qnf|;#6zzx=+Zq{eQea71~Wy_YI0ioZlBYvxjo_650qI1j^x^1nV7t zLD2|dlrTm(MWE-wzao51_=Ye+I7gTyOcQ1Zblm&`VU9p&z)`9Zyh`Cb;hI3PB+qmC XG}lk_>)0AU260@B*TM~a6hHbe$)6|Z delta 984 zcmZva+fP$L5XR4JOG|pQrBt8=xf!5>0W1`PP(VvXNYDf|P?d|eg>ow_D4>;#;0*!A z3Gu~5B*a9cMsrBi_*d{?Vh9gB=%a=g-i*3)ATbh8a(?se>^D2Rn|(A7PiDY$)@am8 z;#-`q4=qi+Gc6jyFRvIoRNzO0r2xEGVcCXST+6k}zPMgWui8rCB7V0mo0~-;_Yu$3%uYgPe!3&XET}dDyWE z(1E7RJXt+UA~ht$@J|seTfi2Yg)D@B%~j3tvDH5>^cmkeNLqU zPI=x%C?XUSl&$v3{&X22Le2F3l%{69M>%IxrbDGy>Xm{bO5#G+objsgIN9!ol&mNF zoMxCRl>_7J?9;n%Ras#^DvW6AtX5?U*?MMR5; zaU#ZvNFMPwN={V+XQG+UX+@j6Qey}XTxsvY0bK%arp4g;5nV|S?M1mU1@SbzB`_`O z26KdvSUfX;j_H^Q41#JgQZsqW?E!c$=a*_(tY6(e5+3At#bso%j|^u$9(kqA2XF^} zRoLMkYRa4KGqm!aMwdlygZ!29hw_@g{v=N`%wT$Td`xe+?>CcYJ*~hpRaj7ZNKU9q z2ja|+emd2dIO-vI}5Z>8sx7&qQ+64;OLMeZ(kccE21kr-xFCrlRfl(Wa5fs!Z5`UwK9=w=n z#)F9mR4$l!&@}Pj!T3isCMG5(9vWlf#gkt2;?bFHL3+?8@7wq0`{upQo0)gD=Ub0w zm&ao&{JTE0?Z~gkp6yO}kREmhRbL$&t9hgx3zJh6NA&2j*)y&gm zp1L`G!#4(2D;uZsh*sXFn8G#7sn6OK2r>7-fiC`_Ay6R@vIF{PeTF7_RNMzO1SMx- zC7my6fDp};oP|<`_ASi@ z;#MCoZ}XVHB@O%R4cg02=mR3%Y47r;>~O(ydnqtryZ4uOS^d16<}vT?#D3fTTlu=& z`5hJatwC1IE>dYYXscS5e&YDt!K2RI%s!$Yp&c|AjORQ3KRgA`=xw-n_w1MCBIuLaIMTc1*B)NK%k4^3%#iBxHO1Co}W`q^PCos z$75D5jiOpFAVrhW#uH~U&XH566UT9-ST_hrnd3%*O#)#7DLULNuti|2fL$FuJj*yw z;^@R=%*Ym{S5d|l+|I<1n-4fRl{kKSB;y!Qq>iEV2ksEcT%c0f2<%wwFqSa?fk!IJ zpg<9r;DsVJtOgyrTB;emGQqEU`D;Ka6w>p^YHL4B--(mt!2mAo{ke>tS(GjKMNewx zMw(a?EBwcua3LIE{{J@;(-(qWh;?)#8!H|X_g*O`_)WiSw_ZxH;U|y3e@-l%l~tlt z1&Rgap+kAOP#zi_@% delta 1275 zcmaJ>O-vI}5bo=?ODVKawy=O(+EV@&j75SXe_Dzt|4;}9XoL`~5e3v~Xds&4ff$eI zcrfu`q8GfOy?IoL#>9k##6#69H@$fB=FGMP8l#)cx9`n-GrRNi-u~dmpku<}u$$(-)%Y*IG@#J1kQpg_CgCG4i1)t^ihyd=Y zrNz~xtYa@Z<7Jbfn*2gIW}g?e#8&aMJxVKYj)pyXVOI?iJJ}=i9SauP!^u=$R45#% z=ZHyyIu4b5;Aj)Vi|?EpN$*@{NSVkov``o~-t`7?RGCmUvc0rG4!yv}P|lzk>Fg=rL<4TSM@1!y;x*BXcZzF; zhA)b5i4eL=W|TG}-y_MSXtzQe=YcyVJ>rZJqf(tLY9)p)K|j5~%AguG+EPQx2yLH` z9MnjH5hFl*=}kGp)G;H~lb7h_veC{iip)-|474gyqI@Db zoU>@AfQ|S%(0ina;wP3??Zg_0tmpqDdFst&vj0DIUH&_{h2?Z|FvIRc++Xnvr>pzd zXG!sc@I3(a419or4=eDQ0zMbO(+xbl!1D<_c)+6s VTn^w5q>E}d;a4>ugEgl`;164q?|=XR diff --git a/norch/autograd/functions.py b/norch/autograd/functions.py index e5b3c20..daa0668 100644 --- a/norch/autograd/functions.py +++ b/norch/autograd/functions.py @@ -216,6 +216,7 @@ class MaxBackward: grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] else: + if not self.keepdim: # Remove dimensions of size 1 from the gradient tensor. input_shape = [s for i, s in enumerate(input_shape) if i != self.axis] @@ -225,8 +226,12 @@ class MaxBackward: grad_output_shape.insert(self.axis, 1) grad_output = gradient.reshape(grad_output_shape) grad_output = grad_output + self.input[0].zeros_like() - + + print(self.input[0]) max_value = self.input[0].max() + print(max_value) + max_values = self.input[0].max(axis=self.axis, keepdim=True) + print('\n\n', max_values, '@@@') mask = self.input[0].equal(max_value) grad_output = (grad_output * mask) / mask.sum().tensor.contents.data[0] diff --git a/norch/csrc/cpu.cpp b/norch/csrc/cpu.cpp index 6d65273..9584c23 100644 --- a/norch/csrc/cpu.cpp +++ b/norch/csrc/cpu.cpp @@ -306,6 +306,45 @@ void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data) { } } +void equal_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + // Calculate strides for broadcasting + int* strides1 = (int*)malloc(max_ndim * sizeof(int)); + int* strides2 = (int*)malloc(max_ndim * sizeof(int)); + if (strides1 == NULL || strides2 == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + int stride1 = 1, stride2 = 1; + for (int i = max_ndim - 1; i >= 0; i--) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; + strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; + strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; + } + + // Perform element-wise equal with broadcasting + for (int i = 0; i < broadcasted_size; i++) { + int index1 = 0, index2 = 0; + int linear_index = i; + for (int j = max_ndim - 1; j >= 0; j--) { + int pos = linear_index % broadcasted_shape[j]; + linear_index /= broadcasted_shape[j]; + if (strides1[j] != 0) index1 += pos * strides1[j]; + if (strides2[j] != 0) index2 += pos * strides2[j]; + } + result_data[i] = (tensor1->data[index1] == tensor2->data[index2]) ? 1.0f : 0.0f; + } + + // Free strides + free(strides1); + free(strides2); +} + void ones_like_tensor_cpu(Tensor* tensor, float* result_data) { diff --git a/norch/csrc/cpu.h b/norch/csrc/cpu.h index b006ca7..83bd83f 100644 --- a/norch/csrc/cpu.h +++ b/norch/csrc/cpu.h @@ -22,6 +22,7 @@ void tensor_pow_scalar_cpu(Tensor* tensor, float exponent, float* result_data); void log_tensor_cpu(Tensor* tensor, float* result_data); void scalar_mul_tensor_cpu(Tensor* tensor, float scalar, float* result_data); void equal_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data); +void equal_broadcasted_tensor_cpu(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); void ones_like_tensor_cpu(Tensor* tensor, float* result_data); void zeros_like_tensor_cpu(Tensor* tensor, float* result_data); void transpose_1D_tensor_cpu(Tensor* tensor, float* result_data); diff --git a/norch/csrc/cuda.cu b/norch/csrc/cuda.cu index 5726c69..7d056ab 100644 --- a/norch/csrc/cuda.cu +++ b/norch/csrc/cuda.cu @@ -564,6 +564,67 @@ __host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_ cudaDeviceSynchronize(); } +__global__ void equal_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size) { + int i = blockIdx.x * blockDim.x + threadIdx.x; + if (i >= size) return; + + int index1 = 0, index2 = 0; + int linear_index = i; + for (int j = max_ndim - 1; j >= 0; j--) { + int pos = linear_index % broadcasted_shape[j]; + linear_index /= broadcasted_shape[j]; + if (strides1[j] != 0) index1 += pos * strides1[j]; + if (strides2[j] != 0) index2 += pos * strides2[j]; + } + result_data[i] = (data1[index1] == data2[index2]) ? 1.0f : 0.0f; +} + +__host__ void equal_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size) { + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + int* strides1 = (int*)malloc(max_ndim * sizeof(int)); + int* strides2 = (int*)malloc(max_ndim * sizeof(int)); + if (strides1 == NULL || strides2 == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + int stride1 = 1, stride2 = 1; + for (int i = max_ndim - 1; i >= 0; i--) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - max_ndim + i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - max_ndim + i] : 1; + strides1[i] = dim1 == broadcasted_shape[i] ? stride1 : 0; + strides2[i] = dim2 == broadcasted_shape[i] ? stride2 : 0; + stride1 *= (dim1 == broadcasted_shape[i]) ? dim1 : 1; + stride2 *= (dim2 == broadcasted_shape[i]) ? dim2 : 1; + } + + int* d_broadcasted_shape; + int* d_strides1; + int* d_strides2; + + cudaMalloc((void**)&d_broadcasted_shape, max_ndim * sizeof(int)); + cudaMemcpy(d_broadcasted_shape, broadcasted_shape, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + cudaMalloc((void**)&d_strides1, max_ndim * sizeof(int)); + cudaMemcpy(d_strides1, strides1, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + cudaMalloc((void**)&d_strides2, max_ndim * sizeof(int)); + cudaMemcpy(d_strides2, strides2, max_ndim * sizeof(int), cudaMemcpyHostToDevice); + + int number_of_blocks = (broadcasted_size + THREADS_PER_BLOCK - 1) / THREADS_PER_BLOCK; + equal_broadcasted_tensor_cuda_kernel<<>>(tensor1->data, tensor2->data, result_data, d_broadcasted_shape, d_strides1, d_strides2, max_ndim, broadcasted_size); + + cudaError_t error = cudaGetLastError(); + if (error != cudaSuccess) { + printf("CUDA error: %s\n", cudaGetErrorString(error)); + exit(-1); + } + + cudaDeviceSynchronize(); + cudaFree(d_broadcasted_shape); +} + __global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size) { diff --git a/norch/csrc/cuda.h b/norch/csrc/cuda.h index 712e600..73eb831 100644 --- a/norch/csrc/cuda.h +++ b/norch/csrc/cuda.h @@ -52,6 +52,9 @@ __global__ void equal_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int size); __host__ void equal_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data); + __global__ void equal_broadcasted_tensor_cuda_kernel(float* data1, float* data2, float* result_data, int* broadcasted_shape, int* strides1, int*strides2, int max_ndim, int size); + __host__ void equal_broadcasted_tensor_cuda(Tensor* tensor1, Tensor* tensor2, float* result_data, int* broadcasted_shape, int broadcasted_size); + __global__ void ones_like_tensor_cuda_kernel(float* data, float* result_data, int size); __host__ void ones_like_tensor_cuda(Tensor* tensor, float* result_data); diff --git a/norch/csrc/tensor.cpp b/norch/csrc/tensor.cpp index 8564b2a..570aefa 100644 --- a/norch/csrc/tensor.cpp +++ b/norch/csrc/tensor.cpp @@ -1084,6 +1084,54 @@ extern "C" { } } + Tensor* equal_broadcasted_tensor(Tensor* tensor1, Tensor* tensor2) { + + if (strcmp(tensor1->device, tensor2->device) != 0) { + fprintf(stderr, "Tensors must be on the same device: %s and %s\n", tensor1->device, tensor2->device); + exit(1); + } + + int max_ndim = tensor1->ndim > tensor2->ndim ? tensor1->ndim : tensor2->ndim; + + // Determine the broadcasted shape + int* broadcasted_shape = (int*)malloc(max_ndim * sizeof(int)); + if (broadcasted_shape == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + for (int i = 0; i < max_ndim; i++) { + int dim1 = i < tensor1->ndim ? tensor1->shape[tensor1->ndim - 1 - i] : 1; + int dim2 = i < tensor2->ndim ? tensor2->shape[tensor2->ndim - 1 - i] : 1; + if (dim1 != dim2 && dim1 != 1 && dim2 != 1) { + fprintf(stderr, "Shapes are not compatible for broadcasting\n"); + exit(1); + } + broadcasted_shape[max_ndim - 1 - i] = dim1 > dim2 ? dim1 : dim2; + } + + int broadcasted_size = 1; + for (int i = 0; i < max_ndim; i++) { + broadcasted_size *= broadcasted_shape[i]; + } + + if (strcmp(tensor1->device, "cuda") == 0) { + float* result_data; + cudaMalloc((void **)&result_data, broadcasted_size * sizeof(float)); + equal_broadcasted_tensor_cuda(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); + return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + } + else { + float* result_data = (float*)malloc(broadcasted_size * sizeof(float)); + if (result_data == NULL) { + fprintf(stderr, "Memory allocation failed\n"); + exit(1); + } + + equal_broadcasted_tensor_cpu(tensor1, tensor2, result_data, broadcasted_shape, broadcasted_size); + return create_tensor(result_data, broadcasted_shape, max_ndim, tensor1->device); + } + } + Tensor* ones_like_tensor(Tensor* tensor) { char* device = (char*)malloc(strlen(tensor->device) + 1); diff --git a/norch/csrc/tensor.h b/norch/csrc/tensor.h index bdcf25d..5669d4f 100644 --- a/norch/csrc/tensor.h +++ b/norch/csrc/tensor.h @@ -31,6 +31,7 @@ extern "C" { Tensor* scalar_pow_tensor(float base, Tensor* tensor); Tensor* log_tensor(Tensor* tensor); Tensor* equal_tensor(Tensor* tensor1, Tensor* tensor2); + Tensor* equal_broadcasted_tensor(Tensor* tensor1, Tensor* tensor2); void to_device(Tensor* tensor, char* device); Tensor* ones_like_tensor(Tensor* tensor); Tensor* zeros_like_tensor(Tensor* tensor); diff --git a/norch/libtensor.so b/norch/libtensor.so index 90f234627e5a99a18bef507126d80646332a50f1..d1b1cd0abda25d6f7180b68a7f9c04a092883ca5 100755 GIT binary patch delta 46385 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 diff --git a/norch/tensor.py b/norch/tensor.py index e8963e8..22cc111 100644 --- a/norch/tensor.py +++ b/norch/tensor.py @@ -656,25 +656,59 @@ class Tensor: return self.equal(other) else: return False - - if self.shape != other.shape: - return False - Tensor._C.equal_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] - Tensor._C.equal_tensor.restype = ctypes.POINTER(CTensor) + broadcasted_shape_add = [] - result_tensor_ptr = Tensor._C.equal_tensor(self.tensor, other.tensor) + # Function to determine if broadcasting is needed and get the broadcasted shape + def broadcast_shape(shape1, shape2): + if shape1 == shape2: + return shape1, False + + max_len = max(len(shape1), len(shape2)) + shape1 = [1] * (max_len - len(shape1)) + shape1 + shape2 = [1] * (max_len - len(shape2)) + shape2 - result_data = Tensor() - result_data.tensor = result_tensor_ptr - result_data.shape = self.shape.copy() - result_data.ndim = self.ndim - result_data.device = self.device - result_data.numel = self.numel + + for dim1, dim2 in zip(shape1, shape2): + if dim1 != dim2 and dim1 != 1 and dim2 != 1: + raise ValueError("Shapes are not compatible for broadcasting") + broadcasted_shape_add.append(max(dim1, dim2)) + return broadcasted_shape_add, True + + broadcasted_shape_add, needs_broadcasting = broadcast_shape(self.shape, other.shape) + if needs_broadcasting: + # Call equal_broadcasted_tensor if broadcasting is needed + Tensor._C.equal_broadcasted_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.equal_broadcasted_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.equal_broadcasted_tensor(self.tensor, other.tensor) + + result_data = Tensor() + result_data.tensor = result_tensor_ptr + result_data.shape = broadcasted_shape_add.copy() + result_data.ndim = len(broadcasted_shape_add) + + result_data.device = self.device + result_data.numel = 1 + for s in result_data.shape: + result_data.numel *= s + + else: + Tensor._C.equal_tensor.argtypes = [ctypes.POINTER(CTensor), ctypes.POINTER(CTensor)] + Tensor._C.equal_tensor.restype = ctypes.POINTER(CTensor) + + result_tensor_ptr = Tensor._C.equal_tensor(self.tensor, other.tensor) + + result_data = Tensor() + result_data.tensor = result_tensor_ptr + result_data.shape = self.shape.copy() + result_data.ndim = self.ndim + result_data.device = self.device + result_data.numel = self.numel + return result_data - def log(self): Tensor._C.log_tensor.argtypes = [ctypes.POINTER(CTensor)] Tensor._C.log_tensor.restype = ctypes.POINTER(CTensor) diff --git a/tests/test_operations.py b/tests/test_operations.py index 008136d..6a12367 100644 --- a/tests/test_operations.py +++ b/tests/test_operations.py @@ -546,6 +546,21 @@ class TestTensorOperations(unittest.TestCase): self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_broadcasted_equal(self): + """ + Test broadcasted equal two tensors: tensor1.equal(tensor2) + """ + norch_tensor1 = norch.Tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device) + norch_tensor2 = norch.Tensor([[[10, 10]], [[5, 6]]]).to(self.device) + norch_result = norch_tensor1.equal(norch_tensor2) + torch_result = utils.to_torch(norch_result).to(self.device) + + torch_tensor1 = torch.tensor([[[10, 10], [-4, -4]], [[5., 6], [7, 8]]]).to(self.device) + torch_tensor2 = torch.tensor([[[10, 10]], [[5, 6]]]).to(self.device) + torch_expected = (torch_tensor1 == torch_tensor2).float() + + self.assertTrue(utils.compare_torch(torch_result, torch_expected)) + def test_zeros_like(self):