From 2cb87c6b084dc3537ad83c02723ae0fa366ff2e3 Mon Sep 17 00:00:00 2001 From: Brett Laptop Date: Tue, 19 Nov 2024 19:34:37 -0500 Subject: [PATCH] silly --- CMakeLists.txt | 10 ++---- errors25.png | Bin 0 -> 36924 bytes heatmap.png | Bin 0 -> 16682 bytes include/assign3/manager.h | 1 - include/assign3/som.h | 2 ++ plot_heatmap.py | 30 +++++++++++++++++ plot_line_graph.py | 41 +++++++++++++++++++++++ src/main.cpp | 66 +++++++++++++++++--------------------- src/manager.cpp | 3 -- src/som.cpp | 15 ++++++--- 10 files changed, 115 insertions(+), 53 deletions(-) create mode 100644 errors25.png create mode 100644 heatmap.png create mode 100644 plot_heatmap.py create mode 100644 plot_line_graph.py diff --git a/CMakeLists.txt b/CMakeLists.txt index b94938c..cc7c100 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.25) -project(COSC-4P80-Assignment-3 VERSION 0.0.22) +project(COSC-4P80-Assignment-3 VERSION 0.0.23) include(FetchContent) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) @@ -14,12 +14,6 @@ FetchContent_Declare(implot FIND_PACKAGE_ARGS) FetchContent_MakeAvailable(implot) -FetchContent_Declare(matplotplusplus - GIT_REPOSITORY https://github.com/alandefreitas/matplotplusplus - GIT_TAG v1.2.1 - FIND_PACKAGE_ARGS) -FetchContent_MakeAvailable(matplotplusplus) - add_subdirectory(lib/blt-with-graphics) include_directories(include/) @@ -32,7 +26,7 @@ add_executable(COSC-4P80-Assignment-3 ${PROJECT_BUILD_FILES} ${IM_PLOT_FILES}) target_compile_options(COSC-4P80-Assignment-3 PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) target_link_options(COSC-4P80-Assignment-3 PRIVATE -Wall -Wextra -Wpedantic -Wno-comment) -target_link_libraries(COSC-4P80-Assignment-3 PRIVATE BLT_WITH_GRAPHICS matplot) +target_link_libraries(COSC-4P80-Assignment-3 PRIVATE BLT_WITH_GRAPHICS) if (${ENABLE_ADDRSAN} MATCHES ON) target_compile_options(COSC-4P80-Assignment-3 PRIVATE -fsanitize=address) diff --git a/errors25.png b/errors25.png new file mode 100644 index 0000000000000000000000000000000000000000..81b4f6d04675d16f07d26ab08fd0fc7d69d01e1e GIT binary patch literal 36924 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 YC2D1y8ECRE2>7EQ_wZi99m7}u3yZQ-!vFvP literal 0 HcmV?d00001 diff --git a/heatmap.png b/heatmap.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef3a2889c6e9bd51f462ddefa99b53a1ab1165a GIT binary patch literal 16682 zcmdUXbyQZ}_U;Bn1O+|=X)pi*2?6Oe00E_2N)aWbyD>396a@q+Md?sNIxJ9Mx*O^4 z?mO3u=hN?Z&bfEoG44P24+aAJ-Fxk|=9=@F&+{xFs3^&ik4Vv@Q7Td;j*9<)@ zJ3E)=QcP(~{9#Bxh3JdP;X5YqXy;!TR$Bc8BgC*}n*Gx7JE^mTyWx`S=P?2dTi;7^ z7{h#{j}Yv{uxWl~U%0^g-v5sux<6-86(N$rsi>=~Yu!CnA0Mdq;f9-`iB5@kQCT2U zx{=SuLZP>k)yznIGzTxQ>gCIq;bO%QPCJi|G=qYxx*?pny5*U^PDY2VjU}jZ}$EHFR%nx&%LeuCdZFwB|lpvgO_BIagWL zT(S|MkaPe3ZUgU?0R?4cZ9BX7xMg@il4Wz+5LfX-4sPzI{_oG6<|>6wh>Gehj5TX} zdKN~x_LAiIP*fL})mim^dB9yRu`yqtKl|fE(JC3iI@68_xy=qg^F_3L0Pa2QfL z_Wh2Z-^-R39d&*E@!8GVSh?THains$A;!@iFX8_K%ed441XVD&bo3O$EL2BHaF=hu;|6c-;XavptupC5j^GA+jZ5Nj-_Z{)Q-%zp zj^};W`AFYk`bJ)5?v`0f*t4T-6F`UY$R$ zs9o=!GiT3MyIn9dOGqnP)C=`Gdv9;;cx$E;toqnTVx|W8T$M{gFrQuTIRmeSMuiI( zIOhx>9pchY8yb9R(*#iwztrWSoL)Hl(7~tugOw$-mNMKj8>U#mx}zm89TwW6_VwM# z!Z{_c>5s&t-6bWs7YPXod?l@|M~@wAEb;NV7$K|*lb>X7N{ewBH=HS>zW7q`qPD)> zUAR-@yUS-g?aCS2-kbR0?83sr@&`gL9b{lgee&e5Y>P&Pvwq}_A8!*!miseF8Tzc& zM>EUY+1XV!)eS}aaLZe?X1raUABi2ng(jPnKPDw3(}eg6;W6SKxv)n}Qqm9tKYw*J z8JCrjnUUexY~Ye;iVH!P;X=5amO5R=;5N=8X<3v}VPT1DlLg8UQffLnFIE>PE)C{a z4Lss0R>`sML|b8XVeEv6i0;;Mxs-mI07P@p{BmC)E+d6ByZKU%b!vbA)yi-Ioat7I zZl+_qRSr8JUq;1?prHLl^L2_A^>G2WZr^UqGFK;~J}F(`GUd!zpKYnV(Z{q^Q!TYM zUe|QOX}IPH8Iz_b0u4A zCHaHlmJJE2$7If(8~$-3y`dtE-_(DGx_NVBE!%bG%Gt&feg$*4;Mkk2g&@pOgEWMu%$;rBsRuc66+X7m`-q2w zheui4z$sYqsGi02&yV)P^i_FRjl33u^PML~?Yw)#1uPx=0)&!cVx;-6S2cJ{6~#jm zSeR&U*8NTkgFD2f$1IPs2O8YhM^xIwOEluH??O0rUw`(aPz?HD)13wx(h}}%9q2Rs z$`8d?vRoh|G*nc6ipOcE8@3g?WuoDlUc1I|?ATcdF2|Kj-Bl?*hrx=(nxQDGh0(^K zH#qKslnM_B`ZUNMDC+Lq*;yGS(LN!vfB*i5nA1B#w^j&Lh$O9~KQAf_VIesi&Pt6? z8~mB)AQ*BZjtdDj-??`$98z$#wN(6@H(w$|oqnvZ&sEC?>E=5o&K1oLVr6Az;WafX znI22lL&f9cH;#Q@pL8kVzkBy?)do~i-k^#MQ(To>QM_`rVEJPPe%NY*joL^UY;^n z%Nr6~YpGlDR*$>DTSh$>FSYUg%h#`AkUsdYeAyi;;tR=_Kd555=d(f8(m<#Yt^DBk z4G1eq!=(Qz-f`H1we#g~pehivouIKFnLW`iO7j@bd6scy*X($th1`;DfQ zFz%9d`|D#`LFDwpF;HgNSy^Ro-n^NlogQ_?#Dqm2rZ3s{1}Zw-`NKzt#+H{|<*&Dy z!+zt*+{#J)4Ao;~rkqI|X6_(0bwX*WG|F_a;MOL&Pyr1Lj~_pNLQwEShqKn@%dd*p zXBAP^vZ#r<>EHvI4l-Ld1dA2q+fU77N>J_`haz1pno@K{y;idZt0I%3xKvNjC)eo^ zlhO8PY{86aL8+45m{$*s&T(I`fJcoy7MrwfHFB@YthPgm1YRRJ_q7ARby5`+m`0|} zX<{h4IxwN=wRY{Lj_K{avH?kgQgmvxF}bnG!$n3`R#m83)YC^++eVY2fQ+_tJ2aK@AQZYdm;!8(>u z{nu#o&-QqMx{iLh_^%FI6ABC;3O->T=g=@RdB zsFoP*$jh`Y*>FzPd4I-hb-wn?g98gVo|5MxM)h$QvtgMPzG9`6kEB(O_4f63Dp?2m z`#XkFWch4))0iBjq#Rfjhd6%!hM(*G^XCUAIt#Rwr8ctKa%|Gyy*mjobhynj-O0S( zy$4k&WpGB_b`;H>sSads%+k&@bqm|>9g z)G4FhfGvGu&Jc9617@BmLBsFZW3|bkRSb z2L-(bTxcZSYqPw6&@o@KsMmB?h3@7z9w`0cG)?4kHbX zHxLDR44F3Fk~wQom7S`p&0F5Rq9^y8ot^zrm;=~V{(7;Hjnim@Jk*$`J0u4hV&y2l ze)*Csk#;KV8xH5dExKYQPWj+1~`^yh8MydJp!zyaKiiT0e(`R+SE$C+mWgu2)qsxp%q8oy?6 z(wO}CG7SJaB=59MBBZ#yLPO}xydkpeZMx_V9g4F|%aa9|ZJ&1!0mQi~F^Gz$x1ol5 z3wu@q;-lOZyw(;QO#&sHt4yp5XJq9$94ub|YBq;7#+?&QLQd|yR^$R$3yRN)XNM~x zgRg9^ob*_{;Wb|?mk+t7@wLnz`N=I9$tjaEf4;tICy5YK_LC=T-d4J=FWaF4Px&wz zZuz6S+C}$ykG%PN%`qt_Pu0s-fRdcju$brYwS4L{$1eoiAuV_Pqu0FZ(0f>J1bd!8efkPehX_463oENdsV@OS-LOv*-~);9 zj;l~!=RfV?Nr2=y+ErAb-E0R_K4hhYb~?+$oqU!}oDP)&%`YH*1Dvqn&g=px*zbO>wIy%qgp&VAEcD;dd2F?<^WQo`r{7 zaOeIcM5AzD7&Y}?9XU?X-Agmpl*%vEl9I|akqA8V|`C%gKVNV>Or@z5nynN+ZNXWHoRS~+c;%lIhdKtoX zDMVLy!kW_d{Q3KN&~{c1*T!BnG&BsebjcxY>9kpIFFC%pG$jQ1gwLuiix+Tf-09o9 z;@}+y(|tCQaicwTQsuzDa4)(4xKzaXePF5#kZuJ)E_@Iipr$s1B6D%5Dl+oK^J+nd z!3*lpCqniwwl~34BylfDJKi4j)rOdeMcwpEK`Z+L>Rqk_!P@hHuq~k?4M+}r>2{jz z(1je%#ve&2J#dovFU;cRzGJd%e0*vE`xczovYXnnEfLxy?bN4}UIF$l=Fn~ul3G|` zgC4irn+V-N%}t$7y^2N9aTx=VCv}$Szp{U>`_D6|s;Rv$@>t>!7cbO{eng5T(3DX# zNVsc2A zIz$WGo!LjF|3+QLuTWAO=$b6xLxQAV9wkUQrVxEeto@<7*P|*gxBLqF;jzC)@AQQl zPN47EvV|rDkEz39gP;j z=L2ez zEsNVX8^k^x&?|+TdEX;F41@gqO!_AYez<4%P)9xK-GTUB4qpsw@&DZOmDW|g^Inod;B+^q9^l{i}X()edKWRY{la8T|7%nDGhIY zPsDfn4&sJi9wE4MJj>VXFc;Uw5L5H{;o4$yA{f9C4qt&A00+Y*z4ApJhkuT#z(~Ia z#PpC*meM+oxA1lXEDPr~xkoE_V|dIK{y1Q(V`%eEXn!d!Aj91Vb3@Wva$BYRUqTnc5f~U z!H-F~xw#cDAV&drQJM#2&FW!$3^SI?^0kx(_G^A3r#pWfPc%X0me^eGBi+55jh|Im zSm)!%j{=lOu~OMwio>wNDFl$jQG1IkhExyDN^XTWh83E+(VDHl3 zpZ$C!J!)rfjFg$j`WNi)2*Sq=ez{W!@BMnpXVFYIe#XTzdRXplp}@ED`3K*k)}$){ zg&sb7qz+jWNQErx&U^>PH3VQFJ6Uw(zDK+z%*4h*x=-pTOKcDtiZOrjiygVf9|;2( zCG;RH;L^zXGCG?C=yp~C-ZTJk;4AmLd)FLl@o3Gf+kj&cC^7?Xg>=uJ`r8zeIs+}L zyc)2be47#52DGQ;wmHGtHA{ra{3qMVG3d3UkbfVY;TV-{2> zsz^yMy}t6m<|jwaCeaIVJVtxf_5?KEPoF-m;^N{;?dYpcqo*|y*E-OgdMQ37g%e?I z&l7%9)7wExmhm@{2S{^9-mwyjmc*e)U>_9I751Dnjf#?Q|m z91)@4lN-PbOls2V-Plhk<6!9lH2kuOiHSgYYpAIO#VJRp0e}L|@IufjKpYdTnQ~BJ zf|)NyifTfsQh`#&9GS=S6WZ7mfUGOywCmfkXrg(UWvaFBM6ywdH-PD8=y0BghQ>Qj zw3&Z>Nck`zAeebzV1WJj@p3m$dHDxrj~SAIdP6%fRNwvqt5QS)Flt&N)*AHq45#ic zMrBi`{S}xV0ncUIoTYx^>XDcTtS9Bs|tS;SN~qwrgAA6 z=)r@;9y7>Y22Zi3n5NWAC&a`q(Fs{MoTZR>b8`prC}3(^k8z;kHG%N$uy=5<6xPF} z4Za@z3zj(-wk%O15XeDqzI-_c&{RIXcr~fZwJ(6LeAjj|iaQGx4MX!tDb2)JmRm6g z(Vsw~fmwcgrYxBVv<3&H9-zw`*H`8iR)(T2fpjh=Cxe8@Nq7@-zQ9#b5fm<7)HF1V zLekW$v&5et9Q{kLbWsi; znt)iNwo@%LfQ0r^xa@B{!e{ULKB)P3km;39-}y5zelPslu1?}7i!mEBbboAPmSc*B z;(J;i{Ub;b7K+ik0IC^iU!)`?s%OsJf*E$HGNIzqdjnL@Fm#(5AZ`W)1J$$S&&*A#Qc&j?}Sk{3?1Ahye7}X!;>KJVaJ<;p84bZ_u6-tKQqfC9uNZil*^>P{Pmy`f*ret zL1t*oZZ;fCVZ3+m9tUWQqwizSY!5l93pbVXe=0%)g3dRkK{&v zD#tIZAFXaa(m)c*`eDXtcXrqJ19Fz*nJ)k~eq+V}svrRQC;Ic}i6ZjPzl#res6qeu z+ur2AF(b4ND4hRQ5oyrQ{8gO{9t9SZwlLwVEA)|3e~%KiEWewa|GAfSX43+29L8(g zB@k7ge{?Mz#;#}ZFD`r)T8syCsGHI&9calgeN?=$cxhp609o@T#u~L!eRRr_bZWr+qzoRds1{VE%5VAZh zjMU2o9XxO#_BJuaD&Hv#t9l@Fc=`^hu?R4n3mp#CBY^&!K-+8pUxa*JRiqdt5cGY+ z7=|U?+xd&cuSF0V4lWKtfFFPD z!?36sW=Q^oIs^bxn?6{zgD(a204HDzRz+c@eL2unz@BSJH1r$&X04qj1uo@!(qgDYeJNAFuC$T>LxtN>+ z!*Wn4C14>YP#{*pI&zSSX{w|7gViO3_1M|jCH(=%#EhQDaELSjnkcx{2w0Ax4FUoz z!D@S%2Xbf1=oY>m3pAVRDys7GSRAi~Qk?{?g662cxw(1%Xi_qA4S3Z?a5+S zF$_L^Y=5c!g)lxD7$)9^@SP?D}Lu4;=-GTtQwQ zd^|mF4;f(tNkAOkfAF9d8i0Aw0nLCH<||8pf((44$J^>``vHZ}X5ewOpc6qk)&LAa zjg$~xK?+yDzXZ_hkRi6e)IkS=1ql(*tR#9YO@fx7ksVb60(KCx2;@vrQBo!*Bz#+0 z{r2=&cEh2%!8Crai zm30b=a~Ykw6+?ok>YaBQ9B6TW5O#hMKlv_O%8iaTXG4^qiB0MH1A4EKnfQt0gQN*uboF zA=+y(>~(BxwVOWR73eDT1u=}{mL}|Y5npS1DQ|s%GPcF!yLTfzvpE;NrLhtLBZuI5 zM8(qtx=b3h#G|_@F<-u_EW705C$|s11HK^YGti17K|7N73^2ehkJU;9qqtzJDA;5&`l<}CwdK^gJwrX>>_#K$V{kQw4^h!I3YZd{N{fDmD zgofu*sUZT;ds+g3T3AB>#&_->JRQ1`yp&}kEQY%WEg<}c%Fy-nFz+n_ zJ22_D)0`+}4VS^LSpd0Z48G@&XW+7KwDKAG?Zl+-25;w(_q+S)p&`ox-SQ$tGQ3U! zY+Op};Z-NA){L)mkf1OP1`=ZtkUXvZzfVV7HQ3 zY%xJ3IntaATzl>olapY+q&~Q!8y=eAbA-dP*RI7uJb5ud{m{IU_+=~= zZ;1IXEjsMVQBS}6P-W#joyD8yW z&8B!b^3Lw3URZ~4mGNwdGN8KRV*TKPo za^Jq54kmhf{Y_z_xx+N+RszE| zeeqW4*5b8f|CpG=EX>So{QUK%KJd~(&z>dE)u~+EX6d>lt8++A%E4dK(4Yb02Q&@x zC=)0tJ%*-$Rh|~M>f(57Y@WlAX!+Ia*A;_+t%x)7UXcM)5bR+!B*Qa`ap3E20lPs( zPvx@0D7e{X=jH@1f7%I+GE!ai-e0+kRQt%hnYq@bov_to=c0vdx>Ar!@%{VvkXf`F zP5vFLz?Njstag3&=F1;fypcLMvR|Hb(I z3Dq?Q%o(o-qJ97|lt51rZ{HO~FSD=H1+!S&MvAndv6B|rzE-*j!L&5JDo8(@aA1;Q z#seXl{z>jxKrn{2cDUvvm=gZu0ldI)pi@y6tJ_4AbRVAEIy!Y>8M>xe-T{> zjTc;|JSlz{-??u$c)wc^GA?6Rd@?hml zTm~nXZpN49BLvuZQWoIQU6+m#q#ObJME?3L4&@0WKSJ!Cn8{|r{#*q*l9)~aJoo0I z-;wZGoOeRA%$Erfa4qG7%$x1{n?CxtW<6WHpA=$j8Um z!MFt)bF$2%L+E#4s6i?)_*#sRWicmo`!1L@gNXeX(-S~!cy#=SrnB>LE4Wa@`fNb> zM2re}*cu%OgpZ@FFdp~CwM72mg9oZ8AQTl99oFpr11V}FJuw4@uqG%9$vukF zZZO-xYf-Bo?fY6Uu24QWILHAhI8O?Q)egH5(?};q^=r}beVr3>3ut*TYjCUNGW!l8 zI|k9tozD{&8H8esh3l14Qi7NbMG!Dz{&;mo zg@(O-MmsMUG)*5HfhY?Q9(<2{7p)Aw1SApys5?%M$x8#s z0MIF3Fq=cNr9ElvJGTrH<_G|xCg@JHtl9;^KBI{A_O12mEsgeOUk5z5RtQA0)WFb? zC78y3;Be{%&J$TzzLI_|D^myp)n9*-gqn&f9>|5^S~;dhG_j!MZcNzt)C2CAOjexU zPSpPFynKe@RjElPeiTe;V8tl#vH^p;>7vs}oy?^VHxlcve-vZ(g>Ex|eP&X()G+0+ z>@S*-+9V=JBXQKVh3*k7*DrDsRtSa0X8{M&!zcF1j=3WkSnwlJb- z6-0dBKYBxTHCQzTLz}YK*7!KIR;UQ8e2FSWspy~|QTK@o(rG8^?6w7;;Xb77S*H3R z2K80v*NecMMjCTpmVJ6$E|8(#$ctg;v=2TJ(3mJ({YU2<5Sr|3PSB2TJDp7mJrB}T zO1GQT?fT6U+8#6e)k4}bN&k}#_2=a)`phk!YI?W&H$?1+v%-(l(lm(Qc45ik#ZV5%gxXq;34s}1G6tu zV|lw$`voBAoVR&W#FD4{LAitlU5VWf_*+;ArPdor75=3eY!g^*>zqH~wkHGxG(kdo z=3*?3*RQrJ5ZVW>y|pg24s`=g8_45}|3%i53EtOE*MwG9);dMLvTwxx&@NzkNg42QOdc0D+FMzNc5(Xg74{;Lvah z_DDmW2WodfB*PUhfmX!^cH07DRrX&q#r;&Z@q|U(a)p&K>WYB^Ft2;_04B9}Cil%7 zW~e5^$?3&)$jdB`oJGLZ$MpC2bCf^&f$}{Fq>?0GUQT%44-9MuUr`D%qpLI``Jpf^ zlz=5KH=M*WgZIxftV2!99se8kMvJw`Su(eZD(h}xm`sFVPF;&g>G{V#aX7zzTl*lvz#8+@WVuNAZ(<;r-K@qXgp7%7mvUr2mwS z6dndB_)~HHoXgt1Gn*Kn1x=J_X#A+SjQ2~o|3gD!i4RaHOu*_0KS=!@!rRFUZlbLz zZgc2KjI}ZO;om9zg*ppgh~~mf+#A~1(^lIQ>Wg|=mJlij43a-V&fDUgdcaBs47qQx4h@~39)#cujQK5l6;+D3sm}vD!vA{ zu{+T6L;5Ofz6_H4^OrA^hlhvf>?gr?#Kz671l{7?syqA5&0J z$O4zrrs7$bCK`T*!vo zuWw!Rf->0EfV*_}ih|BDNQy3&NMBBv{_)(RkAD~Fu?YfOw^A#Yqd4wOC0>DVaUZ;t zVQ&v@@PmEue8kH$}q6^hc53*6jcNaNoIL zbiU{xO3BdJ6N_N5nJ0l!$)c+;58R|dEQ+2CYN^k`Gm)H@uYi0DZCQ!dMGIQMM|Cz| zZWM(wfOPt~vNFI^t6-e>WY9_t2?=3^vpDYaX3%Cdh%);o1pQZxJL6=QEemdOWK9Q} z%B@po2&^8g8-bw0u0K2;)EjdqXB1f$P4Uw#pJ14U`lv~WNLB-(Phzt zFj-}>saB)@oJ#AD1x5`ZK*ZhU1OI{2LsVEyHfVD&~ z^I8BRh+$=u1uPoQ1BX=yr3+nR53Ushw}D`E@&?Eh5d*uglz^+3oOGm9L1zzk{&&*C zok@nMfQ^mqi6d*?O%G@g!litQ!OsBva{rjEMI;bo2`*Dzh~Y^;OJI>IZA9#wKlC3; zrJU+Va(`Z0{%ihlqDU0hs;arCLA2X@Efqv9bh-uk@qwJv0t&$k7J|=b(-XKR8PMft zo!F#PWeM+dWd16uk}-UUk46-tk>|`^2%}(R?}X}r&e*NOvAk2@DgZ|L9`p28|KQ;0 zQ#r=kB+X=ClzPW(ef0n<2RHo_g=_wm01~{cd25EL9yFtPE=VaASxMoDq9jyDF^tu!8fOZ7{FAWV+l^0A5`dHkBFtAb_1$4iB@=VMkBnl2LHn#I{$&ZQ( z$JS^#ga*gRm}Kte|DcWqCk7m?lR;ihV7nk#BQN`_Lf@MHIYA)bX)F#YvXES>xZr>% zXiVuDnVIsNzTGh}5i~H3Q2l~myhwnoelc3o;1|A!C=)=GjL*iHQB|{;%Ot9k$U+YE z0yu^K~v%-0~1v|&=^8cO8n%cCRf`W(N#%e4AK*2_5ma3Y;qWqMZj9yp;7@QeZ zjcnzS)H~=@RfZ3+NwlZTR4R!z6eqe%jKHvg+6?H$z;YD>6+8)8bO3A83}3u}VS_~C zu>B2X@&BunzOHX`b9F$k%Liw)!6MbcgKU5V!3NzRQV!&)CU|NxI8kbi7Nfbkv^e(q5qBNfNRGyaI#AV@9+H@Y;>`2AJ{n s5IBz=1{+u9I6}hz4^QQXZBd*#`KmklV+8yc1z_WlQ974)#`xC%0&S=7eE(motor_data.files[currently_selected_network], som_width, som_height, max_epochs, distance_function.get(), topology_function.get(), static_cast(selected_som_mode), static_cast(selected_init_type), normalize_init); - som->compute_neuron_activations(); } private: diff --git a/include/assign3/som.h b/include/assign3/som.h index dca8871..701c008 100644 --- a/include/assign3/som.h +++ b/include/assign3/som.h @@ -44,6 +44,8 @@ namespace assign3 Scalar quantization_error(); + void compute_errors(); + void compute_neuron_activations(Scalar distance = 2, Scalar activation = 0.5); void write_activations(std::ostream& out); diff --git a/plot_heatmap.py b/plot_heatmap.py new file mode 100644 index 0000000..79db893 --- /dev/null +++ b/plot_heatmap.py @@ -0,0 +1,30 @@ +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +import matplotlib +import matplotlib as mpl +import sys + +filename = sys.argv[1] +size = sys.argv[2] + +df = pd.read_csv(filename, header=None) + +height, width = df.shape + +data = df.to_numpy() + +plt.imshow(data, cmap='coolwarm_r', interpolation='nearest') + +plt.xticks(np.arange(width), np.arange(width)) +plt.yticks(np.arange(height), np.arange(height)) + +plt.xlabel('X Pos') +plt.ylabel('Y Pos') +plt.title('Heatmap of Motor Data (Bins: {})'.format(size)) + +plt.gca().invert_yaxis() + +plt.colorbar() + +plt.savefig("heatmap.png") diff --git a/plot_line_graph.py b/plot_line_graph.py new file mode 100644 index 0000000..ef4596b --- /dev/null +++ b/plot_line_graph.py @@ -0,0 +1,41 @@ +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np +import sys + +file1 = sys.argv[1] +file2 = sys.argv[2] +bins = sys.argv[3] +split = sys.argv[4] + +df1 = pd.read_csv(file1) +df2 = pd.read_csv(file2) + +data1 = df1.to_numpy() +data2 = df2.to_numpy() + +y_min = np.min(data2) +y_max = np.max(data2) + +if split.lower() == "false": + fig, ax1 = plt.subplots() + + ax1.plot(data1, color='b', label='Topological Error') + ax1.set_xlabel('Epochs') + ax1.set_ylabel('Error %', color='b') + ax1.tick_params(axis='y', labelcolor='b') + ax1.set_ylim(0, 1) + #ax1.set_xlim(0, data1.size) + + ax2 = ax1.twinx() + + ax2.plot(data2, color='r', label='Quantization Error') + ax2.set_ylabel('Incorrect BMU', color='r') + ax2.tick_params(axis='y', labelcolor='r') + ax2.set_ylim(y_min, y_max) + + ax1.set_title('Topological and Quantization Error (Bins: {})'.format(bins)) + + plt.savefig("errors{}.png".format(bins)) +else: + plt.plot(data1, color='b', label='Topological Error') diff --git a/src/main.cpp b/src/main.cpp index 13187c4..d1fbc93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include #include #include -#include using namespace assign3; @@ -78,11 +77,6 @@ void action_start_graphics(const std::vector& argv_vector) blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1).setMaximized(true)); } -struct activation -{ - Scalar x, y, act; -}; - struct task_t // NOLINT { data_file_t* file; @@ -101,7 +95,7 @@ struct task_t // NOLINT gaussian_function_t topology_func{}; std::vector> topological_errors{}; std::vector> quantization_errors{}; - std::vector> activations{}; + std::vector> activations{}; }; void action_test(const std::vector& argv_vector) @@ -125,7 +119,7 @@ void action_test(const std::vector& argv_vector) static blt::size_t runs = 30; - for (blt::size_t i = 0; i < std::thread::hardware_concurrency(); i++) + for (blt::size_t _ = 0; _ < std::thread::hardware_concurrency(); _++) { threads.emplace_back([&task_mutex, &tasks]() { do @@ -139,7 +133,7 @@ void action_test(const std::vector& argv_vector) tasks.pop_back(); } - for (blt::size_t i = 0; i < runs; i++) + for (blt::size_t run = 0; run < runs; run++) { gaussian_function_t func{}; auto dist = distance_function_t::from_shape(task.shape, task.width, task.height); @@ -147,13 +141,13 @@ void action_test(const std::vector& argv_vector) &task.topology_func, task.shape, task.init, false); while (som->get_current_epoch() < som->get_max_epochs()) som->train_epoch(task.initial_learn_rate); - som->compute_neuron_activations(); task.topological_errors.push_back(som->get_topological_errors()); task.quantization_errors.push_back(som->get_quantization_errors()); - std::vector acts; - for (const auto& neuron : som->get_array().get_map()) - acts.push_back({neuron.get_x(), neuron.get_y(), neuron.get_activation()}); + + std::vector acts; + for (const auto& v : som->get_array().get_map()) + acts.push_back(v.get_activation()); task.activations.emplace_back(std::move(acts)); } std::stringstream paths; @@ -170,7 +164,7 @@ void action_test(const std::vector& argv_vector) std::vector average_topological_errors; std::vector average_quantization_errors; - std::vector average_activations; + std::vector average_activations; average_topological_errors.resize(task.topological_errors.begin()->size()); average_quantization_errors.resize(task.quantization_errors.begin()->size()); @@ -184,30 +178,30 @@ void action_test(const std::vector& argv_vector) average_quantization_errors[index] += v; for (const auto& vec : task.activations) for (auto [index, v] : blt::enumerate(vec)) - average_activations[index].act += v.act; + average_activations[index] += v; - for (auto& v : average_topological_errors) - v /= static_cast(runs); - for (auto& v : average_quantization_errors) - v /= static_cast(runs); - for (auto& v : average_activations) - v.act /= static_cast(runs); + std::ofstream topological{path + "topological_avg.csv"}; + std::ofstream quantization{path + "quantization_avg.csv"}; + std::ofstream activations{path + "activations_avg.csv"}; - auto f = matplot::figure(); - f->tiledlayout(2, 1); - auto axis = f->add_axes(); - axis->hold(true); - axis->plot(matplot::linspace(0, static_cast(task.max_epochs)), average_topological_errors)->display_name("Topological Error"); - axis->title("Error"); - axis->xlabel("Epoch"); - axis->ylabel("Error"); - axis->grid(true); - axis->plot(matplot::linspace(0, static_cast(task.max_epochs)), average_quantization_errors)->display_name("Quantization Error"); - f->title("Topological and Quantization Errors, " + std::to_string(runs) + " Runs"); - - f->save((path + "errors_plot.eps"), "postscript"); - f->save((path + "errors_plot.tex"), "epslatex"); - f->save((path + "errors_plot.png"), "png"); + topological << "error\n"; + quantization << "error\n"; + for (auto [i, v] : blt::enumerate(average_topological_errors)) + { + topological << v / static_cast(runs) << '\n'; + } + for (auto [i, v] : blt::enumerate(average_quantization_errors)) + { + quantization << v / static_cast(runs) << '\n'; + } + for (auto [i, v] : blt::enumerate(average_activations)) + { + activations << v / static_cast(runs); + if (i % task.width == task.width-1) + activations << '\n'; + else + activations << ','; + } BLT_INFO("Task '%s' Complete", path.c_str()); diff --git a/src/manager.cpp b/src/manager.cpp index 9f4b974..4288741 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -242,10 +242,7 @@ namespace assign3 if (running) { if (som->get_current_epoch() < som->get_max_epochs()) - { som->train_epoch(initial_learn_rate); - som->compute_neuron_activations(); - } } diff --git a/src/som.cpp b/src/som.cpp index a6a33f7..3cdc8ca 100644 --- a/src/som.cpp +++ b/src/som.cpp @@ -34,8 +34,7 @@ namespace assign3 { for (auto& v : array.get_map()) v.randomize(std::random_device{}(), init, normalize, file); - topological_errors.push_back(topological_error()); - quantization_errors.push_back(quantization_error()); + compute_errors(); } void som_t::train_epoch(Scalar initial_learn_rate) @@ -67,8 +66,7 @@ namespace assign3 } } current_epoch++; - topological_errors.push_back(topological_error()); - quantization_errors.push_back(quantization_error()); + compute_errors(); } blt::size_t som_t::get_closest_neuron(const std::vector& data) @@ -265,9 +263,16 @@ namespace assign3 continue; incorrect++; } - + return incorrect; } + void som_t::compute_errors() + { + compute_neuron_activations(); + topological_errors.push_back(topological_error()); + quantization_errors.push_back(quantization_error()); + } + } \ No newline at end of file