From 43351ab1f2d93ebc24d4e26bd7503017b42253e0 Mon Sep 17 00:00:00 2001 From: ltcptgeneral <35508619+ltcptgeneral@users.noreply.github.com> Date: Mon, 9 Mar 2020 22:58:11 -0500 Subject: [PATCH] test1 --- .gitignore | 3 +- analysis-master/analysis.egg-info/PKG-INFO | 14 - analysis-master/analysis.egg-info/SOURCES.txt | 12 - .../analysis.egg-info/dependency_links.txt | 1 - .../analysis.egg-info/requires.txt | 6 - .../analysis.egg-info/top_level.txt | 1 - analysis-master/analysis/__init__.py | 0 .../__pycache__/__init__.cpython-37.pyc | Bin 150 -> 0 bytes .../__pycache__/analysis.cpython-36.pyc | Bin 32633 -> 0 bytes .../__pycache__/analysis.cpython-37.pyc | Bin 26681 -> 0 bytes .../__pycache__/regression.cpython-37.pyc | Bin 6984 -> 0 bytes .../__pycache__/titanlearn.cpython-37.pyc | Bin 3058 -> 0 bytes .../__pycache__/trueskill.cpython-37.pyc | Bin 32168 -> 0 bytes analysis-master/analysis/analysis.py | 790 --------------- analysis-master/analysis/regression.py | 220 ----- analysis-master/analysis/titanlearn.py | 122 --- analysis-master/analysis/trueskill.py | 907 ------------------ analysis-master/analysis/visualization.py | 34 - analysis-master/build.sh | 1 - .../build/lib/analysis/__init__.py | 0 .../build/lib/analysis/analysis.py | 790 --------------- .../build/lib/analysis/regression.py | 220 ----- .../build/lib/analysis/titanlearn.py | 122 --- .../build/lib/analysis/trueskill.py | 907 ------------------ .../build/lib/analysis/visualization.py | 34 - .../dist/analysis-1.0.0.8-py3-none-any.whl | Bin 20459 -> 0 bytes analysis-master/dist/analysis-1.0.0.8.tar.gz | Bin 18782 -> 0 bytes analysis-master/setup.py | 27 - data analysis/__pycache__/data.cpython-37.pyc | Bin 4239 -> 0 bytes .../__pycache__/superscript.cpython-37.pyc | Bin 3965 -> 0 bytes data analysis/config/competition.config | 1 - data analysis/config/database.config | 0 data analysis/config/keys.config | 2 - data analysis/config/stats.config | 14 - data analysis/data.py | 102 -- data analysis/get_team_rankings.py | 59 -- data analysis/superscript.py | 374 -------- data analysis/visualize_pit.py | 59 -- 38 files changed, 2 insertions(+), 4820 deletions(-) delete mode 100644 analysis-master/analysis.egg-info/PKG-INFO delete mode 100644 analysis-master/analysis.egg-info/SOURCES.txt delete mode 100644 analysis-master/analysis.egg-info/dependency_links.txt delete mode 100644 analysis-master/analysis.egg-info/requires.txt delete mode 100644 analysis-master/analysis.egg-info/top_level.txt delete mode 100644 analysis-master/analysis/__init__.py delete mode 100644 analysis-master/analysis/__pycache__/__init__.cpython-37.pyc delete mode 100644 analysis-master/analysis/__pycache__/analysis.cpython-36.pyc delete mode 100644 analysis-master/analysis/__pycache__/analysis.cpython-37.pyc delete mode 100644 analysis-master/analysis/__pycache__/regression.cpython-37.pyc delete mode 100644 analysis-master/analysis/__pycache__/titanlearn.cpython-37.pyc delete mode 100644 analysis-master/analysis/__pycache__/trueskill.cpython-37.pyc delete mode 100644 analysis-master/analysis/analysis.py delete mode 100644 analysis-master/analysis/regression.py delete mode 100644 analysis-master/analysis/titanlearn.py delete mode 100644 analysis-master/analysis/trueskill.py delete mode 100644 analysis-master/analysis/visualization.py delete mode 100755 analysis-master/build.sh delete mode 100644 analysis-master/build/lib/analysis/__init__.py delete mode 100644 analysis-master/build/lib/analysis/analysis.py delete mode 100644 analysis-master/build/lib/analysis/regression.py delete mode 100644 analysis-master/build/lib/analysis/titanlearn.py delete mode 100644 analysis-master/build/lib/analysis/trueskill.py delete mode 100644 analysis-master/build/lib/analysis/visualization.py delete mode 100644 analysis-master/dist/analysis-1.0.0.8-py3-none-any.whl delete mode 100644 analysis-master/dist/analysis-1.0.0.8.tar.gz delete mode 100644 analysis-master/setup.py delete mode 100644 data analysis/__pycache__/data.cpython-37.pyc delete mode 100644 data analysis/__pycache__/superscript.cpython-37.pyc delete mode 100644 data analysis/config/competition.config delete mode 100644 data analysis/config/database.config delete mode 100644 data analysis/config/keys.config delete mode 100644 data analysis/config/stats.config delete mode 100644 data analysis/data.py delete mode 100644 data analysis/get_team_rankings.py delete mode 100644 data analysis/superscript.py delete mode 100644 data analysis/visualize_pit.py diff --git a/.gitignore b/.gitignore index 1b800b48..7adccf64 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ data analysis/arthur_pull.ipynb data analysis/keys.txt data analysis/check_for_new_matches.ipynb data analysis/test.ipynb -data analysis/visualize_pit.ipynb \ No newline at end of file +data analysis/visualize_pit.ipynb +data analysis/config/keys.config \ No newline at end of file diff --git a/analysis-master/analysis.egg-info/PKG-INFO b/analysis-master/analysis.egg-info/PKG-INFO deleted file mode 100644 index cc62b061..00000000 --- a/analysis-master/analysis.egg-info/PKG-INFO +++ /dev/null @@ -1,14 +0,0 @@ -Metadata-Version: 2.1 -Name: analysis -Version: 1.0.0.8 -Summary: analysis package developed by Titan Scouting for The Red Alliance -Home-page: https://github.com/titanscout2022/tr2022-strategy -Author: The Titan Scouting Team -Author-email: titanscout2022@gmail.com -License: GNU General Public License v3.0 -Description: UNKNOWN -Platform: UNKNOWN -Classifier: Programming Language :: Python :: 3 -Classifier: Operating System :: OS Independent -Requires-Python: >=3.6 -Description-Content-Type: text/markdown diff --git a/analysis-master/analysis.egg-info/SOURCES.txt b/analysis-master/analysis.egg-info/SOURCES.txt deleted file mode 100644 index b7f40198..00000000 --- a/analysis-master/analysis.egg-info/SOURCES.txt +++ /dev/null @@ -1,12 +0,0 @@ -setup.py -analysis/__init__.py -analysis/analysis.py -analysis/regression.py -analysis/titanlearn.py -analysis/trueskill.py -analysis/visualization.py -analysis.egg-info/PKG-INFO -analysis.egg-info/SOURCES.txt -analysis.egg-info/dependency_links.txt -analysis.egg-info/requires.txt -analysis.egg-info/top_level.txt \ No newline at end of file diff --git a/analysis-master/analysis.egg-info/dependency_links.txt b/analysis-master/analysis.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/analysis-master/analysis.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/analysis-master/analysis.egg-info/requires.txt b/analysis-master/analysis.egg-info/requires.txt deleted file mode 100644 index 6868226f..00000000 --- a/analysis-master/analysis.egg-info/requires.txt +++ /dev/null @@ -1,6 +0,0 @@ -numba -numpy -scipy -scikit-learn -six -matplotlib diff --git a/analysis-master/analysis.egg-info/top_level.txt b/analysis-master/analysis.egg-info/top_level.txt deleted file mode 100644 index 09ad3be3..00000000 --- a/analysis-master/analysis.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -analysis diff --git a/analysis-master/analysis/__init__.py b/analysis-master/analysis/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/analysis-master/analysis/__pycache__/__init__.cpython-37.pyc b/analysis-master/analysis/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index fa1abb0083cec6db1575fcf5da3f9955077f08b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmZ?b<>g`kf~M@GI1v39M8E(ekl_Ht#VkM~g&~+hlhJP_LlHHsZ_ZI+yAOU`eI#LovNEAd--?TzeB1O6SfMt@AowKaV;dTKWc(^;x z9!L;5#C0J%O;W{9+c3uS;^TI=1ki;$=My`IEAIjrKt_WzRSLw z-}#h%)Xu+RqUJqP<{q>)V;AJDy}?`dy>?M@_Juje?2_b^(1ZKz5%geW!>a5@>bO0M z)F@K7gf-r8KOm(KggFn|4@u6gVa~($Ba%}Na~`!%NY249=S}uwl5;4``3C!>2#ae5@X|@+mKb>t8^onuu2yT!X0;k`>b__-x+lcQyrAMyc(y^Ii$A+}j zT3Mc}%`EADC}$b{YP3LKE3JCB0lbA3Jc{>YSiR_|meXvk0e3H)enL9??Cfl;d<=aW z%@|X+e5KJ{ET4bzd3kBJ*0}6ES-TpcXSOv2R=!audXJXfl{twyh?6km@NSEVM}}A4 z2Sc}kA*|Pkg0j};)BUcB0IRHvqD%TCu(?sWi*4O75Lm+>Ad^SNmVmuZo=2`O73;6cC~V|op9r(O%Y z#Ue54ggjlTgES*DbyIT}mz`S6#c;1c$SIP2WT=UG)*3^Wi|U$WZo8~BGjjyzu9h*B zczveZK4LqU8+Atycu+?SNj~uH=G9hvxdDVnYQos{Q%9!Xw3&||07yfs>#7yUz1V0r zBXo2BW}LJzs4>umiA02j-8mWop{fybWST|#kd+sjjf?H${!`5_Eb+8thl4@@V(1G9 z7q8dxeo$?9MYYP^l}^*a+<>8|*`(>3QnfzavaTWh_G^kmLV=(k#261$sfVuLe63zz zQMG!%nd4|CMrxX*F`q{e%R(_?(?i-Xq$ys~<;9v?7NMv4QtAsa#F}ClQs_MAOX8Yy z1<6a3aO|BJCK{0S3rZ%p{t(S{u9neqKSPgFga;t(+n3|i5NRpOv=9U}OrEv!d|Pc- zqsCRNbPW>as@ouwL2ieAfVwo;ZoS=1g9_2OTXt6KPN!S$gzOo@%n3B6Kdp4MMp4C-*{y26k5CEA$8@Ox z;VZ7w1X$&+x>}|R$f-)E6c7OgC+cwQ27lcy zI)Ai$xu%e;>zR_Gk2S{l#BJ3&9aMP4@6t3TzooWnbfE*!L)JMCX{jZltsHK167@-uh`V)IAuaf!X!zNT_ZZd znk>^00Tl0UZ1@LQRrAlhYA=`Fi%q8nDW+jB-=*cH;4kdgl}6L9*HpZN0iI&y8;p1z zblb(e3@xV>iMQuKHg(X@QH>GkPcCM9fDu(Q9>aa zz##)%U8{=18^JVSsI^e}N~gRCqorL9*&P^dv@o$k1wqS{MX0V1L%Y*$EH~Cfzlsj@ zRsay-h$stTr!6sGy9|m1{midmB5}%~RglYK*>A(q+P22VcJ;T~EpR0fjybvC0tk(^ zO3<^Tpqo~Gak-{0lJHfdQ>AE(DtJM|c6qJZBKJ{A_B8~bPG*4Tj%wE_omvYsVW|ji zH5G%Nnj#;{THU@k0E{LSFduX_f}EiFz{)r_vq_y+&OD?k%oRJ*po|l$bTChX-2qqR zVV@FUvc<5jhc^H#5b?)msAk8+7;H$46X%F{p_teT+QD4oTmk1mR>G^|bfCln?O+6f ziK2z|$HbK{@v4_=%@ruVz5x>}9}`BQ_|}FWBmidIdKVLnh=W#_CZ5(@0z z2&Er^mjz;nPLzmrh_d45s%ihk$Wm7w<@N1%qR&UHY6z}@(|Q&9p_*@WkCv}2HXu=%x6;Jy0Nv$E zn@WHSf$OsFu%x3DxSY~HqI*QBMx#5Zi8HYODgh2eS7eYwvY{lF}a>NQh5!PbE1{X&Rg--}7=937D!ajw#qLCP*AdU5immp@E2a-^PM_8No z(tyQeG@aEB=0`m__F1q`Mr<<1)w)T(K3LpWOvKLFuG(lh)D!J7=?kw$voM}f?T2ET zHe<{ys$m-FCJA9h0k;ZMKg0+aRD{t0Y?FYyP_5vG-E=r#b*@JSIbf56Xk?|^SYht^RN*$RvyRjo=q z^chE~wQoH}h(7|8u2!{Sh0u~JyO&mKoTaxMcQw370P{8zN))XAki^g(lN7@ct--y) zLDUUKILQ_xSLSJ#UI`FdFic3sAc+YO-y*Zi_t{6mna!2umiF~3I0HnVk&3B}l6I-3 z`x!Ab1~?ircMKt%_z>{j<1?$PtD8OaYh^fCVQtXO9`@nB!Lq}Mmz5qHET~c3Y}af~ ziFNn#CTfXdVE_a9*UL{pfUT(Vb1UV?Xoxbg&_1;QcYbpQh&=iFt6&ls1Wh81f>c%yn5A>Bcp#RL@SEUIKmx{Frx5 zo2rPsIv)fd6OmK+x_sgoSid%~er;mS(DG8ECCyGJylk~fpKP`2<*QY=S5}&g7pqlB zgr6%xGu%)N_HGVX_*mYZ5V3AHd~f$ts6Z9ok`5D zrRytp?Zh!JQSYpvODQqGy(DwJV#BT0E`ti`X92F#dIszFNF74mDuEzJP?Crk=D1mB z+x+lJ0bhaR5k$IZ*EVhIRqK6+*3B+p>L$9$rBn|K-~3y*dgfAk1HiuxTL<21oHO2T z^elnYGXO!RRO?9*Ko9^c1H`B9viC(go>ZN;``80ZN&Q9mm-Kbd|MNmoC z+%DF5ywp6Hyz3=71$k-h%=1#>>+!OIGtW!4I<0q+8bC&R`EW=+Ug*dQUC{gDGYZE5hF2XWzF!1V+MIN-QM-1{nHPaKfdrw`p#$AQmdg+G@j{PW3VFWqh}fSBm+4 zD!I_SoKDEtc*&YtaJ>Y=L69?m<*tyT5_vOkPQ<|`o&-lP#lU6vJQyK zU9o{wp4lDjIQip#cASFLNaoGm z==zwMw;E%28pgu6Onm(5&#%4mcs<9*_>ADocLWvlAe;qbJ=Zlhe6GT_9kZ9Xm{#8* zc^0}0`9aRvFuSQG;jN_%Vp(uthA5I;YXQ&W`Q$JgXnQrip6}(Ba=p9_1TE$9o7w}} zgTLZ?fIQ~TTN@@?PP66rn<^Q$UAU39-$2WyftE9V%ikKdT>Lt#_AxcVuaFX{=YW^RJrgd8BqMjraJwb7^A3*sy@< z{L-$T*|Rn*5a{k+o_Kw~dFh8C?F-V9rRKsuPJW&GI2!L`F9)U=o2?`;v0T7hk#E2! zpq<-ZNfrj6)m1q&dnM&?xwDE{#dTa^9CZ@Z>Lp;}LR(txR1%%$)oT=ZdcMTMm{Uo( zjfHD}gpYa#5BkCMl$zq(dAO$hB`7a@{^>IpxIx7meNGHas4&mUW({?UFW-)!lI}XN zb(LpfwkC^uX)evQY%kwm`uB>@wHLydz2Z}=oqj3>jcLJACs^Z~SmXE$;hNc5r$$%2 zTgi4B9qbWlIhE2f{EgKxS!8BSQRQ)(m9Y>`yu>w6F9Elcm+D|qzUxh$Z!9dgv3;i* zwyxFP=@A79fW9M0SP~W zry$3~tkBi9JgUL$5>EeK!d+ROPJ1KJ=oS1H3%*7_+U{U^F-&9ZV5mC^>wlg(>zU71 zvY<`)q8xh=3v6M9Sl|R?Bo0`0_JbeJSz&)pAwlJh%!_F=v7YXwm(V*q@d|o%h9W`7 zlZ>M;3DZCc3*Q93$$8H7Nh-~0@>XlXvy|;6F1MTE-e37eUennRwdGq4e0Vtg4tl|qPiWmk$}0=-aGc^(b}I#<*~;7gLUU-3cKDZwGe zs2%qRsbD=ZVjZ&T1ezaa@hb$?MFcX~TAk||khYce1X9V>vqb(oPlB9MwsA3`&Iq}s zL@7ww7G$-BXGu+7rg3SC&o@YZkv!->7D^;3jW6ZjMy)Bqq2AD%cVEs;C%xQ-V6#dk zeNIMFy#=6Ek`$2B=}I0uK$cxm4OD{pEQ@@5ihLsob&3w?aV;?RU2BJ32tqVn~vHQU0@-GrTxoxHO580i%EALBP|Q|#we z3Gaob->Ag?M0Hb5rLZw=Qnp8jA2u3ekq!XU&T3YF{$yh|kcwtygNgXTR@m4Z=)^)sO5 z5)@vUA_=2fL2zB5S}639?STSO%tD|XV4b%zC^JwDh-$@tDb^Di2pN(?%p<*iFB_ni zoVBt^{tf2_stVWV26Q^BNHj4_g@S)So@~VhVom^KkjC|8d==2sM)|GBjJ^Dk2zqWA z>NR@f1TNH684LGk!fxgM%GT;+!XN3Y*GEgsP0!H)iZtb)x%#j9D4fkJ{_r^>1&imujek_Lpko= zeYc_)0oKd*V3_v` z&^Qb0#a@wW36&JYwEv@oSZaNwH_|JKFoFxG;N|I7mR-TFm*i@jmu`uF+{?m^B^HF2 z=(d~Q1my!7B{j z!{EIPzKy}3W$^6`z5{`m)|-k%*rIJ+=^zsl&7a|5k%NuIA*fM#Gi{xHjY)p$965ey z4)H4k!l`4}fM6gp>)Bpb4+JCu5T5ahKseASOUyty5#^<^>i||NY}?CrRkqS8>6AC3 zO*+S}cG_1k)3a;SNsYl076@38D+}qB1db+DN-!p?aE!NHZibxBX`Fo*-cs*l@O}p0 z%|LMW=NS`p{W-=ChJnZ=Iv{_O=tv@BB=%ab-7lYC19{?{K*P(}*j=^K?1oklX*5&` zUqr%3izt8M%1*wNTu-k9Ep$|gYf&7ya4jXo;Xzk!)^~ousSB@N0nXn-FNeG$sJkeX zo75H`=29rf@U6L6=+(G^URej7!OI5IIxD;QknnP(Wvq{O7cqZROYugC zn4EN}>iT%x1?o5b5xdX$XB93W70h%P3ev0kP6nJ9rb~iqFAev<({$Ab_~3gPd>?}k zBJlFslPvyYpyt3V$gz7*EyWv||EiXmPF< zu_VsY;%qHqDV(Xr`C7!_>V~kMP!o7H6nV3+ea~kPf2lCD<76o9$aW#?ynuv1%rYx) zA?>hOg`w5XU0>FLf%0G=k#%qY`_e8h=OROEd1*Kc!7g42N>kNc#@-5UVcj&6AaQz$Mh_uCtdVp-(BwYEEg5jMR78N6oolqPc#6q8M8y_3E<6dhejA5k z8k;dZvNeYPE|DA7eRF8e%>_+PMUf2GHe~(ULDn*5YYs zkb2wwP2shV(w`-UZ;E{L)LM0$2ZtbbR|K*Bk}&>1j+SQCk0OKy%!K+$1Q+0g>ZjCC zZ_2tbdv>z}=_mO5CmH+pk3SV~?K`_za z_&i+vI6jZHyGjBnajV)%q>@PCVh21+BNcJ5;z9$Xl99>Cz(oxuOhF@R7b=Z42Uv7- zge+)IH%9L*UP`WwVFS?2Y1nk_WxW_yr(wenfB6-~RPv_6|&TnB#UR`Y)V)?Q0+X>aRXO_rl}X*t)i(yhQu5x-OBx?;Gs3*^)^5 zYp)hpKjobj8Wk&*a#9uI&5mHZ1-6{C^_GEU*^oW$RUnd0;Bv-+T=;34;i4d!Q&VreLQk&n8sp4&CSC@A?Tx2*^D_$Q=EvLA!PUFpc}x~f#DFn5kw`>le0n9 zz|g+}Y&VS|5oiM}>F+VQDJ)@MpWc}gVCqxbVd_`$aM;W`8*~SlIy3}RK4fnMTN(e| z=Yl4HvCjgGA&ZCPT%k8Xsg#NUD+7E8MZA$X-X3AiS`8dY^H2S2l&X`y_~CB?U!k=k zBG{XYodUf?u&8W`*^Mcoi-%fu$!nLRY%?wCc(AW$fX*)9opV9gfV~W{2O~9Xp5h{G zo)Snm4|6Qp<+L4wVMTnFsXi>eBbA_d*n|L|Y5+-x;Ksr6b~-qE8g44rt8_0CJi!L` z1g%XHz#*mHFu>w+iU6$s4dME?42D5CDs8K9H^56V=yrzC^-IjcTRx!CQA3jHSB6@< zO+4oPI)gq=LYfhM{Vp~|3dmYQD0{K!oc5AeaA;okDb+bV{OrY-?pGn1B}9B}Jnk)@ zSl_k2dwmkmxDhwp?o6|r4p^+_jKbx*qc`5$38Q>;eX2JF_s2vJH+{hNP*dyJfsQ4l+#Fn4+}7Xma_#n<-g2SWa70(9 z{_f?0$7SB9h3y8vh>T5!t(V1iKW7=6@GHB|$?3DRat2~+kMiC@kMh4mz5U*+f6wG^ zGWZ+x&WRma?wVh=$8ba1;H{O^d zedQ4`;8?p$yb-buqE5dkKYQ@KK3(lx7s?u-D}N19TYmv%30+P0CP7y_K~1EqNzl~< z=xVAr3A&mhwQcFK996>*`Z++UXqfa-sJ_JHwm91~-0F*Xr?M+L(p!40_vnG{d#s1S z6%O>x0~Z;~zsa8bEe2bTFKMNS)NcA8Y3Z@YA>AU*!_*v4zxGEv=i4f;v!1>mieT8z`hk`>qI2~ zmidHS9cfl}*@vKi-7b52*K+!#7_K>}{g`PO;VH9C7q!IC>;-Q{kM%vFFX7{Xwf^>A zp2WkZ%KCvX&WxD#HhoM?gXdMu#*_a8C$$cfmgb?;aN5` zC=llPD>eGy-BWAw)X&xOWvqT;VV+mp9q}&&!t!7QHRb+c-D&B->2Eyq#67`5aeX<% zb&uR#|I{8_?s88DT!3yem1m5`OaZ+#P zgA$DlhyiWLs&jnfMF!9DX+isAE^xgyIMStV!;?@YP9Om~)H5@6z8(%G+1zVv&dR(G zBY|pMCC?p*{z0!hkr7b}4lzkse~3JeFREONY`dH3?_-a#JWHO!*FAzDVEF~^&E#qo z*QsRhFf4bjWzl6w!#TaS*B5)3b$zQlM8Is=F;*U0mJcC8m7|BP*Ik@*dlef-stM-6 zU)j&W<`M2-SV}Hn7iSeaXVF}8_5v2)vBUq;N(1{XtGJZQmL2M%;b4!)?Wo(sHrJM) z&6P+mDJJI&h{B8@{=6*57Ry+JhA2-9lJ=|Bj{{`A-xtf<VGohbp``n zB1QaMjM(njzC!`M6|X~Y!U0yRrR`?{ZF>^@9YFF4LPDJ;AVKU?-(o*5-TuRf5E3wr zZ^nUQvy=xh7JOnX`oviBi7_fDHWQ;hRt$dtQJ!GA1-e9)tGZqm7P=NPH$vCHOw^0jAaYb>>CtUTL8E9h zg;Q*Wa|owabZe+kMSfz4unp1Cg*-tSp6L~#yJ1e?cuY+A=G6Z}1FFvhf5hY;G8o1U zzH^1#fNfP2!U5h&k_s_4giAh61f}sNMKXl)-8X`qfLlhk<`(IIKrFBe|9*`f_$-4P zV4N3G?iUE$7$O1VP=14X(!*5;%AM_)$7h+W&oVimWhNtCj-H46lfovNSo*lW5x)5K zA$(EZ0uNs&Uf`))eE$!4L5S)s>8U?6M0i0_nN`>dJal^a`~czKx>fH3V$a_&v2Q;k zP*wj78|Ok$l(4^!WF0-&`TsLn|MqprI*Nz?x5@hHH%QjMyA4^#djH2w)|?W*cr#>u z1}|(Wt+l3|@g;SZ{}Kd7Urx3!!v5sH2BAMkBzSRcD#E)l8+bNcT#QZMZ${Htq%f+S z#9Xz@-m8nt(~)C`>U&jKUy?dX3g+`1ZE?%)jIS{$~avrsA4SL^1aTmMG?Y z2X{umIMg=(z=Y@e3==07>skf4z--NUjcz~xa*Y*Av@CScHuJ{kkt6o>;m-E0j) z$^N8cP-0DQODIF)ZulouDt#ES`NOT}Vo*GRoC19rGW(nL2k?i8oH$o`U zT-I7Wf*9_47<_^gzD1tc?WG>Yncqv#@Vu^_MAzqgI2Mg}vUq0T4hxQ1l4HgjD|{5B z#IIqh$`~DOJb2cu)#X;&$cW~Jki8;yr*Z$H+ zsGj4uiDST7p1daj9 zK!+E13j6}0JB4OY4$ty9C5@x{F^#^Ur2vs4XQ2ftO87m6Rh|09L94f~(P+$xty{sl z8Jw~pvMRjhs0z2smiF&1qK;OlVc~uP==S`sMU}ib;(`g`dM2<4Hc@dzx(SDKsJ3>) z{1-g^J+P)bj!0kQ$)>SHn9AlGUjn2H-~T~=e){qLW>Rk1E8r`vv3gI^QlrT6Y8 zv80Gq02~u5VEjgw3b=~|qmNioN@kZz@f)WuJpv9hdxZrEmeq4%OJ^f39luUXTi5+z z)*Xr`>p)}<)o#p>wbTPQY=XE!T>R7v6IC(DPF&(d6dJi@=O*O6cK>7`s2=F)oX^l;Vd%p zGU)gZX@(kQFvdV6_YTI!860HY5*%XeD+nIu@X`%!-1y+`jtp*O-2&&x)iu`ppAby% z9xwoBpn03z93awM5B%w*CshyA5f6Ul61q&QL< z86Vk!zdNV))bU>U0V<7C_`2VPAkd!S7{tn`jcZo0RHA*;DJShDofOt5GrsPe^>ybQ zo@5=+6}0HGjeoB9iU~a$|6mVq7x_>2@LTfNOGo(6_K+UsKitFb82{-Wes^%K6Tjp3 zE%pKY?zC@(C;Xt?lCl86@cG>1vE7eAfR^K&%P~h{)|`Nj>0e~C@W6+E^5NhAgmbF# z88XWJL9F z3UE-rtp@6#01<+Orl(8*rI&8>r7weEK;UXHUK|2O_;5Hv=$s4?0wh|qk(NQwAs%=P zeHac>?56#WNW;ZQ|Lx&@mr+39BmIswtoB8^TnB^^(msSFZV_awFAr%|9$e!%UV9mp zy!Och@A>juKl$;;>q)%F4}Zf!IDiCuPbCaO&^I>_g9=s+)*fLy>_t`T76vxn2)2{W zA>O}%X8=Z78=~T^Eec!^lQ-8!ecXg=W~fp=`~}RKNVx_{WdmPP2r(V-jwZ%AHGphs z8dVcwKVad4_9V_@T*i;Of&f>Uwyy{dHMqk^u88ARj;5P)XBr_Yt+ky2kdfyh`ZB0L zi|=WC-A^OX#~^rfA?7EpVd4Zv&yo}Yrj|nUmRhTry4*~)L3g}#jdPWq)s(jWVC<&XQ>#yQv-%cFnJtgwiYJ=Gc^K1aS}UjyK$Q_H9p1)-VQkW)w}q!;QnE+whl_cX@)o^0HeMFQ6M zK22M3t`%AU&pLQVQQsdV4vMLEfOA~`bi~Jb?24yhmq87OZ6biJs9_7?GDCL0A&@$K zki?D_(h?&KM^UWmWlUYr`(MOmQse`5h#V1;ojS-QSyvHZ>J$RJgnP8APiY~R5fS1Q zv{bFiZK#=ZXLN+;P+^ogI%T3Q-oA8xWqGJ|wus7?3GHq~3@B$K7Tpn9>oClXaVTxK zn`;xpkXk33@k1dLvfNofZJV%QeTvmox}KK%U#anv^+3mIsyBX{mXL}laAL+!&LK?2n(>l-Zc=x!%ZCx*aGlJ?!%%W}fS18u^RklL+*~UKJrpDkcNM*)JD6CVjdk#(h;=!lB*K;#Z}Kv?M7Xm93_!swB`%@PU`>9Hmhp%G%i_volT0>W-rQWP9d4fg&vy!9dW zo+^*-{RDP$uH}OMUk>|@QhhfWRdY;ooQ=^BFh7P<7ES4@k^2zI+R_XE>IF^WNewXx zSdW|NasC#jZ0EQZf|X?F__;HKuyTk$LRdNb7}i7t%BNx~70K{4zGA=nFak=^7BCr8 zU@(p^JjoORdP3D0KZzIhsVbr!T1{M(SFBFM*E=iLW*9jNA0v#MC9yj`#N$FZN1+7f zcOOFff+${P%IBElS}s$jkS$cpQM+N|A6(%cbsSyA|0(%+q_N%ppbZq^Epi9YqPmqq z83Aq-ppqB4#_S$G+QWcaotMS=Nt}Dc;Ym^F_Va;gdUd`gjwYGr7MY~nP-G)TYSMHm zJb8@CgDlby4l%h0!CMG;8Ii1&w6ck8BJYQJx}e6hli5r*3s+PU*hW2uB1U0Le+%P1 z2IN1r&fr@ayo&+(W%k0XB&IX7I1eY;uLcEp)aoxV@fR6<4@+aSz2r#2A7u82nLVri z8soxye;qMwdy)*sb)`~Pp6eHI4aINpA*V#+mlatn>V4g3K|#J2Msa7dY|Is!iO|tOJ|Ho9*~j!1{wE2A`vC;WcNx!Y zXVDL{@+~u+ebt#$wa diff --git a/analysis-master/analysis/__pycache__/analysis.cpython-37.pyc b/analysis-master/analysis/__pycache__/analysis.cpython-37.pyc deleted file mode 100644 index b9d52e8ee9a5a977a701ce6545d86f8bcda85344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26681 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⋘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 Oy)oZ7jNiiI@qYn3mzW#? diff --git a/analysis-master/analysis/__pycache__/titanlearn.cpython-37.pyc b/analysis-master/analysis/__pycache__/titanlearn.cpython-37.pyc deleted file mode 100644 index 20d728b4f689d96a58a67d5efa62afb6878a2386..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3058 zcmbUj%WfRUvAgHFv%BPyVk|pxl1X4ASVSf%#)ttz7A#wUfI#6z1`G@$42ILa%UR9N zth#$7YB8HbD8MHJ{si*qKk&EoC5N1R$p;uYq^kEpnn7|%54yUhs;8@}>ouSE`w@Zh z-Cw_d{|G<~^Z~^%YrfLCjk`PW4%2~p=!`(4U9PV+ybQ2fWp!BAG;!lGlpoE9q zc}tS;3E>?cy(K&n?xZt`E}Wzbv@Y)ftp~K;q(9jL>NdZ_xA^v3nhf}WyYTG}-{HHk z?sESb8Sc$HNBf8P9NwS*JwMB;u_){DakQZxM=^|#;*4{_!PmIX4~O< zTV79RrO+al;3aejdLPQrY8fic$rh6l<9kRZ)%O zvaV;*=9`tgrB-8637MfraXpG3ot!+o7oTRT$m3^E9&b*F<_{l9eb&hMX%j!lELt|7 zXA@l~R)*Bj^gALu zfdBa9;H;jCgRE>0WPMtL_Ub^(qr;=4j}$1T#rVwu&vX`NRaU-Hg*w*FL;Y8&FAT*>A+h~sz2LTNA0RTZDLP^5DCL_whg0~?=DMTwolKVArN#cPGo^eiu z&QX0FxZ)u=)h+-{G;PTRyJT0uv}V(jp<_JN%!HI(*kZyo1%K-_1*2Uh$`P!cG|kIQ zsWi1$AThIKAHY4B3U8Uvc@ISIuRqQ$5$L$09$&yzc$X`w_8_?9Eg%`MsAi2`o{e{G z1(;w|%h#FY2l(kqNEd~y=tCcsMW?5!iPCgh^QJ_+m!>b9tXy)eo^l7p^jBu&qAb^@ zptvEG+iR)z5`K>oQQ468981aJWi#+TaVRwZe>n!r2sp^I4#IQ~On{-GZaE2!vrc*k z5OmUnO`M5)K`*_Vj`AfGGPo^W%lyoEjV?-MTs+VOkC(?9$=?8;+bRPHra}nQDt!c(-Z~R< zNw3Jl=X4Rk498^A`Iep!G;5($J|^GN=WG$3|4Tcq!|4TkOrHPqFKp3W^x7V$?~zNl zC0BIOhc~u)+iKlq_`+56q0DZ#Uc23HUAo$z1Xpm%V$cpQLkw`(*|BTD*(YQXv>|Y! zD*|}fb}qYZctt7Ey$jf@``l^CU2=hxK6mw2+r6N73HR<2y`{HXAM6?Y0{E8d11CUE z8(r^0t2QTuz#BjdzovA661D-JvE23YWw0j~kUw^=mo%c5_vaXKf z`QA*{V=0t6jv)zFZShBXFn8iMHlCQ(`PqCgZ=@8JhBgU70lgKs&wH||R=rlK`OXLm zu{wiJy%sRsSq1?{uJI)PESQdf5JEA+FkL5|xsY`_mKnFfWV{iCgHFQJEPsK;6l%H_ zGvi`@8b6!OM8$0x=m2_)TBDq@l*5mM8Vv@yqN~ zlqw18MnkhuiHo1#N&tzK^bt(84}h?Uy7;hNw#T;U77JNOcVUJuRCXFspY`bHbeFyl zv`vCwas)7Kf;n;%gm)XX ztfd#&0-Te%pEo=^K1`f^)(rh+(qofLTx2A@LZ#WOtN?>cA%6!9Yt}H7hlpbVmw!Nj zX)W&~uvGz%kXW#8U5bg6=GI?Aj)1MOixBoM+zvi2lYfNwA^mcACxPO<`WZ~q1pDRs zk965$vqqoQ5^$)!nQfw2u?(w~{ZCr{F=0|9tf~_K&$c7m26|o9jALs6y3A^D oL+wq=GU0<|TYrfE912UL+jAY)W7Ky-I$)u{7rfJr-0;JH13vV&rvLx| diff --git a/analysis-master/analysis/__pycache__/trueskill.cpython-37.pyc b/analysis-master/analysis/__pycache__/trueskill.cpython-37.pyc deleted file mode 100644 index 15c7554df5d4e48dc76a35fea45586d99213951b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32168 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 NJm;X^XUh2c{{ue", - "Jacob Levine ", -) - -__all__ = [ - 'load_csv', - 'basic_stats', - 'z_score', - 'z_normalize', - 'histo_analysis', - 'regression', - 'elo', - 'glicko2', - 'trueskill', - 'RegressionMetrics', - 'ClassificationMetrics', - 'kmeans', - 'pca', - 'decisiontree', - 'knn_classifier', - 'knn_regressor', - 'NaiveBayes', - 'SVM', - 'random_forest_classifier', - 'random_forest_regressor', - 'Glicko2', - # all statistics functions left out due to integration in other functions -] - -# now back to your regularly scheduled programming: - -# imports (now in alphabetical order! v 1.0.3.006): - -import csv -import numba -from numba import jit -import numpy as np -import scipy -from scipy import * -import sklearn -from sklearn import * -from analysis import trueskill as Trueskill - -class error(ValueError): - pass - -def load_csv(filepath): - with open(filepath, newline='') as csvfile: - file_array = np.array(list(csv.reader(csvfile))) - csvfile.close() - return file_array - -# expects 1d array -@jit(forceobj=True) -def basic_stats(data): - - data_t = np.array(data).astype(float) - - _mean = mean(data_t) - _median = median(data_t) - _stdev = stdev(data_t) - _variance = variance(data_t) - _min = npmin(data_t) - _max = npmax(data_t) - - return _mean, _median, _stdev, _variance, _min, _max - -# returns z score with inputs of point, mean and standard deviation of spread -@jit(forceobj=True) -def z_score(point, mean, stdev): - score = (point - mean) / stdev - - return score - -# expects 2d array, normalizes across all axes -@jit(forceobj=True) -def z_normalize(array, *args): - - array = np.array(array) - for arg in args: - array = sklearn.preprocessing.normalize(array, axis = arg) - - return array - -@jit(forceobj=True) -# expects 2d array of [x,y] -def histo_analysis(hist_data): - - if(len(hist_data[0]) > 2): - - hist_data = np.array(hist_data) - derivative = np.array(len(hist_data) - 1, dtype = float) - t = np.diff(hist_data) - derivative = t[1] / t[0] - np.sort(derivative) - - return basic_stats(derivative)[0], basic_stats(derivative)[3] - - else: - - return None - -def regression(inputs, outputs, args): # inputs, outputs expects N-D array - - X = np.array(inputs) - y = np.array(outputs) - - regressions = [] - - if 'lin' in args: # formula: ax + b - - try: - - def func(x, a, b): - - return a * x + b - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - if 'log' in args: # formula: a log (b(x + c)) + d - - try: - - def func(x, a, b, c, d): - - return a * np.log(b*(x + c)) + d - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - if 'exp' in args: # formula: a e ^ (b(x + c)) + d - - try: - - def func(x, a, b, c, d): - - return a * np.exp(b*(x + c)) + d - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - if 'ply' in args: # formula: a + bx^1 + cx^2 + dx^3 + ... - - inputs = np.array([inputs]) - outputs = np.array([outputs]) - - plys = [] - limit = len(outputs[0]) - - for i in range(2, limit): - - model = sklearn.preprocessing.PolynomialFeatures(degree = i) - model = sklearn.pipeline.make_pipeline(model, sklearn.linear_model.LinearRegression()) - model = model.fit(np.rot90(inputs), np.rot90(outputs)) - - params = model.steps[1][1].intercept_.tolist() - params = np.append(params, model.steps[1][1].coef_[0].tolist()[1::]) - params.flatten() - params = params.tolist() - - plys.append(params) - - regressions.append(plys) - - if 'sig' in args: # formula: a tanh (b(x + c)) + d - - try: - - def func(x, a, b, c, d): - - return a * np.tanh(b*(x + c)) + d - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - return regressions - -def elo(starting_score, opposing_score, observed, N, K): - - expected = 1/(1+10**((np.array(opposing_score) - starting_score)/N)) - - return starting_score + K*(np.sum(observed) - np.sum(expected)) - -def glicko2(starting_score, starting_rd, starting_vol, opposing_score, opposing_rd, observations): - - player = Glicko2(rating = starting_score, rd = starting_rd, vol = starting_vol) - - player.update_player([x for x in opposing_score], [x for x in opposing_rd], observations) - - return (player.rating, player.rd, player.vol) - -def trueskill(teams_data, observations): # teams_data is array of array of tuples ie. [[(mu, sigma), (mu, sigma), (mu, sigma)], [(mu, sigma), (mu, sigma), (mu, sigma)]] - - team_ratings = [] - - for team in teams_data: - team_temp = [] - for player in team: - player = Trueskill.Rating(player[0], player[1]) - team_temp.append(player) - team_ratings.append(team_temp) - - return Trueskill.rate(teams_data, observations) - -class RegressionMetrics(): - - def __new__(cls, predictions, targets): - - return cls.r_squared(cls, predictions, targets), cls.mse(cls, predictions, targets), cls.rms(cls, predictions, targets) - - def r_squared(self, predictions, targets): # assumes equal size inputs - - return sklearn.metrics.r2_score(targets, predictions) - - def mse(self, predictions, targets): - - return sklearn.metrics.mean_squared_error(targets, predictions) - - def rms(self, predictions, targets): - - return math.sqrt(sklearn.metrics.mean_squared_error(targets, predictions)) - -class ClassificationMetrics(): - - def __new__(cls, predictions, targets): - - return cls.cm(cls, predictions, targets), cls.cr(cls, predictions, targets) - - def cm(self, predictions, targets): - - return sklearn.metrics.confusion_matrix(targets, predictions) - - def cr(self, predictions, targets): - - return sklearn.metrics.classification_report(targets, predictions) - -@jit(nopython=True) -def mean(data): - - return np.mean(data) - -@jit(nopython=True) -def median(data): - - return np.median(data) - -@jit(nopython=True) -def stdev(data): - - return np.std(data) - -@jit(nopython=True) -def variance(data): - - return np.var(data) - -@jit(nopython=True) -def npmin(data): - - return np.amin(data) - -@jit(nopython=True) -def npmax(data): - - return np.amax(data) - -@jit(forceobj=True) -def kmeans(data, n_clusters=8, init="k-means++", n_init=10, max_iter=300, tol=0.0001, precompute_distances="auto", verbose=0, random_state=None, copy_x=True, n_jobs=None, algorithm="auto"): - - kernel = sklearn.cluster.KMeans(n_clusters = n_clusters, init = init, n_init = n_init, max_iter = max_iter, tol = tol, precompute_distances = precompute_distances, verbose = verbose, random_state = random_state, copy_x = copy_x, n_jobs = n_jobs, algorithm = algorithm) - kernel.fit(data) - predictions = kernel.predict(data) - centers = kernel.cluster_centers_ - - return centers, predictions - -@jit(forceobj=True) -def pca(data, n_components = None, copy = True, whiten = False, svd_solver = "auto", tol = 0.0, iterated_power = "auto", random_state = None): - - kernel = sklearn.decomposition.PCA(n_components = n_components, copy = copy, whiten = whiten, svd_solver = svd_solver, tol = tol, iterated_power = iterated_power, random_state = random_state) - - return kernel.fit_transform(data) - -@jit(forceobj=True) -def decisiontree(data, labels, test_size = 0.3, criterion = "gini", splitter = "default", max_depth = None): #expects *2d data and 1d labels - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.tree.DecisionTreeClassifier(criterion = criterion, splitter = splitter, max_depth = max_depth) - model = model.fit(data_train,labels_train) - predictions = model.predict(data_test) - metrics = ClassificationMetrics(predictions, labels_test) - - return model, metrics - -@jit(forceobj=True) -def knn_classifier(data, labels, test_size = 0.3, algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=None, n_neighbors=5, p=2, weights='uniform'): #expects *2d data and 1d labels post-scaling - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.neighbors.KNeighborsClassifier() - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - -def knn_regressor(data, outputs, test_size, n_neighbors = 5, weights = "uniform", algorithm = "auto", leaf_size = 30, p = 2, metric = "minkowski", metric_params = None, n_jobs = None): - - data_train, data_test, outputs_train, outputs_test = sklearn.model_selection.train_test_split(data, outputs, test_size=test_size, random_state=1) - model = sklearn.neighbors.KNeighborsRegressor(n_neighbors = n_neighbors, weights = weights, algorithm = algorithm, leaf_size = leaf_size, p = p, metric = metric, metric_params = metric_params, n_jobs = n_jobs) - model.fit(data_train, outputs_train) - predictions = model.predict(data_test) - - return model, RegressionMetrics(predictions, outputs_test) - -class NaiveBayes: - - def guassian(self, data, labels, test_size = 0.3, priors = None, var_smoothing = 1e-09): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.GaussianNB(priors = priors, var_smoothing = var_smoothing) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - - def multinomial(self, data, labels, test_size = 0.3, alpha=1.0, fit_prior=True, class_prior=None): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.MultinomialNB(alpha = alpha, fit_prior = fit_prior, class_prior = class_prior) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - - def bernoulli(self, data, labels, test_size = 0.3, alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.BernoulliNB(alpha = alpha, binarize = binarize, fit_prior = fit_prior, class_prior = class_prior) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - - def complement(self, data, labels, test_size = 0.3, alpha=1.0, fit_prior=True, class_prior=None, norm=False): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.ComplementNB(alpha = alpha, fit_prior = fit_prior, class_prior = class_prior, norm = norm) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - -class SVM: - - class CustomKernel: - - def __new__(cls, C, kernel, degre, gamma, coef0, shrinking, probability, tol, cache_size, class_weight, verbose, max_iter, decision_function_shape, random_state): - - return sklearn.svm.SVC(C = C, kernel = kernel, gamma = gamma, coef0 = coef0, shrinking = shrinking, probability = probability, tol = tol, cache_size = cache_size, class_weight = class_weight, verbose = verbose, max_iter = max_iter, decision_function_shape = decision_function_shape, random_state = random_state) - - class StandardKernel: - - def __new__(cls, kernel, C=1.0, degree=3, gamma='auto_deprecated', coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape='ovr', random_state=None): - - return sklearn.svm.SVC(C = C, kernel = kernel, gamma = gamma, coef0 = coef0, shrinking = shrinking, probability = probability, tol = tol, cache_size = cache_size, class_weight = class_weight, verbose = verbose, max_iter = max_iter, decision_function_shape = decision_function_shape, random_state = random_state) - - class PrebuiltKernel: - - class Linear: - - def __new__(cls): - - return sklearn.svm.SVC(kernel = 'linear') - - class Polynomial: - - def __new__(cls, power, r_bias): - - return sklearn.svm.SVC(kernel = 'polynomial', degree = power, coef0 = r_bias) - - class RBF: - - def __new__(cls, gamma): - - return sklearn.svm.SVC(kernel = 'rbf', gamma = gamma) - - class Sigmoid: - - def __new__(cls, r_bias): - - return sklearn.svm.SVC(kernel = 'sigmoid', coef0 = r_bias) - - def fit(self, kernel, train_data, train_outputs): # expects *2d data, 1d labels or outputs - - return kernel.fit(train_data, train_outputs) - - def eval_classification(self, kernel, test_data, test_outputs): - - predictions = kernel.predict(test_data) - - return ClassificationMetrics(predictions, test_outputs) - - def eval_regression(self, kernel, test_data, test_outputs): - - predictions = kernel.predict(test_data) - - return RegressionMetrics(predictions, test_outputs) - -def random_forest_classifier(data, labels, test_size, n_estimators="warn", criterion="gini", max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features="auto", max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - kernel = sklearn.ensemble.RandomForestClassifier(n_estimators = n_estimators, criterion = criterion, max_depth = max_depth, min_samples_split = min_samples_split, min_samples_leaf = min_samples_leaf, min_weight_fraction_leaf = min_weight_fraction_leaf, max_leaf_nodes = max_leaf_nodes, min_impurity_decrease = min_impurity_decrease, bootstrap = bootstrap, oob_score = oob_score, n_jobs = n_jobs, random_state = random_state, verbose = verbose, warm_start = warm_start, class_weight = class_weight) - kernel.fit(data_train, labels_train) - predictions = kernel.predict(data_test) - - return kernel, ClassificationMetrics(predictions, labels_test) - -def random_forest_regressor(data, outputs, test_size, n_estimators="warn", criterion="mse", max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features="auto", max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False): - - data_train, data_test, outputs_train, outputs_test = sklearn.model_selection.train_test_split(data, outputs, test_size=test_size, random_state=1) - kernel = sklearn.ensemble.RandomForestRegressor(n_estimators = n_estimators, criterion = criterion, max_depth = max_depth, min_samples_split = min_samples_split, min_weight_fraction_leaf = min_weight_fraction_leaf, max_features = max_features, max_leaf_nodes = max_leaf_nodes, min_impurity_decrease = min_impurity_decrease, min_impurity_split = min_impurity_split, bootstrap = bootstrap, oob_score = oob_score, n_jobs = n_jobs, random_state = random_state, verbose = verbose, warm_start = warm_start) - kernel.fit(data_train, outputs_train) - predictions = kernel.predict(data_test) - - return kernel, RegressionMetrics(predictions, outputs_test) - -class Glicko2: - - _tau = 0.5 - - def getRating(self): - return (self.__rating * 173.7178) + 1500 - - def setRating(self, rating): - self.__rating = (rating - 1500) / 173.7178 - - rating = property(getRating, setRating) - - def getRd(self): - return self.__rd * 173.7178 - - def setRd(self, rd): - self.__rd = rd / 173.7178 - - rd = property(getRd, setRd) - - def __init__(self, rating = 1500, rd = 350, vol = 0.06): - - self.setRating(rating) - self.setRd(rd) - self.vol = vol - - def _preRatingRD(self): - - self.__rd = math.sqrt(math.pow(self.__rd, 2) + math.pow(self.vol, 2)) - - def update_player(self, rating_list, RD_list, outcome_list): - - rating_list = [(x - 1500) / 173.7178 for x in rating_list] - RD_list = [x / 173.7178 for x in RD_list] - - v = self._v(rating_list, RD_list) - self.vol = self._newVol(rating_list, RD_list, outcome_list, v) - self._preRatingRD() - - self.__rd = 1 / math.sqrt((1 / math.pow(self.__rd, 2)) + (1 / v)) - - tempSum = 0 - for i in range(len(rating_list)): - tempSum += self._g(RD_list[i]) * \ - (outcome_list[i] - self._E(rating_list[i], RD_list[i])) - self.__rating += math.pow(self.__rd, 2) * tempSum - - - def _newVol(self, rating_list, RD_list, outcome_list, v): - - i = 0 - delta = self._delta(rating_list, RD_list, outcome_list, v) - a = math.log(math.pow(self.vol, 2)) - tau = self._tau - x0 = a - x1 = 0 - - while x0 != x1: - # New iteration, so x(i) becomes x(i-1) - x0 = x1 - d = math.pow(self.__rating, 2) + v + math.exp(x0) - h1 = -(x0 - a) / math.pow(tau, 2) - 0.5 * math.exp(x0) \ - / d + 0.5 * math.exp(x0) * math.pow(delta / d, 2) - h2 = -1 / math.pow(tau, 2) - 0.5 * math.exp(x0) * \ - (math.pow(self.__rating, 2) + v) \ - / math.pow(d, 2) + 0.5 * math.pow(delta, 2) * math.exp(x0) \ - * (math.pow(self.__rating, 2) + v - math.exp(x0)) / math.pow(d, 3) - x1 = x0 - (h1 / h2) - - return math.exp(x1 / 2) - - def _delta(self, rating_list, RD_list, outcome_list, v): - - tempSum = 0 - for i in range(len(rating_list)): - tempSum += self._g(RD_list[i]) * (outcome_list[i] - self._E(rating_list[i], RD_list[i])) - return v * tempSum - - def _v(self, rating_list, RD_list): - - tempSum = 0 - for i in range(len(rating_list)): - tempE = self._E(rating_list[i], RD_list[i]) - tempSum += math.pow(self._g(RD_list[i]), 2) * tempE * (1 - tempE) - return 1 / tempSum - - def _E(self, p2rating, p2RD): - - return 1 / (1 + math.exp(-1 * self._g(p2RD) * \ - (self.__rating - p2rating))) - - def _g(self, RD): - - return 1 / math.sqrt(1 + 3 * math.pow(RD, 2) / math.pow(math.pi, 2)) - - def did_not_compete(self): - - self._preRatingRD() diff --git a/analysis-master/analysis/regression.py b/analysis-master/analysis/regression.py deleted file mode 100644 index e899e9ff..00000000 --- a/analysis-master/analysis/regression.py +++ /dev/null @@ -1,220 +0,0 @@ -# Titan Robotics Team 2022: CUDA-based Regressions Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this module has been automatically inegrated into analysis.py, and should be callable as a class from the package -# this module is cuda-optimized and vectorized (except for one small part) -# setup: - -__version__ = "1.0.0.004" - -# changelog should be viewed using print(analysis.regression.__changelog__) -__changelog__ = """ - 1.0.0.004: - - bug fixes - - fixed changelog - 1.0.0.003: - - bug fixes - 1.0.0.002: - -Added more parameters to log, exponential, polynomial - -Added SigmoidalRegKernelArthur, because Arthur apparently needs - to train the scaling and shifting of sigmoids - 1.0.0.001: - -initial release, with linear, log, exponential, polynomial, and sigmoid kernels - -already vectorized (except for polynomial generation) and CUDA-optimized -""" - -__author__ = ( - "Jacob Levine ", - "Arthur Lu " -) - -__all__ = [ - 'factorial', - 'take_all_pwrs', - 'num_poly_terms', - 'set_device', - 'LinearRegKernel', - 'SigmoidalRegKernel', - 'LogRegKernel', - 'PolyRegKernel', - 'ExpRegKernel', - 'SigmoidalRegKernelArthur', - 'SGDTrain', - 'CustomTrain' -] - -import torch - -global device - -device = "cuda:0" if torch.torch.cuda.is_available() else "cpu" - -#todo: document completely - -def set_device(self, new_device): - device=new_device - -class LinearRegKernel(): - parameters= [] - weights=None - bias=None - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.bias] - def forward(self,mtx): - long_bias=self.bias.repeat([1,mtx.size()[1]]) - return torch.matmul(self.weights,mtx)+long_bias - -class SigmoidalRegKernel(): - parameters= [] - weights=None - bias=None - sigmoid=torch.nn.Sigmoid() - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.bias] - def forward(self,mtx): - long_bias=self.bias.repeat([1,mtx.size()[1]]) - return self.sigmoid(torch.matmul(self.weights,mtx)+long_bias) - -class SigmoidalRegKernelArthur(): - parameters= [] - weights=None - in_bias=None - scal_mult=None - out_bias=None - sigmoid=torch.nn.Sigmoid() - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.in_bias=torch.rand(1, requires_grad=True, device=device) - self.scal_mult=torch.rand(1, requires_grad=True, device=device) - self.out_bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.in_bias, self.scal_mult, self.out_bias] - def forward(self,mtx): - long_in_bias=self.in_bias.repeat([1,mtx.size()[1]]) - long_out_bias=self.out_bias.repeat([1,mtx.size()[1]]) - return (self.scal_mult*self.sigmoid(torch.matmul(self.weights,mtx)+long_in_bias))+long_out_bias - -class LogRegKernel(): - parameters= [] - weights=None - in_bias=None - scal_mult=None - out_bias=None - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.in_bias=torch.rand(1, requires_grad=True, device=device) - self.scal_mult=torch.rand(1, requires_grad=True, device=device) - self.out_bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.in_bias, self.scal_mult, self.out_bias] - def forward(self,mtx): - long_in_bias=self.in_bias.repeat([1,mtx.size()[1]]) - long_out_bias=self.out_bias.repeat([1,mtx.size()[1]]) - return (self.scal_mult*torch.log(torch.matmul(self.weights,mtx)+long_in_bias))+long_out_bias - -class ExpRegKernel(): - parameters= [] - weights=None - in_bias=None - scal_mult=None - out_bias=None - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.in_bias=torch.rand(1, requires_grad=True, device=device) - self.scal_mult=torch.rand(1, requires_grad=True, device=device) - self.out_bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.in_bias, self.scal_mult, self.out_bias] - def forward(self,mtx): - long_in_bias=self.in_bias.repeat([1,mtx.size()[1]]) - long_out_bias=self.out_bias.repeat([1,mtx.size()[1]]) - return (self.scal_mult*torch.exp(torch.matmul(self.weights,mtx)+long_in_bias))+long_out_bias - -class PolyRegKernel(): - parameters= [] - weights=None - bias=None - power=None - def __init__(self, num_vars, power): - self.power=power - num_terms=self.num_poly_terms(num_vars, power) - self.weights=torch.rand(num_terms, requires_grad=True, device=device) - self.bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.bias] - def num_poly_terms(self,num_vars, power): - if power == 0: - return 0 - return int(self.factorial(num_vars+power-1) / self.factorial(power) / self.factorial(num_vars-1)) + self.num_poly_terms(num_vars, power-1) - def factorial(self,n): - if n==0: - return 1 - else: - return n*self.factorial(n-1) - def take_all_pwrs(self, vec, pwr): - #todo: vectorize (kinda) - combins=torch.combinations(vec, r=pwr, with_replacement=True) - out=torch.ones(combins.size()[0]).to(device).to(torch.float) - for i in torch.t(combins).to(device).to(torch.float): - out *= i - if pwr == 1: - return out - else: - return torch.cat((out,self.take_all_pwrs(vec, pwr-1))) - def forward(self,mtx): - #TODO: Vectorize the last part - cols=[] - for i in torch.t(mtx): - cols.append(self.take_all_pwrs(i,self.power)) - new_mtx=torch.t(torch.stack(cols)) - long_bias=self.bias.repeat([1,mtx.size()[1]]) - return torch.matmul(self.weights,new_mtx)+long_bias - -def SGDTrain(self, kernel, data, ground, loss=torch.nn.MSELoss(), iterations=1000, learning_rate=.1, return_losses=False): - optim=torch.optim.SGD(kernel.parameters, lr=learning_rate) - data_cuda=data.to(device) - ground_cuda=ground.to(device) - if (return_losses): - losses=[] - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data_cuda) - ls=loss(pred,ground_cuda) - losses.append(ls.item()) - ls.backward() - optim.step() - return [kernel,losses] - else: - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data_cuda) - ls=loss(pred,ground_cuda) - ls.backward() - optim.step() - return kernel - -def CustomTrain(self, kernel, optim, data, ground, loss=torch.nn.MSELoss(), iterations=1000, return_losses=False): - data_cuda=data.to(device) - ground_cuda=ground.to(device) - if (return_losses): - losses=[] - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data) - ls=loss(pred,ground) - losses.append(ls.item()) - ls.backward() - optim.step() - return [kernel,losses] - else: - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data_cuda) - ls=loss(pred,ground_cuda) - ls.backward() - optim.step() - return kernel \ No newline at end of file diff --git a/analysis-master/analysis/titanlearn.py b/analysis-master/analysis/titanlearn.py deleted file mode 100644 index b69d36e3..00000000 --- a/analysis-master/analysis/titanlearn.py +++ /dev/null @@ -1,122 +0,0 @@ -# Titan Robotics Team 2022: ML Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this should be imported as a python module using 'import titanlearn' -# this should be included in the local directory or environment variable -# this module is optimized for multhreaded computing -# this module learns from its mistakes far faster than 2022's captains -# setup: - -__version__ = "2.0.1.001" - -#changelog should be viewed using print(analysis.__changelog__) -__changelog__ = """changelog: - 2.0.1.001: - - removed matplotlib import - - removed graphloss() - 2.0.1.000: - - added net, dataset, dataloader, and stdtrain template definitions - - added graphloss function - 2.0.0.001: - - added clear functions - 2.0.0.000: - - complete rewrite planned - - depreciated 1.0.0.xxx versions - - added simple training loop - 1.0.0.xxx: - -added generation of ANNS, basic SGD training -""" - -__author__ = ( - "Arthur Lu ," - "Jacob Levine ," - ) - -__all__ = [ - 'clear', - 'net', - 'dataset', - 'dataloader', - 'train', - 'stdtrainer', - ] - -import torch -from os import system, name -import numpy as np - -def clear(): - if name == 'nt': - _ = system('cls') - else: - _ = system('clear') - -class net(torch.nn.Module): #template for standard neural net - def __init__(self): - super(Net, self).__init__() - - def forward(self, input): - pass - -class dataset(torch.utils.data.Dataset): #template for standard dataset - - def __init__(self): - super(torch.utils.data.Dataset).__init__() - - def __getitem__(self, index): - pass - - def __len__(self): - pass - -def dataloader(dataset, batch_size, num_workers, shuffle = True): - - return torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers) - -def train(device, net, epochs, trainloader, optimizer, criterion): #expects standard dataloader, whch returns (inputs, labels) - - dataset_len = trainloader.dataset.__len__() - iter_count = 0 - running_loss = 0 - running_loss_list = [] - - for epoch in range(epochs): # loop over the dataset multiple times - - for i, data in enumerate(trainloader, 0): - - inputs = data[0].to(device) - labels = data[1].to(device) - - optimizer.zero_grad() - - outputs = net(inputs) - loss = criterion(outputs, labels.to(torch.float)) - - loss.backward() - optimizer.step() - - # monitoring steps below - - iter_count += 1 - running_loss += loss.item() - running_loss_list.append(running_loss) - clear() - - print("training on: " + device) - print("iteration: " + str(i) + "/" + str(int(dataset_len / trainloader.batch_size)) + " | " + "epoch: " + str(epoch) + "/" + str(epochs)) - print("current batch loss: " + str(loss.item)) - print("running loss: " + str(running_loss / iter_count)) - - return net, running_loss_list - print("finished training") - -def stdtrainer(net, criterion, optimizer, dataloader, epochs, batch_size): - - device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - - net = net.to(device) - criterion = criterion.to(device) - optimizer = optimizer.to(device) - trainloader = dataloader - - return train(device, net, epochs, trainloader, optimizer, criterion) \ No newline at end of file diff --git a/analysis-master/analysis/trueskill.py b/analysis-master/analysis/trueskill.py deleted file mode 100644 index 116357df..00000000 --- a/analysis-master/analysis/trueskill.py +++ /dev/null @@ -1,907 +0,0 @@ -from __future__ import absolute_import - -from itertools import chain -import math - -from six import iteritems -from six.moves import map, range, zip -from six import iterkeys - -import copy -try: - from numbers import Number -except ImportError: - Number = (int, long, float, complex) - -inf = float('inf') - -class Gaussian(object): - #: Precision, the inverse of the variance. - pi = 0 - #: Precision adjusted mean, the precision multiplied by the mean. - tau = 0 - - def __init__(self, mu=None, sigma=None, pi=0, tau=0): - if mu is not None: - if sigma is None: - raise TypeError('sigma argument is needed') - elif sigma == 0: - raise ValueError('sigma**2 should be greater than 0') - pi = sigma ** -2 - tau = pi * mu - self.pi = pi - self.tau = tau - - @property - def mu(self): - return self.pi and self.tau / self.pi - - @property - def sigma(self): - return math.sqrt(1 / self.pi) if self.pi else inf - - def __mul__(self, other): - pi, tau = self.pi + other.pi, self.tau + other.tau - return Gaussian(pi=pi, tau=tau) - - def __truediv__(self, other): - pi, tau = self.pi - other.pi, self.tau - other.tau - return Gaussian(pi=pi, tau=tau) - - __div__ = __truediv__ # for Python 2 - - def __eq__(self, other): - return self.pi == other.pi and self.tau == other.tau - - def __lt__(self, other): - return self.mu < other.mu - - def __le__(self, other): - return self.mu <= other.mu - - def __gt__(self, other): - return self.mu > other.mu - - def __ge__(self, other): - return self.mu >= other.mu - - def __repr__(self): - return 'N(mu={:.3f}, sigma={:.3f})'.format(self.mu, self.sigma) - - def _repr_latex_(self): - latex = r'\mathcal{{ N }}( {:.3f}, {:.3f}^2 )'.format(self.mu, self.sigma) - return '$%s$' % latex - -class Matrix(list): - def __init__(self, src, height=None, width=None): - if callable(src): - f, src = src, {} - size = [height, width] - if not height: - def set_height(height): - size[0] = height - size[0] = set_height - if not width: - def set_width(width): - size[1] = width - size[1] = set_width - try: - for (r, c), val in f(*size): - src[r, c] = val - except TypeError: - raise TypeError('A callable src must return an interable ' - 'which generates a tuple containing ' - 'coordinate and value') - height, width = tuple(size) - if height is None or width is None: - raise TypeError('A callable src must call set_height and ' - 'set_width if the size is non-deterministic') - if isinstance(src, list): - is_number = lambda x: isinstance(x, Number) - unique_col_sizes = set(map(len, src)) - everything_are_number = filter(is_number, sum(src, [])) - if len(unique_col_sizes) != 1 or not everything_are_number: - raise ValueError('src must be a rectangular array of numbers') - two_dimensional_array = src - elif isinstance(src, dict): - if not height or not width: - w = h = 0 - for r, c in iterkeys(src): - if not height: - h = max(h, r + 1) - if not width: - w = max(w, c + 1) - if not height: - height = h - if not width: - width = w - two_dimensional_array = [] - for r in range(height): - row = [] - two_dimensional_array.append(row) - for c in range(width): - row.append(src.get((r, c), 0)) - else: - raise TypeError('src must be a list or dict or callable') - super(Matrix, self).__init__(two_dimensional_array) - - @property - def height(self): - return len(self) - - @property - def width(self): - return len(self[0]) - - def transpose(self): - height, width = self.height, self.width - src = {} - for c in range(width): - for r in range(height): - src[c, r] = self[r][c] - return type(self)(src, height=width, width=height) - - def minor(self, row_n, col_n): - height, width = self.height, self.width - if not (0 <= row_n < height): - raise ValueError('row_n should be between 0 and %d' % height) - elif not (0 <= col_n < width): - raise ValueError('col_n should be between 0 and %d' % width) - two_dimensional_array = [] - for r in range(height): - if r == row_n: - continue - row = [] - two_dimensional_array.append(row) - for c in range(width): - if c == col_n: - continue - row.append(self[r][c]) - return type(self)(two_dimensional_array) - - def determinant(self): - height, width = self.height, self.width - if height != width: - raise ValueError('Only square matrix can calculate a determinant') - tmp, rv = copy.deepcopy(self), 1. - for c in range(width - 1, 0, -1): - pivot, r = max((abs(tmp[r][c]), r) for r in range(c + 1)) - pivot = tmp[r][c] - if not pivot: - return 0. - tmp[r], tmp[c] = tmp[c], tmp[r] - if r != c: - rv = -rv - rv *= pivot - fact = -1. / pivot - for r in range(c): - f = fact * tmp[r][c] - for x in range(c): - tmp[r][x] += f * tmp[c][x] - return rv * tmp[0][0] - - def adjugate(self): - height, width = self.height, self.width - if height != width: - raise ValueError('Only square matrix can be adjugated') - if height == 2: - a, b = self[0][0], self[0][1] - c, d = self[1][0], self[1][1] - return type(self)([[d, -b], [-c, a]]) - src = {} - for r in range(height): - for c in range(width): - sign = -1 if (r + c) % 2 else 1 - src[r, c] = self.minor(r, c).determinant() * sign - return type(self)(src, height, width) - - def inverse(self): - if self.height == self.width == 1: - return type(self)([[1. / self[0][0]]]) - return (1. / self.determinant()) * self.adjugate() - - def __add__(self, other): - height, width = self.height, self.width - if (height, width) != (other.height, other.width): - raise ValueError('Must be same size') - src = {} - for r in range(height): - for c in range(width): - src[r, c] = self[r][c] + other[r][c] - return type(self)(src, height, width) - - def __mul__(self, other): - if self.width != other.height: - raise ValueError('Bad size') - height, width = self.height, other.width - src = {} - for r in range(height): - for c in range(width): - src[r, c] = sum(self[r][x] * other[x][c] - for x in range(self.width)) - return type(self)(src, height, width) - - def __rmul__(self, other): - if not isinstance(other, Number): - raise TypeError('The operand should be a number') - height, width = self.height, self.width - src = {} - for r in range(height): - for c in range(width): - src[r, c] = other * self[r][c] - return type(self)(src, height, width) - - def __repr__(self): - return '{}({})'.format(type(self).__name__, super(Matrix, self).__repr__()) - - def _repr_latex_(self): - rows = [' && '.join(['%.3f' % cell for cell in row]) for row in self] - latex = r'\begin{matrix} %s \end{matrix}' % r'\\'.join(rows) - return '$%s$' % latex - -def _gen_erfcinv(erfc, math=math): - def erfcinv(y): - """The inverse function of erfc.""" - if y >= 2: - return -100. - elif y <= 0: - return 100. - zero_point = y < 1 - if not zero_point: - y = 2 - y - t = math.sqrt(-2 * math.log(y / 2.)) - x = -0.70711 * \ - ((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t) - for i in range(2): - err = erfc(x) - y - x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err) - return x if zero_point else -x - return erfcinv - -def _gen_ppf(erfc, math=math): - erfcinv = _gen_erfcinv(erfc, math) - def ppf(x, mu=0, sigma=1): - return mu - sigma * math.sqrt(2) * erfcinv(2 * x) - return ppf - -def erfc(x): - z = abs(x) - t = 1. / (1. + z / 2.) - r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * ( - 0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * ( - 0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * ( - -0.82215223 + t * 0.17087277 - ))) - ))) - ))) - return 2. - r if x < 0 else r - -def cdf(x, mu=0, sigma=1): - return 0.5 * erfc(-(x - mu) / (sigma * math.sqrt(2))) - - -def pdf(x, mu=0, sigma=1): - return (1 / math.sqrt(2 * math.pi) * abs(sigma) * - math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2))) - -ppf = _gen_ppf(erfc) - -def choose_backend(backend): - if backend is None: # fallback - return cdf, pdf, ppf - elif backend == 'mpmath': - try: - import mpmath - except ImportError: - raise ImportError('Install "mpmath" to use this backend') - return mpmath.ncdf, mpmath.npdf, _gen_ppf(mpmath.erfc, math=mpmath) - elif backend == 'scipy': - try: - from scipy.stats import norm - except ImportError: - raise ImportError('Install "scipy" to use this backend') - return norm.cdf, norm.pdf, norm.ppf - raise ValueError('%r backend is not defined' % backend) - -def available_backends(): - backends = [None] - for backend in ['mpmath', 'scipy']: - try: - __import__(backend) - except ImportError: - continue - backends.append(backend) - return backends - -class Node(object): - - pass - -class Variable(Node, Gaussian): - - def __init__(self): - self.messages = {} - super(Variable, self).__init__() - - def set(self, val): - delta = self.delta(val) - self.pi, self.tau = val.pi, val.tau - return delta - - def delta(self, other): - pi_delta = abs(self.pi - other.pi) - if pi_delta == inf: - return 0. - return max(abs(self.tau - other.tau), math.sqrt(pi_delta)) - - def update_message(self, factor, pi=0, tau=0, message=None): - message = message or Gaussian(pi=pi, tau=tau) - old_message, self[factor] = self[factor], message - return self.set(self / old_message * message) - - def update_value(self, factor, pi=0, tau=0, value=None): - value = value or Gaussian(pi=pi, tau=tau) - old_message = self[factor] - self[factor] = value * old_message / self - return self.set(value) - - def __getitem__(self, factor): - return self.messages[factor] - - def __setitem__(self, factor, message): - self.messages[factor] = message - - def __repr__(self): - args = (type(self).__name__, super(Variable, self).__repr__(), - len(self.messages), '' if len(self.messages) == 1 else 's') - return '<%s %s with %d connection%s>' % args - - -class Factor(Node): - - def __init__(self, variables): - self.vars = variables - for var in variables: - var[self] = Gaussian() - - def down(self): - return 0 - - def up(self): - return 0 - - @property - def var(self): - assert len(self.vars) == 1 - return self.vars[0] - - def __repr__(self): - args = (type(self).__name__, len(self.vars), - '' if len(self.vars) == 1 else 's') - return '<%s with %d connection%s>' % args - - -class PriorFactor(Factor): - - def __init__(self, var, val, dynamic=0): - super(PriorFactor, self).__init__([var]) - self.val = val - self.dynamic = dynamic - - def down(self): - sigma = math.sqrt(self.val.sigma ** 2 + self.dynamic ** 2) - value = Gaussian(self.val.mu, sigma) - return self.var.update_value(self, value=value) - - -class LikelihoodFactor(Factor): - - def __init__(self, mean_var, value_var, variance): - super(LikelihoodFactor, self).__init__([mean_var, value_var]) - self.mean = mean_var - self.value = value_var - self.variance = variance - - def calc_a(self, var): - return 1. / (1. + self.variance * var.pi) - - def down(self): - # update value. - msg = self.mean / self.mean[self] - a = self.calc_a(msg) - return self.value.update_message(self, a * msg.pi, a * msg.tau) - - def up(self): - # update mean. - msg = self.value / self.value[self] - a = self.calc_a(msg) - return self.mean.update_message(self, a * msg.pi, a * msg.tau) - - -class SumFactor(Factor): - - def __init__(self, sum_var, term_vars, coeffs): - super(SumFactor, self).__init__([sum_var] + term_vars) - self.sum = sum_var - self.terms = term_vars - self.coeffs = coeffs - - def down(self): - vals = self.terms - msgs = [var[self] for var in vals] - return self.update(self.sum, vals, msgs, self.coeffs) - - def up(self, index=0): - coeff = self.coeffs[index] - coeffs = [] - for x, c in enumerate(self.coeffs): - try: - if x == index: - coeffs.append(1. / coeff) - else: - coeffs.append(-c / coeff) - except ZeroDivisionError: - coeffs.append(0.) - vals = self.terms[:] - vals[index] = self.sum - msgs = [var[self] for var in vals] - return self.update(self.terms[index], vals, msgs, coeffs) - - def update(self, var, vals, msgs, coeffs): - pi_inv = 0 - mu = 0 - for val, msg, coeff in zip(vals, msgs, coeffs): - div = val / msg - mu += coeff * div.mu - if pi_inv == inf: - continue - try: - # numpy.float64 handles floating-point error by different way. - # For example, it can just warn RuntimeWarning on n/0 problem - # instead of throwing ZeroDivisionError. So div.pi, the - # denominator has to be a built-in float. - pi_inv += coeff ** 2 / float(div.pi) - except ZeroDivisionError: - pi_inv = inf - pi = 1. / pi_inv - tau = pi * mu - return var.update_message(self, pi, tau) - - -class TruncateFactor(Factor): - - def __init__(self, var, v_func, w_func, draw_margin): - super(TruncateFactor, self).__init__([var]) - self.v_func = v_func - self.w_func = w_func - self.draw_margin = draw_margin - - def up(self): - val = self.var - msg = self.var[self] - div = val / msg - sqrt_pi = math.sqrt(div.pi) - args = (div.tau / sqrt_pi, self.draw_margin * sqrt_pi) - v = self.v_func(*args) - w = self.w_func(*args) - denom = (1. - w) - pi, tau = div.pi / denom, (div.tau + sqrt_pi * v) / denom - return val.update_value(self, pi, tau) - -#: Default initial mean of ratings. -MU = 25. -#: Default initial standard deviation of ratings. -SIGMA = MU / 3 -#: Default distance that guarantees about 76% chance of winning. -BETA = SIGMA / 2 -#: Default dynamic factor. -TAU = SIGMA / 100 -#: Default draw probability of the game. -DRAW_PROBABILITY = .10 -#: A basis to check reliability of the result. -DELTA = 0.0001 - - -def calc_draw_probability(draw_margin, size, env=None): - if env is None: - env = global_env() - return 2 * env.cdf(draw_margin / (math.sqrt(size) * env.beta)) - 1 - - -def calc_draw_margin(draw_probability, size, env=None): - if env is None: - env = global_env() - return env.ppf((draw_probability + 1) / 2.) * math.sqrt(size) * env.beta - - -def _team_sizes(rating_groups): - team_sizes = [0] - for group in rating_groups: - team_sizes.append(len(group) + team_sizes[-1]) - del team_sizes[0] - return team_sizes - - -def _floating_point_error(env): - if env.backend == 'mpmath': - msg = 'Set "mpmath.mp.dps" to higher' - else: - msg = 'Cannot calculate correctly, set backend to "mpmath"' - return FloatingPointError(msg) - - -class Rating(Gaussian): - def __init__(self, mu=None, sigma=None): - if isinstance(mu, tuple): - mu, sigma = mu - elif isinstance(mu, Gaussian): - mu, sigma = mu.mu, mu.sigma - if mu is None: - mu = global_env().mu - if sigma is None: - sigma = global_env().sigma - super(Rating, self).__init__(mu, sigma) - - def __int__(self): - return int(self.mu) - - def __long__(self): - return long(self.mu) - - def __float__(self): - return float(self.mu) - - def __iter__(self): - return iter((self.mu, self.sigma)) - - def __repr__(self): - c = type(self) - args = ('.'.join([c.__module__, c.__name__]), self.mu, self.sigma) - return '%s(mu=%.3f, sigma=%.3f)' % args - - -class TrueSkill(object): - def __init__(self, mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, - draw_probability=DRAW_PROBABILITY, backend=None): - self.mu = mu - self.sigma = sigma - self.beta = beta - self.tau = tau - self.draw_probability = draw_probability - self.backend = backend - if isinstance(backend, tuple): - self.cdf, self.pdf, self.ppf = backend - else: - self.cdf, self.pdf, self.ppf = choose_backend(backend) - - def create_rating(self, mu=None, sigma=None): - if mu is None: - mu = self.mu - if sigma is None: - sigma = self.sigma - return Rating(mu, sigma) - - def v_win(self, diff, draw_margin): - x = diff - draw_margin - denom = self.cdf(x) - return (self.pdf(x) / denom) if denom else -x - - def v_draw(self, diff, draw_margin): - abs_diff = abs(diff) - a, b = draw_margin - abs_diff, -draw_margin - abs_diff - denom = self.cdf(a) - self.cdf(b) - numer = self.pdf(b) - self.pdf(a) - return ((numer / denom) if denom else a) * (-1 if diff < 0 else +1) - - def w_win(self, diff, draw_margin): - x = diff - draw_margin - v = self.v_win(diff, draw_margin) - w = v * (v + x) - if 0 < w < 1: - return w - raise _floating_point_error(self) - - def w_draw(self, diff, draw_margin): - abs_diff = abs(diff) - a, b = draw_margin - abs_diff, -draw_margin - abs_diff - denom = self.cdf(a) - self.cdf(b) - if not denom: - raise _floating_point_error(self) - v = self.v_draw(abs_diff, draw_margin) - return (v ** 2) + (a * self.pdf(a) - b * self.pdf(b)) / denom - - def validate_rating_groups(self, rating_groups): - # check group sizes - if len(rating_groups) < 2: - raise ValueError('Need multiple rating groups') - elif not all(rating_groups): - raise ValueError('Each group must contain multiple ratings') - # check group types - group_types = set(map(type, rating_groups)) - if len(group_types) != 1: - raise TypeError('All groups should be same type') - elif group_types.pop() is Rating: - raise TypeError('Rating cannot be a rating group') - # normalize rating_groups - if isinstance(rating_groups[0], dict): - dict_rating_groups = rating_groups - rating_groups = [] - keys = [] - for dict_rating_group in dict_rating_groups: - rating_group, key_group = [], [] - for key, rating in iteritems(dict_rating_group): - rating_group.append(rating) - key_group.append(key) - rating_groups.append(tuple(rating_group)) - keys.append(tuple(key_group)) - else: - rating_groups = list(rating_groups) - keys = None - return rating_groups, keys - - def validate_weights(self, weights, rating_groups, keys=None): - if weights is None: - weights = [(1,) * len(g) for g in rating_groups] - elif isinstance(weights, dict): - weights_dict, weights = weights, [] - for x, group in enumerate(rating_groups): - w = [] - weights.append(w) - for y, rating in enumerate(group): - if keys is not None: - y = keys[x][y] - w.append(weights_dict.get((x, y), 1)) - return weights - - def factor_graph_builders(self, rating_groups, ranks, weights): - flatten_ratings = sum(map(tuple, rating_groups), ()) - flatten_weights = sum(map(tuple, weights), ()) - size = len(flatten_ratings) - group_size = len(rating_groups) - # create variables - rating_vars = [Variable() for x in range(size)] - perf_vars = [Variable() for x in range(size)] - team_perf_vars = [Variable() for x in range(group_size)] - team_diff_vars = [Variable() for x in range(group_size - 1)] - team_sizes = _team_sizes(rating_groups) - # layer builders - def build_rating_layer(): - for rating_var, rating in zip(rating_vars, flatten_ratings): - yield PriorFactor(rating_var, rating, self.tau) - def build_perf_layer(): - for rating_var, perf_var in zip(rating_vars, perf_vars): - yield LikelihoodFactor(rating_var, perf_var, self.beta ** 2) - def build_team_perf_layer(): - for team, team_perf_var in enumerate(team_perf_vars): - if team > 0: - start = team_sizes[team - 1] - else: - start = 0 - end = team_sizes[team] - child_perf_vars = perf_vars[start:end] - coeffs = flatten_weights[start:end] - yield SumFactor(team_perf_var, child_perf_vars, coeffs) - def build_team_diff_layer(): - for team, team_diff_var in enumerate(team_diff_vars): - yield SumFactor(team_diff_var, - team_perf_vars[team:team + 2], [+1, -1]) - def build_trunc_layer(): - for x, team_diff_var in enumerate(team_diff_vars): - if callable(self.draw_probability): - # dynamic draw probability - team_perf1, team_perf2 = team_perf_vars[x:x + 2] - args = (Rating(team_perf1), Rating(team_perf2), self) - draw_probability = self.draw_probability(*args) - else: - # static draw probability - draw_probability = self.draw_probability - size = sum(map(len, rating_groups[x:x + 2])) - draw_margin = calc_draw_margin(draw_probability, size, self) - if ranks[x] == ranks[x + 1]: # is a tie? - v_func, w_func = self.v_draw, self.w_draw - else: - v_func, w_func = self.v_win, self.w_win - yield TruncateFactor(team_diff_var, - v_func, w_func, draw_margin) - # build layers - return (build_rating_layer, build_perf_layer, build_team_perf_layer, - build_team_diff_layer, build_trunc_layer) - - def run_schedule(self, build_rating_layer, build_perf_layer, - build_team_perf_layer, build_team_diff_layer, - build_trunc_layer, min_delta=DELTA): - if min_delta <= 0: - raise ValueError('min_delta must be greater than 0') - layers = [] - def build(builders): - layers_built = [list(build()) for build in builders] - layers.extend(layers_built) - return layers_built - # gray arrows - layers_built = build([build_rating_layer, - build_perf_layer, - build_team_perf_layer]) - rating_layer, perf_layer, team_perf_layer = layers_built - for f in chain(*layers_built): - f.down() - # arrow #1, #2, #3 - team_diff_layer, trunc_layer = build([build_team_diff_layer, - build_trunc_layer]) - team_diff_len = len(team_diff_layer) - for x in range(10): - if team_diff_len == 1: - # only two teams - team_diff_layer[0].down() - delta = trunc_layer[0].up() - else: - # multiple teams - delta = 0 - for x in range(team_diff_len - 1): - team_diff_layer[x].down() - delta = max(delta, trunc_layer[x].up()) - team_diff_layer[x].up(1) # up to right variable - for x in range(team_diff_len - 1, 0, -1): - team_diff_layer[x].down() - delta = max(delta, trunc_layer[x].up()) - team_diff_layer[x].up(0) # up to left variable - # repeat until to small update - if delta <= min_delta: - break - # up both ends - team_diff_layer[0].up(0) - team_diff_layer[team_diff_len - 1].up(1) - # up the remainder of the black arrows - for f in team_perf_layer: - for x in range(len(f.vars) - 1): - f.up(x) - for f in perf_layer: - f.up() - return layers - - def rate(self, rating_groups, ranks=None, weights=None, min_delta=DELTA): - rating_groups, keys = self.validate_rating_groups(rating_groups) - weights = self.validate_weights(weights, rating_groups, keys) - group_size = len(rating_groups) - if ranks is None: - ranks = range(group_size) - elif len(ranks) != group_size: - raise ValueError('Wrong ranks') - # sort rating groups by rank - by_rank = lambda x: x[1][1] - sorting = sorted(enumerate(zip(rating_groups, ranks, weights)), - key=by_rank) - sorted_rating_groups, sorted_ranks, sorted_weights = [], [], [] - for x, (g, r, w) in sorting: - sorted_rating_groups.append(g) - sorted_ranks.append(r) - # make weights to be greater than 0 - sorted_weights.append(max(min_delta, w_) for w_ in w) - # build factor graph - args = (sorted_rating_groups, sorted_ranks, sorted_weights) - builders = self.factor_graph_builders(*args) - args = builders + (min_delta,) - layers = self.run_schedule(*args) - # make result - rating_layer, team_sizes = layers[0], _team_sizes(sorted_rating_groups) - transformed_groups = [] - for start, end in zip([0] + team_sizes[:-1], team_sizes): - group = [] - for f in rating_layer[start:end]: - group.append(Rating(float(f.var.mu), float(f.var.sigma))) - transformed_groups.append(tuple(group)) - by_hint = lambda x: x[0] - unsorting = sorted(zip((x for x, __ in sorting), transformed_groups), - key=by_hint) - if keys is None: - return [g for x, g in unsorting] - # restore the structure with input dictionary keys - return [dict(zip(keys[x], g)) for x, g in unsorting] - - def quality(self, rating_groups, weights=None): - rating_groups, keys = self.validate_rating_groups(rating_groups) - weights = self.validate_weights(weights, rating_groups, keys) - flatten_ratings = sum(map(tuple, rating_groups), ()) - flatten_weights = sum(map(tuple, weights), ()) - length = len(flatten_ratings) - # a vector of all of the skill means - mean_matrix = Matrix([[r.mu] for r in flatten_ratings]) - # a matrix whose diagonal values are the variances (sigma ** 2) of each - # of the players. - def variance_matrix(height, width): - variances = (r.sigma ** 2 for r in flatten_ratings) - for x, variance in enumerate(variances): - yield (x, x), variance - variance_matrix = Matrix(variance_matrix, length, length) - # the player-team assignment and comparison matrix - def rotated_a_matrix(set_height, set_width): - t = 0 - for r, (cur, _next) in enumerate(zip(rating_groups[:-1], - rating_groups[1:])): - for x in range(t, t + len(cur)): - yield (r, x), flatten_weights[x] - t += 1 - x += 1 - for x in range(x, x + len(_next)): - yield (r, x), -flatten_weights[x] - set_height(r + 1) - set_width(x + 1) - rotated_a_matrix = Matrix(rotated_a_matrix) - a_matrix = rotated_a_matrix.transpose() - # match quality further derivation - _ata = (self.beta ** 2) * rotated_a_matrix * a_matrix - _atsa = rotated_a_matrix * variance_matrix * a_matrix - start = mean_matrix.transpose() * a_matrix - middle = _ata + _atsa - end = rotated_a_matrix * mean_matrix - # make result - e_arg = (-0.5 * start * middle.inverse() * end).determinant() - s_arg = _ata.determinant() / middle.determinant() - return math.exp(e_arg) * math.sqrt(s_arg) - - def expose(self, rating): - k = self.mu / self.sigma - return rating.mu - k * rating.sigma - - def make_as_global(self): - return setup(env=self) - - def __repr__(self): - c = type(self) - if callable(self.draw_probability): - f = self.draw_probability - draw_probability = '.'.join([f.__module__, f.__name__]) - else: - draw_probability = '%.1f%%' % (self.draw_probability * 100) - if self.backend is None: - backend = '' - elif isinstance(self.backend, tuple): - backend = ', backend=...' - else: - backend = ', backend=%r' % self.backend - args = ('.'.join([c.__module__, c.__name__]), self.mu, self.sigma, - self.beta, self.tau, draw_probability, backend) - return ('%s(mu=%.3f, sigma=%.3f, beta=%.3f, tau=%.3f, ' - 'draw_probability=%s%s)' % args) - - -def rate_1vs1(rating1, rating2, drawn=False, min_delta=DELTA, env=None): - if env is None: - env = global_env() - ranks = [0, 0 if drawn else 1] - teams = env.rate([(rating1,), (rating2,)], ranks, min_delta=min_delta) - return teams[0][0], teams[1][0] - - -def quality_1vs1(rating1, rating2, env=None): - if env is None: - env = global_env() - return env.quality([(rating1,), (rating2,)]) - - -def global_env(): - try: - global_env.__trueskill__ - except AttributeError: - # setup the default environment - setup() - return global_env.__trueskill__ - - -def setup(mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, - draw_probability=DRAW_PROBABILITY, backend=None, env=None): - if env is None: - env = TrueSkill(mu, sigma, beta, tau, draw_probability, backend) - global_env.__trueskill__ = env - return env - - -def rate(rating_groups, ranks=None, weights=None, min_delta=DELTA): - return global_env().rate(rating_groups, ranks, weights, min_delta) - - -def quality(rating_groups, weights=None): - return global_env().quality(rating_groups, weights) - - -def expose(rating): - return global_env().expose(rating) \ No newline at end of file diff --git a/analysis-master/analysis/visualization.py b/analysis-master/analysis/visualization.py deleted file mode 100644 index 72358662..00000000 --- a/analysis-master/analysis/visualization.py +++ /dev/null @@ -1,34 +0,0 @@ -# Titan Robotics Team 2022: Visualization Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this should be imported as a python module using 'import visualization' -# this should be included in the local directory or environment variable -# fancy -# setup: - -__version__ = "1.0.0.000" - -#changelog should be viewed using print(analysis.__changelog__) -__changelog__ = """changelog: - 1.0.0.000: - - created visualization.py - - added graphloss() - - added imports -""" - -__author__ = ( - "Arthur Lu ," - "Jacob Levine ," - ) - -__all__ = [ - 'graphloss', - ] - -import matplotlib.pyplot as plt - -def graphloss(losses): - - x = range(0, len(losses)) - plt.plot(x, losses) - plt.show() \ No newline at end of file diff --git a/analysis-master/build.sh b/analysis-master/build.sh deleted file mode 100755 index c6ac05d8..00000000 --- a/analysis-master/build.sh +++ /dev/null @@ -1 +0,0 @@ -python3 setup.py sdist bdist_wheel \ No newline at end of file diff --git a/analysis-master/build/lib/analysis/__init__.py b/analysis-master/build/lib/analysis/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/analysis-master/build/lib/analysis/analysis.py b/analysis-master/build/lib/analysis/analysis.py deleted file mode 100644 index c0f0de7f..00000000 --- a/analysis-master/build/lib/analysis/analysis.py +++ /dev/null @@ -1,790 +0,0 @@ -# Titan Robotics Team 2022: Data Analysis Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this should be imported as a python module using 'import analysis' -# this should be included in the local directory or environment variable -# this module has been optimized for multhreaded computing -# current benchmark of optimization: 1.33 times faster -# setup: - -__version__ = "1.1.13.006" - -# changelog should be viewed using print(analysis.__changelog__) -__changelog__ = """changelog: - 1.1.13.006: - - cleaned up imports - 1.1.13.005: - - cleaned up package - 1.1.13.004: - - small fixes to regression to improve performance - 1.1.13.003: - - filtered nans from regression - 1.1.13.002: - - removed torch requirement, and moved Regression back to regression.py - 1.1.13.001: - - bug fix with linear regression not returning a proper value - - cleaned up regression - - fixed bug with polynomial regressions - 1.1.13.000: - - fixed all regressions to now properly work - 1.1.12.006: - - fixed bg with a division by zero in histo_analysis - 1.1.12.005: - - fixed numba issues by removing numba from elo, glicko2 and trueskill - 1.1.12.004: - - renamed gliko to glicko - 1.1.12.003: - - removed depreciated code - 1.1.12.002: - - removed team first time trueskill instantiation in favor of integration in superscript.py - 1.1.12.001: - - improved readibility of regression outputs by stripping tensor data - - used map with lambda to acheive the improved readibility - - lost numba jit support with regression, and generated_jit hangs at execution - - TODO: reimplement correct numba integration in regression - 1.1.12.000: - - temporarily fixed polynomial regressions by using sklearn's PolynomialFeatures - 1.1.11.010: - - alphabeticaly ordered import lists - 1.1.11.009: - - bug fixes - 1.1.11.008: - - bug fixes - 1.1.11.007: - - bug fixes - 1.1.11.006: - - tested min and max - - bug fixes - 1.1.11.005: - - added min and max in basic_stats - 1.1.11.004: - - bug fixes - 1.1.11.003: - - bug fixes - 1.1.11.002: - - consolidated metrics - - fixed __all__ - 1.1.11.001: - - added test/train split to RandomForestClassifier and RandomForestRegressor - 1.1.11.000: - - added RandomForestClassifier and RandomForestRegressor - - note: untested - 1.1.10.000: - - added numba.jit to remaining functions - 1.1.9.002: - - kernelized PCA and KNN - 1.1.9.001: - - fixed bugs with SVM and NaiveBayes - 1.1.9.000: - - added SVM class, subclasses, and functions - - note: untested - 1.1.8.000: - - added NaiveBayes classification engine - - note: untested - 1.1.7.000: - - added knn() - - added confusion matrix to decisiontree() - 1.1.6.002: - - changed layout of __changelog to be vscode friendly - 1.1.6.001: - - added additional hyperparameters to decisiontree() - 1.1.6.000: - - fixed __version__ - - fixed __all__ order - - added decisiontree() - 1.1.5.003: - - added pca - 1.1.5.002: - - reduced import list - - added kmeans clustering engine - 1.1.5.001: - - simplified regression by using .to(device) - 1.1.5.000: - - added polynomial regression to regression(); untested - 1.1.4.000: - - added trueskill() - 1.1.3.002: - - renamed regression class to Regression, regression_engine() to regression gliko2_engine class to Gliko2 - 1.1.3.001: - - changed glicko2() to return tuple instead of array - 1.1.3.000: - - added glicko2_engine class and glicko() - - verified glicko2() accuracy - 1.1.2.003: - - fixed elo() - 1.1.2.002: - - added elo() - - elo() has bugs to be fixed - 1.1.2.001: - - readded regrression import - 1.1.2.000: - - integrated regression.py as regression class - - removed regression import - - fixed metadata for regression class - - fixed metadata for analysis class - 1.1.1.001: - - regression_engine() bug fixes, now actaully regresses - 1.1.1.000: - - added regression_engine() - - added all regressions except polynomial - 1.1.0.007: - - updated _init_device() - 1.1.0.006: - - removed useless try statements - 1.1.0.005: - - removed impossible outcomes - 1.1.0.004: - - added performance metrics (r^2, mse, rms) - 1.1.0.003: - - resolved nopython mode for mean, median, stdev, variance - 1.1.0.002: - - snapped (removed) majority of uneeded imports - - forced object mode (bad) on all jit - - TODO: stop numba complaining about not being able to compile in nopython mode - 1.1.0.001: - - removed from sklearn import * to resolve uneeded wildcard imports - 1.1.0.000: - - removed c_entities,nc_entities,obstacles,objectives from __all__ - - applied numba.jit to all functions - - depreciated and removed stdev_z_split - - cleaned up histo_analysis to include numpy and numba.jit optimizations - - depreciated and removed all regression functions in favor of future pytorch optimizer - - depreciated and removed all nonessential functions (basic_analysis, benchmark, strip_data) - - optimized z_normalize using sklearn.preprocessing.normalize - - TODO: implement kernel/function based pytorch regression optimizer - 1.0.9.000: - - refactored - - numpyed everything - - removed stats in favor of numpy functions - 1.0.8.005: - - minor fixes - 1.0.8.004: - - removed a few unused dependencies - 1.0.8.003: - - added p_value function - 1.0.8.002: - - updated __all__ correctly to contain changes made in v 1.0.8.000 and v 1.0.8.001 - 1.0.8.001: - - refactors - - bugfixes - 1.0.8.000: - - depreciated histo_analysis_old - - depreciated debug - - altered basic_analysis to take array data instead of filepath - - refactor - - optimization - 1.0.7.002: - - bug fixes - 1.0.7.001: - - bug fixes - 1.0.7.000: - - added tanh_regression (logistical regression) - - bug fixes - 1.0.6.005: - - added z_normalize function to normalize dataset - - bug fixes - 1.0.6.004: - - bug fixes - 1.0.6.003: - - bug fixes - 1.0.6.002: - - bug fixes - 1.0.6.001: - - corrected __all__ to contain all of the functions - 1.0.6.000: - - added calc_overfit, which calculates two measures of overfit, error and performance - - added calculating overfit to optimize_regression - 1.0.5.000: - - added optimize_regression function, which is a sample function to find the optimal regressions - - optimize_regression function filters out some overfit funtions (functions with r^2 = 1) - - planned addition: overfit detection in the optimize_regression function - 1.0.4.002: - - added __changelog__ - - updated debug function with log and exponential regressions - 1.0.4.001: - - added log regressions - - added exponential regressions - - added log_regression and exp_regression to __all__ - 1.0.3.008: - - added debug function to further consolidate functions - 1.0.3.007: - - added builtin benchmark function - - added builtin random (linear) data generation function - - added device initialization (_init_device) - 1.0.3.006: - - reorganized the imports list to be in alphabetical order - - added search and regurgitate functions to c_entities, nc_entities, obstacles, objectives - 1.0.3.005: - - major bug fixes - - updated historical analysis - - depreciated old historical analysis - 1.0.3.004: - - added __version__, __author__, __all__ - - added polynomial regression - - added root mean squared function - - added r squared function - 1.0.3.003: - - bug fixes - - added c_entities - 1.0.3.002: - - bug fixes - - added nc_entities, obstacles, objectives - - consolidated statistics.py to analysis.py - 1.0.3.001: - - compiled 1d, column, and row basic stats into basic stats function - 1.0.3.000: - - added historical analysis function - 1.0.2.xxx: - - added z score test - 1.0.1.xxx: - - major bug fixes - 1.0.0.xxx: - - added loading csv - - added 1d, column, row basic stats -""" - -__author__ = ( - "Arthur Lu ", - "Jacob Levine ", -) - -__all__ = [ - 'load_csv', - 'basic_stats', - 'z_score', - 'z_normalize', - 'histo_analysis', - 'regression', - 'elo', - 'glicko2', - 'trueskill', - 'RegressionMetrics', - 'ClassificationMetrics', - 'kmeans', - 'pca', - 'decisiontree', - 'knn_classifier', - 'knn_regressor', - 'NaiveBayes', - 'SVM', - 'random_forest_classifier', - 'random_forest_regressor', - 'Glicko2', - # all statistics functions left out due to integration in other functions -] - -# now back to your regularly scheduled programming: - -# imports (now in alphabetical order! v 1.0.3.006): - -import csv -import numba -from numba import jit -import numpy as np -import scipy -from scipy import * -import sklearn -from sklearn import * -from analysis import trueskill as Trueskill - -class error(ValueError): - pass - -def load_csv(filepath): - with open(filepath, newline='') as csvfile: - file_array = np.array(list(csv.reader(csvfile))) - csvfile.close() - return file_array - -# expects 1d array -@jit(forceobj=True) -def basic_stats(data): - - data_t = np.array(data).astype(float) - - _mean = mean(data_t) - _median = median(data_t) - _stdev = stdev(data_t) - _variance = variance(data_t) - _min = npmin(data_t) - _max = npmax(data_t) - - return _mean, _median, _stdev, _variance, _min, _max - -# returns z score with inputs of point, mean and standard deviation of spread -@jit(forceobj=True) -def z_score(point, mean, stdev): - score = (point - mean) / stdev - - return score - -# expects 2d array, normalizes across all axes -@jit(forceobj=True) -def z_normalize(array, *args): - - array = np.array(array) - for arg in args: - array = sklearn.preprocessing.normalize(array, axis = arg) - - return array - -@jit(forceobj=True) -# expects 2d array of [x,y] -def histo_analysis(hist_data): - - if(len(hist_data[0]) > 2): - - hist_data = np.array(hist_data) - derivative = np.array(len(hist_data) - 1, dtype = float) - t = np.diff(hist_data) - derivative = t[1] / t[0] - np.sort(derivative) - - return basic_stats(derivative)[0], basic_stats(derivative)[3] - - else: - - return None - -def regression(inputs, outputs, args): # inputs, outputs expects N-D array - - X = np.array(inputs) - y = np.array(outputs) - - regressions = [] - - if 'lin' in args: # formula: ax + b - - try: - - def func(x, a, b): - - return a * x + b - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - if 'log' in args: # formula: a log (b(x + c)) + d - - try: - - def func(x, a, b, c, d): - - return a * np.log(b*(x + c)) + d - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - if 'exp' in args: # formula: a e ^ (b(x + c)) + d - - try: - - def func(x, a, b, c, d): - - return a * np.exp(b*(x + c)) + d - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - if 'ply' in args: # formula: a + bx^1 + cx^2 + dx^3 + ... - - inputs = np.array([inputs]) - outputs = np.array([outputs]) - - plys = [] - limit = len(outputs[0]) - - for i in range(2, limit): - - model = sklearn.preprocessing.PolynomialFeatures(degree = i) - model = sklearn.pipeline.make_pipeline(model, sklearn.linear_model.LinearRegression()) - model = model.fit(np.rot90(inputs), np.rot90(outputs)) - - params = model.steps[1][1].intercept_.tolist() - params = np.append(params, model.steps[1][1].coef_[0].tolist()[1::]) - params.flatten() - params = params.tolist() - - plys.append(params) - - regressions.append(plys) - - if 'sig' in args: # formula: a tanh (b(x + c)) + d - - try: - - def func(x, a, b, c, d): - - return a * np.tanh(b*(x + c)) + d - - popt, pcov = scipy.optimize.curve_fit(func, X, y) - - regressions.append((popt.flatten().tolist(), None)) - - except Exception as e: - - pass - - return regressions - -def elo(starting_score, opposing_score, observed, N, K): - - expected = 1/(1+10**((np.array(opposing_score) - starting_score)/N)) - - return starting_score + K*(np.sum(observed) - np.sum(expected)) - -def glicko2(starting_score, starting_rd, starting_vol, opposing_score, opposing_rd, observations): - - player = Glicko2(rating = starting_score, rd = starting_rd, vol = starting_vol) - - player.update_player([x for x in opposing_score], [x for x in opposing_rd], observations) - - return (player.rating, player.rd, player.vol) - -def trueskill(teams_data, observations): # teams_data is array of array of tuples ie. [[(mu, sigma), (mu, sigma), (mu, sigma)], [(mu, sigma), (mu, sigma), (mu, sigma)]] - - team_ratings = [] - - for team in teams_data: - team_temp = [] - for player in team: - player = Trueskill.Rating(player[0], player[1]) - team_temp.append(player) - team_ratings.append(team_temp) - - return Trueskill.rate(teams_data, observations) - -class RegressionMetrics(): - - def __new__(cls, predictions, targets): - - return cls.r_squared(cls, predictions, targets), cls.mse(cls, predictions, targets), cls.rms(cls, predictions, targets) - - def r_squared(self, predictions, targets): # assumes equal size inputs - - return sklearn.metrics.r2_score(targets, predictions) - - def mse(self, predictions, targets): - - return sklearn.metrics.mean_squared_error(targets, predictions) - - def rms(self, predictions, targets): - - return math.sqrt(sklearn.metrics.mean_squared_error(targets, predictions)) - -class ClassificationMetrics(): - - def __new__(cls, predictions, targets): - - return cls.cm(cls, predictions, targets), cls.cr(cls, predictions, targets) - - def cm(self, predictions, targets): - - return sklearn.metrics.confusion_matrix(targets, predictions) - - def cr(self, predictions, targets): - - return sklearn.metrics.classification_report(targets, predictions) - -@jit(nopython=True) -def mean(data): - - return np.mean(data) - -@jit(nopython=True) -def median(data): - - return np.median(data) - -@jit(nopython=True) -def stdev(data): - - return np.std(data) - -@jit(nopython=True) -def variance(data): - - return np.var(data) - -@jit(nopython=True) -def npmin(data): - - return np.amin(data) - -@jit(nopython=True) -def npmax(data): - - return np.amax(data) - -@jit(forceobj=True) -def kmeans(data, n_clusters=8, init="k-means++", n_init=10, max_iter=300, tol=0.0001, precompute_distances="auto", verbose=0, random_state=None, copy_x=True, n_jobs=None, algorithm="auto"): - - kernel = sklearn.cluster.KMeans(n_clusters = n_clusters, init = init, n_init = n_init, max_iter = max_iter, tol = tol, precompute_distances = precompute_distances, verbose = verbose, random_state = random_state, copy_x = copy_x, n_jobs = n_jobs, algorithm = algorithm) - kernel.fit(data) - predictions = kernel.predict(data) - centers = kernel.cluster_centers_ - - return centers, predictions - -@jit(forceobj=True) -def pca(data, n_components = None, copy = True, whiten = False, svd_solver = "auto", tol = 0.0, iterated_power = "auto", random_state = None): - - kernel = sklearn.decomposition.PCA(n_components = n_components, copy = copy, whiten = whiten, svd_solver = svd_solver, tol = tol, iterated_power = iterated_power, random_state = random_state) - - return kernel.fit_transform(data) - -@jit(forceobj=True) -def decisiontree(data, labels, test_size = 0.3, criterion = "gini", splitter = "default", max_depth = None): #expects *2d data and 1d labels - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.tree.DecisionTreeClassifier(criterion = criterion, splitter = splitter, max_depth = max_depth) - model = model.fit(data_train,labels_train) - predictions = model.predict(data_test) - metrics = ClassificationMetrics(predictions, labels_test) - - return model, metrics - -@jit(forceobj=True) -def knn_classifier(data, labels, test_size = 0.3, algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=None, n_neighbors=5, p=2, weights='uniform'): #expects *2d data and 1d labels post-scaling - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.neighbors.KNeighborsClassifier() - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - -def knn_regressor(data, outputs, test_size, n_neighbors = 5, weights = "uniform", algorithm = "auto", leaf_size = 30, p = 2, metric = "minkowski", metric_params = None, n_jobs = None): - - data_train, data_test, outputs_train, outputs_test = sklearn.model_selection.train_test_split(data, outputs, test_size=test_size, random_state=1) - model = sklearn.neighbors.KNeighborsRegressor(n_neighbors = n_neighbors, weights = weights, algorithm = algorithm, leaf_size = leaf_size, p = p, metric = metric, metric_params = metric_params, n_jobs = n_jobs) - model.fit(data_train, outputs_train) - predictions = model.predict(data_test) - - return model, RegressionMetrics(predictions, outputs_test) - -class NaiveBayes: - - def guassian(self, data, labels, test_size = 0.3, priors = None, var_smoothing = 1e-09): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.GaussianNB(priors = priors, var_smoothing = var_smoothing) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - - def multinomial(self, data, labels, test_size = 0.3, alpha=1.0, fit_prior=True, class_prior=None): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.MultinomialNB(alpha = alpha, fit_prior = fit_prior, class_prior = class_prior) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - - def bernoulli(self, data, labels, test_size = 0.3, alpha=1.0, binarize=0.0, fit_prior=True, class_prior=None): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.BernoulliNB(alpha = alpha, binarize = binarize, fit_prior = fit_prior, class_prior = class_prior) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - - def complement(self, data, labels, test_size = 0.3, alpha=1.0, fit_prior=True, class_prior=None, norm=False): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - model = sklearn.naive_bayes.ComplementNB(alpha = alpha, fit_prior = fit_prior, class_prior = class_prior, norm = norm) - model.fit(data_train, labels_train) - predictions = model.predict(data_test) - - return model, ClassificationMetrics(predictions, labels_test) - -class SVM: - - class CustomKernel: - - def __new__(cls, C, kernel, degre, gamma, coef0, shrinking, probability, tol, cache_size, class_weight, verbose, max_iter, decision_function_shape, random_state): - - return sklearn.svm.SVC(C = C, kernel = kernel, gamma = gamma, coef0 = coef0, shrinking = shrinking, probability = probability, tol = tol, cache_size = cache_size, class_weight = class_weight, verbose = verbose, max_iter = max_iter, decision_function_shape = decision_function_shape, random_state = random_state) - - class StandardKernel: - - def __new__(cls, kernel, C=1.0, degree=3, gamma='auto_deprecated', coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape='ovr', random_state=None): - - return sklearn.svm.SVC(C = C, kernel = kernel, gamma = gamma, coef0 = coef0, shrinking = shrinking, probability = probability, tol = tol, cache_size = cache_size, class_weight = class_weight, verbose = verbose, max_iter = max_iter, decision_function_shape = decision_function_shape, random_state = random_state) - - class PrebuiltKernel: - - class Linear: - - def __new__(cls): - - return sklearn.svm.SVC(kernel = 'linear') - - class Polynomial: - - def __new__(cls, power, r_bias): - - return sklearn.svm.SVC(kernel = 'polynomial', degree = power, coef0 = r_bias) - - class RBF: - - def __new__(cls, gamma): - - return sklearn.svm.SVC(kernel = 'rbf', gamma = gamma) - - class Sigmoid: - - def __new__(cls, r_bias): - - return sklearn.svm.SVC(kernel = 'sigmoid', coef0 = r_bias) - - def fit(self, kernel, train_data, train_outputs): # expects *2d data, 1d labels or outputs - - return kernel.fit(train_data, train_outputs) - - def eval_classification(self, kernel, test_data, test_outputs): - - predictions = kernel.predict(test_data) - - return ClassificationMetrics(predictions, test_outputs) - - def eval_regression(self, kernel, test_data, test_outputs): - - predictions = kernel.predict(test_data) - - return RegressionMetrics(predictions, test_outputs) - -def random_forest_classifier(data, labels, test_size, n_estimators="warn", criterion="gini", max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features="auto", max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None): - - data_train, data_test, labels_train, labels_test = sklearn.model_selection.train_test_split(data, labels, test_size=test_size, random_state=1) - kernel = sklearn.ensemble.RandomForestClassifier(n_estimators = n_estimators, criterion = criterion, max_depth = max_depth, min_samples_split = min_samples_split, min_samples_leaf = min_samples_leaf, min_weight_fraction_leaf = min_weight_fraction_leaf, max_leaf_nodes = max_leaf_nodes, min_impurity_decrease = min_impurity_decrease, bootstrap = bootstrap, oob_score = oob_score, n_jobs = n_jobs, random_state = random_state, verbose = verbose, warm_start = warm_start, class_weight = class_weight) - kernel.fit(data_train, labels_train) - predictions = kernel.predict(data_test) - - return kernel, ClassificationMetrics(predictions, labels_test) - -def random_forest_regressor(data, outputs, test_size, n_estimators="warn", criterion="mse", max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features="auto", max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False): - - data_train, data_test, outputs_train, outputs_test = sklearn.model_selection.train_test_split(data, outputs, test_size=test_size, random_state=1) - kernel = sklearn.ensemble.RandomForestRegressor(n_estimators = n_estimators, criterion = criterion, max_depth = max_depth, min_samples_split = min_samples_split, min_weight_fraction_leaf = min_weight_fraction_leaf, max_features = max_features, max_leaf_nodes = max_leaf_nodes, min_impurity_decrease = min_impurity_decrease, min_impurity_split = min_impurity_split, bootstrap = bootstrap, oob_score = oob_score, n_jobs = n_jobs, random_state = random_state, verbose = verbose, warm_start = warm_start) - kernel.fit(data_train, outputs_train) - predictions = kernel.predict(data_test) - - return kernel, RegressionMetrics(predictions, outputs_test) - -class Glicko2: - - _tau = 0.5 - - def getRating(self): - return (self.__rating * 173.7178) + 1500 - - def setRating(self, rating): - self.__rating = (rating - 1500) / 173.7178 - - rating = property(getRating, setRating) - - def getRd(self): - return self.__rd * 173.7178 - - def setRd(self, rd): - self.__rd = rd / 173.7178 - - rd = property(getRd, setRd) - - def __init__(self, rating = 1500, rd = 350, vol = 0.06): - - self.setRating(rating) - self.setRd(rd) - self.vol = vol - - def _preRatingRD(self): - - self.__rd = math.sqrt(math.pow(self.__rd, 2) + math.pow(self.vol, 2)) - - def update_player(self, rating_list, RD_list, outcome_list): - - rating_list = [(x - 1500) / 173.7178 for x in rating_list] - RD_list = [x / 173.7178 for x in RD_list] - - v = self._v(rating_list, RD_list) - self.vol = self._newVol(rating_list, RD_list, outcome_list, v) - self._preRatingRD() - - self.__rd = 1 / math.sqrt((1 / math.pow(self.__rd, 2)) + (1 / v)) - - tempSum = 0 - for i in range(len(rating_list)): - tempSum += self._g(RD_list[i]) * \ - (outcome_list[i] - self._E(rating_list[i], RD_list[i])) - self.__rating += math.pow(self.__rd, 2) * tempSum - - - def _newVol(self, rating_list, RD_list, outcome_list, v): - - i = 0 - delta = self._delta(rating_list, RD_list, outcome_list, v) - a = math.log(math.pow(self.vol, 2)) - tau = self._tau - x0 = a - x1 = 0 - - while x0 != x1: - # New iteration, so x(i) becomes x(i-1) - x0 = x1 - d = math.pow(self.__rating, 2) + v + math.exp(x0) - h1 = -(x0 - a) / math.pow(tau, 2) - 0.5 * math.exp(x0) \ - / d + 0.5 * math.exp(x0) * math.pow(delta / d, 2) - h2 = -1 / math.pow(tau, 2) - 0.5 * math.exp(x0) * \ - (math.pow(self.__rating, 2) + v) \ - / math.pow(d, 2) + 0.5 * math.pow(delta, 2) * math.exp(x0) \ - * (math.pow(self.__rating, 2) + v - math.exp(x0)) / math.pow(d, 3) - x1 = x0 - (h1 / h2) - - return math.exp(x1 / 2) - - def _delta(self, rating_list, RD_list, outcome_list, v): - - tempSum = 0 - for i in range(len(rating_list)): - tempSum += self._g(RD_list[i]) * (outcome_list[i] - self._E(rating_list[i], RD_list[i])) - return v * tempSum - - def _v(self, rating_list, RD_list): - - tempSum = 0 - for i in range(len(rating_list)): - tempE = self._E(rating_list[i], RD_list[i]) - tempSum += math.pow(self._g(RD_list[i]), 2) * tempE * (1 - tempE) - return 1 / tempSum - - def _E(self, p2rating, p2RD): - - return 1 / (1 + math.exp(-1 * self._g(p2RD) * \ - (self.__rating - p2rating))) - - def _g(self, RD): - - return 1 / math.sqrt(1 + 3 * math.pow(RD, 2) / math.pow(math.pi, 2)) - - def did_not_compete(self): - - self._preRatingRD() diff --git a/analysis-master/build/lib/analysis/regression.py b/analysis-master/build/lib/analysis/regression.py deleted file mode 100644 index e899e9ff..00000000 --- a/analysis-master/build/lib/analysis/regression.py +++ /dev/null @@ -1,220 +0,0 @@ -# Titan Robotics Team 2022: CUDA-based Regressions Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this module has been automatically inegrated into analysis.py, and should be callable as a class from the package -# this module is cuda-optimized and vectorized (except for one small part) -# setup: - -__version__ = "1.0.0.004" - -# changelog should be viewed using print(analysis.regression.__changelog__) -__changelog__ = """ - 1.0.0.004: - - bug fixes - - fixed changelog - 1.0.0.003: - - bug fixes - 1.0.0.002: - -Added more parameters to log, exponential, polynomial - -Added SigmoidalRegKernelArthur, because Arthur apparently needs - to train the scaling and shifting of sigmoids - 1.0.0.001: - -initial release, with linear, log, exponential, polynomial, and sigmoid kernels - -already vectorized (except for polynomial generation) and CUDA-optimized -""" - -__author__ = ( - "Jacob Levine ", - "Arthur Lu " -) - -__all__ = [ - 'factorial', - 'take_all_pwrs', - 'num_poly_terms', - 'set_device', - 'LinearRegKernel', - 'SigmoidalRegKernel', - 'LogRegKernel', - 'PolyRegKernel', - 'ExpRegKernel', - 'SigmoidalRegKernelArthur', - 'SGDTrain', - 'CustomTrain' -] - -import torch - -global device - -device = "cuda:0" if torch.torch.cuda.is_available() else "cpu" - -#todo: document completely - -def set_device(self, new_device): - device=new_device - -class LinearRegKernel(): - parameters= [] - weights=None - bias=None - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.bias] - def forward(self,mtx): - long_bias=self.bias.repeat([1,mtx.size()[1]]) - return torch.matmul(self.weights,mtx)+long_bias - -class SigmoidalRegKernel(): - parameters= [] - weights=None - bias=None - sigmoid=torch.nn.Sigmoid() - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.bias] - def forward(self,mtx): - long_bias=self.bias.repeat([1,mtx.size()[1]]) - return self.sigmoid(torch.matmul(self.weights,mtx)+long_bias) - -class SigmoidalRegKernelArthur(): - parameters= [] - weights=None - in_bias=None - scal_mult=None - out_bias=None - sigmoid=torch.nn.Sigmoid() - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.in_bias=torch.rand(1, requires_grad=True, device=device) - self.scal_mult=torch.rand(1, requires_grad=True, device=device) - self.out_bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.in_bias, self.scal_mult, self.out_bias] - def forward(self,mtx): - long_in_bias=self.in_bias.repeat([1,mtx.size()[1]]) - long_out_bias=self.out_bias.repeat([1,mtx.size()[1]]) - return (self.scal_mult*self.sigmoid(torch.matmul(self.weights,mtx)+long_in_bias))+long_out_bias - -class LogRegKernel(): - parameters= [] - weights=None - in_bias=None - scal_mult=None - out_bias=None - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.in_bias=torch.rand(1, requires_grad=True, device=device) - self.scal_mult=torch.rand(1, requires_grad=True, device=device) - self.out_bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.in_bias, self.scal_mult, self.out_bias] - def forward(self,mtx): - long_in_bias=self.in_bias.repeat([1,mtx.size()[1]]) - long_out_bias=self.out_bias.repeat([1,mtx.size()[1]]) - return (self.scal_mult*torch.log(torch.matmul(self.weights,mtx)+long_in_bias))+long_out_bias - -class ExpRegKernel(): - parameters= [] - weights=None - in_bias=None - scal_mult=None - out_bias=None - def __init__(self, num_vars): - self.weights=torch.rand(num_vars, requires_grad=True, device=device) - self.in_bias=torch.rand(1, requires_grad=True, device=device) - self.scal_mult=torch.rand(1, requires_grad=True, device=device) - self.out_bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.in_bias, self.scal_mult, self.out_bias] - def forward(self,mtx): - long_in_bias=self.in_bias.repeat([1,mtx.size()[1]]) - long_out_bias=self.out_bias.repeat([1,mtx.size()[1]]) - return (self.scal_mult*torch.exp(torch.matmul(self.weights,mtx)+long_in_bias))+long_out_bias - -class PolyRegKernel(): - parameters= [] - weights=None - bias=None - power=None - def __init__(self, num_vars, power): - self.power=power - num_terms=self.num_poly_terms(num_vars, power) - self.weights=torch.rand(num_terms, requires_grad=True, device=device) - self.bias=torch.rand(1, requires_grad=True, device=device) - self.parameters=[self.weights,self.bias] - def num_poly_terms(self,num_vars, power): - if power == 0: - return 0 - return int(self.factorial(num_vars+power-1) / self.factorial(power) / self.factorial(num_vars-1)) + self.num_poly_terms(num_vars, power-1) - def factorial(self,n): - if n==0: - return 1 - else: - return n*self.factorial(n-1) - def take_all_pwrs(self, vec, pwr): - #todo: vectorize (kinda) - combins=torch.combinations(vec, r=pwr, with_replacement=True) - out=torch.ones(combins.size()[0]).to(device).to(torch.float) - for i in torch.t(combins).to(device).to(torch.float): - out *= i - if pwr == 1: - return out - else: - return torch.cat((out,self.take_all_pwrs(vec, pwr-1))) - def forward(self,mtx): - #TODO: Vectorize the last part - cols=[] - for i in torch.t(mtx): - cols.append(self.take_all_pwrs(i,self.power)) - new_mtx=torch.t(torch.stack(cols)) - long_bias=self.bias.repeat([1,mtx.size()[1]]) - return torch.matmul(self.weights,new_mtx)+long_bias - -def SGDTrain(self, kernel, data, ground, loss=torch.nn.MSELoss(), iterations=1000, learning_rate=.1, return_losses=False): - optim=torch.optim.SGD(kernel.parameters, lr=learning_rate) - data_cuda=data.to(device) - ground_cuda=ground.to(device) - if (return_losses): - losses=[] - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data_cuda) - ls=loss(pred,ground_cuda) - losses.append(ls.item()) - ls.backward() - optim.step() - return [kernel,losses] - else: - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data_cuda) - ls=loss(pred,ground_cuda) - ls.backward() - optim.step() - return kernel - -def CustomTrain(self, kernel, optim, data, ground, loss=torch.nn.MSELoss(), iterations=1000, return_losses=False): - data_cuda=data.to(device) - ground_cuda=ground.to(device) - if (return_losses): - losses=[] - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data) - ls=loss(pred,ground) - losses.append(ls.item()) - ls.backward() - optim.step() - return [kernel,losses] - else: - for i in range(iterations): - with torch.set_grad_enabled(True): - optim.zero_grad() - pred=kernel.forward(data_cuda) - ls=loss(pred,ground_cuda) - ls.backward() - optim.step() - return kernel \ No newline at end of file diff --git a/analysis-master/build/lib/analysis/titanlearn.py b/analysis-master/build/lib/analysis/titanlearn.py deleted file mode 100644 index b69d36e3..00000000 --- a/analysis-master/build/lib/analysis/titanlearn.py +++ /dev/null @@ -1,122 +0,0 @@ -# Titan Robotics Team 2022: ML Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this should be imported as a python module using 'import titanlearn' -# this should be included in the local directory or environment variable -# this module is optimized for multhreaded computing -# this module learns from its mistakes far faster than 2022's captains -# setup: - -__version__ = "2.0.1.001" - -#changelog should be viewed using print(analysis.__changelog__) -__changelog__ = """changelog: - 2.0.1.001: - - removed matplotlib import - - removed graphloss() - 2.0.1.000: - - added net, dataset, dataloader, and stdtrain template definitions - - added graphloss function - 2.0.0.001: - - added clear functions - 2.0.0.000: - - complete rewrite planned - - depreciated 1.0.0.xxx versions - - added simple training loop - 1.0.0.xxx: - -added generation of ANNS, basic SGD training -""" - -__author__ = ( - "Arthur Lu ," - "Jacob Levine ," - ) - -__all__ = [ - 'clear', - 'net', - 'dataset', - 'dataloader', - 'train', - 'stdtrainer', - ] - -import torch -from os import system, name -import numpy as np - -def clear(): - if name == 'nt': - _ = system('cls') - else: - _ = system('clear') - -class net(torch.nn.Module): #template for standard neural net - def __init__(self): - super(Net, self).__init__() - - def forward(self, input): - pass - -class dataset(torch.utils.data.Dataset): #template for standard dataset - - def __init__(self): - super(torch.utils.data.Dataset).__init__() - - def __getitem__(self, index): - pass - - def __len__(self): - pass - -def dataloader(dataset, batch_size, num_workers, shuffle = True): - - return torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=shuffle, num_workers=num_workers) - -def train(device, net, epochs, trainloader, optimizer, criterion): #expects standard dataloader, whch returns (inputs, labels) - - dataset_len = trainloader.dataset.__len__() - iter_count = 0 - running_loss = 0 - running_loss_list = [] - - for epoch in range(epochs): # loop over the dataset multiple times - - for i, data in enumerate(trainloader, 0): - - inputs = data[0].to(device) - labels = data[1].to(device) - - optimizer.zero_grad() - - outputs = net(inputs) - loss = criterion(outputs, labels.to(torch.float)) - - loss.backward() - optimizer.step() - - # monitoring steps below - - iter_count += 1 - running_loss += loss.item() - running_loss_list.append(running_loss) - clear() - - print("training on: " + device) - print("iteration: " + str(i) + "/" + str(int(dataset_len / trainloader.batch_size)) + " | " + "epoch: " + str(epoch) + "/" + str(epochs)) - print("current batch loss: " + str(loss.item)) - print("running loss: " + str(running_loss / iter_count)) - - return net, running_loss_list - print("finished training") - -def stdtrainer(net, criterion, optimizer, dataloader, epochs, batch_size): - - device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") - - net = net.to(device) - criterion = criterion.to(device) - optimizer = optimizer.to(device) - trainloader = dataloader - - return train(device, net, epochs, trainloader, optimizer, criterion) \ No newline at end of file diff --git a/analysis-master/build/lib/analysis/trueskill.py b/analysis-master/build/lib/analysis/trueskill.py deleted file mode 100644 index 116357df..00000000 --- a/analysis-master/build/lib/analysis/trueskill.py +++ /dev/null @@ -1,907 +0,0 @@ -from __future__ import absolute_import - -from itertools import chain -import math - -from six import iteritems -from six.moves import map, range, zip -from six import iterkeys - -import copy -try: - from numbers import Number -except ImportError: - Number = (int, long, float, complex) - -inf = float('inf') - -class Gaussian(object): - #: Precision, the inverse of the variance. - pi = 0 - #: Precision adjusted mean, the precision multiplied by the mean. - tau = 0 - - def __init__(self, mu=None, sigma=None, pi=0, tau=0): - if mu is not None: - if sigma is None: - raise TypeError('sigma argument is needed') - elif sigma == 0: - raise ValueError('sigma**2 should be greater than 0') - pi = sigma ** -2 - tau = pi * mu - self.pi = pi - self.tau = tau - - @property - def mu(self): - return self.pi and self.tau / self.pi - - @property - def sigma(self): - return math.sqrt(1 / self.pi) if self.pi else inf - - def __mul__(self, other): - pi, tau = self.pi + other.pi, self.tau + other.tau - return Gaussian(pi=pi, tau=tau) - - def __truediv__(self, other): - pi, tau = self.pi - other.pi, self.tau - other.tau - return Gaussian(pi=pi, tau=tau) - - __div__ = __truediv__ # for Python 2 - - def __eq__(self, other): - return self.pi == other.pi and self.tau == other.tau - - def __lt__(self, other): - return self.mu < other.mu - - def __le__(self, other): - return self.mu <= other.mu - - def __gt__(self, other): - return self.mu > other.mu - - def __ge__(self, other): - return self.mu >= other.mu - - def __repr__(self): - return 'N(mu={:.3f}, sigma={:.3f})'.format(self.mu, self.sigma) - - def _repr_latex_(self): - latex = r'\mathcal{{ N }}( {:.3f}, {:.3f}^2 )'.format(self.mu, self.sigma) - return '$%s$' % latex - -class Matrix(list): - def __init__(self, src, height=None, width=None): - if callable(src): - f, src = src, {} - size = [height, width] - if not height: - def set_height(height): - size[0] = height - size[0] = set_height - if not width: - def set_width(width): - size[1] = width - size[1] = set_width - try: - for (r, c), val in f(*size): - src[r, c] = val - except TypeError: - raise TypeError('A callable src must return an interable ' - 'which generates a tuple containing ' - 'coordinate and value') - height, width = tuple(size) - if height is None or width is None: - raise TypeError('A callable src must call set_height and ' - 'set_width if the size is non-deterministic') - if isinstance(src, list): - is_number = lambda x: isinstance(x, Number) - unique_col_sizes = set(map(len, src)) - everything_are_number = filter(is_number, sum(src, [])) - if len(unique_col_sizes) != 1 or not everything_are_number: - raise ValueError('src must be a rectangular array of numbers') - two_dimensional_array = src - elif isinstance(src, dict): - if not height or not width: - w = h = 0 - for r, c in iterkeys(src): - if not height: - h = max(h, r + 1) - if not width: - w = max(w, c + 1) - if not height: - height = h - if not width: - width = w - two_dimensional_array = [] - for r in range(height): - row = [] - two_dimensional_array.append(row) - for c in range(width): - row.append(src.get((r, c), 0)) - else: - raise TypeError('src must be a list or dict or callable') - super(Matrix, self).__init__(two_dimensional_array) - - @property - def height(self): - return len(self) - - @property - def width(self): - return len(self[0]) - - def transpose(self): - height, width = self.height, self.width - src = {} - for c in range(width): - for r in range(height): - src[c, r] = self[r][c] - return type(self)(src, height=width, width=height) - - def minor(self, row_n, col_n): - height, width = self.height, self.width - if not (0 <= row_n < height): - raise ValueError('row_n should be between 0 and %d' % height) - elif not (0 <= col_n < width): - raise ValueError('col_n should be between 0 and %d' % width) - two_dimensional_array = [] - for r in range(height): - if r == row_n: - continue - row = [] - two_dimensional_array.append(row) - for c in range(width): - if c == col_n: - continue - row.append(self[r][c]) - return type(self)(two_dimensional_array) - - def determinant(self): - height, width = self.height, self.width - if height != width: - raise ValueError('Only square matrix can calculate a determinant') - tmp, rv = copy.deepcopy(self), 1. - for c in range(width - 1, 0, -1): - pivot, r = max((abs(tmp[r][c]), r) for r in range(c + 1)) - pivot = tmp[r][c] - if not pivot: - return 0. - tmp[r], tmp[c] = tmp[c], tmp[r] - if r != c: - rv = -rv - rv *= pivot - fact = -1. / pivot - for r in range(c): - f = fact * tmp[r][c] - for x in range(c): - tmp[r][x] += f * tmp[c][x] - return rv * tmp[0][0] - - def adjugate(self): - height, width = self.height, self.width - if height != width: - raise ValueError('Only square matrix can be adjugated') - if height == 2: - a, b = self[0][0], self[0][1] - c, d = self[1][0], self[1][1] - return type(self)([[d, -b], [-c, a]]) - src = {} - for r in range(height): - for c in range(width): - sign = -1 if (r + c) % 2 else 1 - src[r, c] = self.minor(r, c).determinant() * sign - return type(self)(src, height, width) - - def inverse(self): - if self.height == self.width == 1: - return type(self)([[1. / self[0][0]]]) - return (1. / self.determinant()) * self.adjugate() - - def __add__(self, other): - height, width = self.height, self.width - if (height, width) != (other.height, other.width): - raise ValueError('Must be same size') - src = {} - for r in range(height): - for c in range(width): - src[r, c] = self[r][c] + other[r][c] - return type(self)(src, height, width) - - def __mul__(self, other): - if self.width != other.height: - raise ValueError('Bad size') - height, width = self.height, other.width - src = {} - for r in range(height): - for c in range(width): - src[r, c] = sum(self[r][x] * other[x][c] - for x in range(self.width)) - return type(self)(src, height, width) - - def __rmul__(self, other): - if not isinstance(other, Number): - raise TypeError('The operand should be a number') - height, width = self.height, self.width - src = {} - for r in range(height): - for c in range(width): - src[r, c] = other * self[r][c] - return type(self)(src, height, width) - - def __repr__(self): - return '{}({})'.format(type(self).__name__, super(Matrix, self).__repr__()) - - def _repr_latex_(self): - rows = [' && '.join(['%.3f' % cell for cell in row]) for row in self] - latex = r'\begin{matrix} %s \end{matrix}' % r'\\'.join(rows) - return '$%s$' % latex - -def _gen_erfcinv(erfc, math=math): - def erfcinv(y): - """The inverse function of erfc.""" - if y >= 2: - return -100. - elif y <= 0: - return 100. - zero_point = y < 1 - if not zero_point: - y = 2 - y - t = math.sqrt(-2 * math.log(y / 2.)) - x = -0.70711 * \ - ((2.30753 + t * 0.27061) / (1. + t * (0.99229 + t * 0.04481)) - t) - for i in range(2): - err = erfc(x) - y - x += err / (1.12837916709551257 * math.exp(-(x ** 2)) - x * err) - return x if zero_point else -x - return erfcinv - -def _gen_ppf(erfc, math=math): - erfcinv = _gen_erfcinv(erfc, math) - def ppf(x, mu=0, sigma=1): - return mu - sigma * math.sqrt(2) * erfcinv(2 * x) - return ppf - -def erfc(x): - z = abs(x) - t = 1. / (1. + z / 2.) - r = t * math.exp(-z * z - 1.26551223 + t * (1.00002368 + t * ( - 0.37409196 + t * (0.09678418 + t * (-0.18628806 + t * ( - 0.27886807 + t * (-1.13520398 + t * (1.48851587 + t * ( - -0.82215223 + t * 0.17087277 - ))) - ))) - ))) - return 2. - r if x < 0 else r - -def cdf(x, mu=0, sigma=1): - return 0.5 * erfc(-(x - mu) / (sigma * math.sqrt(2))) - - -def pdf(x, mu=0, sigma=1): - return (1 / math.sqrt(2 * math.pi) * abs(sigma) * - math.exp(-(((x - mu) / abs(sigma)) ** 2 / 2))) - -ppf = _gen_ppf(erfc) - -def choose_backend(backend): - if backend is None: # fallback - return cdf, pdf, ppf - elif backend == 'mpmath': - try: - import mpmath - except ImportError: - raise ImportError('Install "mpmath" to use this backend') - return mpmath.ncdf, mpmath.npdf, _gen_ppf(mpmath.erfc, math=mpmath) - elif backend == 'scipy': - try: - from scipy.stats import norm - except ImportError: - raise ImportError('Install "scipy" to use this backend') - return norm.cdf, norm.pdf, norm.ppf - raise ValueError('%r backend is not defined' % backend) - -def available_backends(): - backends = [None] - for backend in ['mpmath', 'scipy']: - try: - __import__(backend) - except ImportError: - continue - backends.append(backend) - return backends - -class Node(object): - - pass - -class Variable(Node, Gaussian): - - def __init__(self): - self.messages = {} - super(Variable, self).__init__() - - def set(self, val): - delta = self.delta(val) - self.pi, self.tau = val.pi, val.tau - return delta - - def delta(self, other): - pi_delta = abs(self.pi - other.pi) - if pi_delta == inf: - return 0. - return max(abs(self.tau - other.tau), math.sqrt(pi_delta)) - - def update_message(self, factor, pi=0, tau=0, message=None): - message = message or Gaussian(pi=pi, tau=tau) - old_message, self[factor] = self[factor], message - return self.set(self / old_message * message) - - def update_value(self, factor, pi=0, tau=0, value=None): - value = value or Gaussian(pi=pi, tau=tau) - old_message = self[factor] - self[factor] = value * old_message / self - return self.set(value) - - def __getitem__(self, factor): - return self.messages[factor] - - def __setitem__(self, factor, message): - self.messages[factor] = message - - def __repr__(self): - args = (type(self).__name__, super(Variable, self).__repr__(), - len(self.messages), '' if len(self.messages) == 1 else 's') - return '<%s %s with %d connection%s>' % args - - -class Factor(Node): - - def __init__(self, variables): - self.vars = variables - for var in variables: - var[self] = Gaussian() - - def down(self): - return 0 - - def up(self): - return 0 - - @property - def var(self): - assert len(self.vars) == 1 - return self.vars[0] - - def __repr__(self): - args = (type(self).__name__, len(self.vars), - '' if len(self.vars) == 1 else 's') - return '<%s with %d connection%s>' % args - - -class PriorFactor(Factor): - - def __init__(self, var, val, dynamic=0): - super(PriorFactor, self).__init__([var]) - self.val = val - self.dynamic = dynamic - - def down(self): - sigma = math.sqrt(self.val.sigma ** 2 + self.dynamic ** 2) - value = Gaussian(self.val.mu, sigma) - return self.var.update_value(self, value=value) - - -class LikelihoodFactor(Factor): - - def __init__(self, mean_var, value_var, variance): - super(LikelihoodFactor, self).__init__([mean_var, value_var]) - self.mean = mean_var - self.value = value_var - self.variance = variance - - def calc_a(self, var): - return 1. / (1. + self.variance * var.pi) - - def down(self): - # update value. - msg = self.mean / self.mean[self] - a = self.calc_a(msg) - return self.value.update_message(self, a * msg.pi, a * msg.tau) - - def up(self): - # update mean. - msg = self.value / self.value[self] - a = self.calc_a(msg) - return self.mean.update_message(self, a * msg.pi, a * msg.tau) - - -class SumFactor(Factor): - - def __init__(self, sum_var, term_vars, coeffs): - super(SumFactor, self).__init__([sum_var] + term_vars) - self.sum = sum_var - self.terms = term_vars - self.coeffs = coeffs - - def down(self): - vals = self.terms - msgs = [var[self] for var in vals] - return self.update(self.sum, vals, msgs, self.coeffs) - - def up(self, index=0): - coeff = self.coeffs[index] - coeffs = [] - for x, c in enumerate(self.coeffs): - try: - if x == index: - coeffs.append(1. / coeff) - else: - coeffs.append(-c / coeff) - except ZeroDivisionError: - coeffs.append(0.) - vals = self.terms[:] - vals[index] = self.sum - msgs = [var[self] for var in vals] - return self.update(self.terms[index], vals, msgs, coeffs) - - def update(self, var, vals, msgs, coeffs): - pi_inv = 0 - mu = 0 - for val, msg, coeff in zip(vals, msgs, coeffs): - div = val / msg - mu += coeff * div.mu - if pi_inv == inf: - continue - try: - # numpy.float64 handles floating-point error by different way. - # For example, it can just warn RuntimeWarning on n/0 problem - # instead of throwing ZeroDivisionError. So div.pi, the - # denominator has to be a built-in float. - pi_inv += coeff ** 2 / float(div.pi) - except ZeroDivisionError: - pi_inv = inf - pi = 1. / pi_inv - tau = pi * mu - return var.update_message(self, pi, tau) - - -class TruncateFactor(Factor): - - def __init__(self, var, v_func, w_func, draw_margin): - super(TruncateFactor, self).__init__([var]) - self.v_func = v_func - self.w_func = w_func - self.draw_margin = draw_margin - - def up(self): - val = self.var - msg = self.var[self] - div = val / msg - sqrt_pi = math.sqrt(div.pi) - args = (div.tau / sqrt_pi, self.draw_margin * sqrt_pi) - v = self.v_func(*args) - w = self.w_func(*args) - denom = (1. - w) - pi, tau = div.pi / denom, (div.tau + sqrt_pi * v) / denom - return val.update_value(self, pi, tau) - -#: Default initial mean of ratings. -MU = 25. -#: Default initial standard deviation of ratings. -SIGMA = MU / 3 -#: Default distance that guarantees about 76% chance of winning. -BETA = SIGMA / 2 -#: Default dynamic factor. -TAU = SIGMA / 100 -#: Default draw probability of the game. -DRAW_PROBABILITY = .10 -#: A basis to check reliability of the result. -DELTA = 0.0001 - - -def calc_draw_probability(draw_margin, size, env=None): - if env is None: - env = global_env() - return 2 * env.cdf(draw_margin / (math.sqrt(size) * env.beta)) - 1 - - -def calc_draw_margin(draw_probability, size, env=None): - if env is None: - env = global_env() - return env.ppf((draw_probability + 1) / 2.) * math.sqrt(size) * env.beta - - -def _team_sizes(rating_groups): - team_sizes = [0] - for group in rating_groups: - team_sizes.append(len(group) + team_sizes[-1]) - del team_sizes[0] - return team_sizes - - -def _floating_point_error(env): - if env.backend == 'mpmath': - msg = 'Set "mpmath.mp.dps" to higher' - else: - msg = 'Cannot calculate correctly, set backend to "mpmath"' - return FloatingPointError(msg) - - -class Rating(Gaussian): - def __init__(self, mu=None, sigma=None): - if isinstance(mu, tuple): - mu, sigma = mu - elif isinstance(mu, Gaussian): - mu, sigma = mu.mu, mu.sigma - if mu is None: - mu = global_env().mu - if sigma is None: - sigma = global_env().sigma - super(Rating, self).__init__(mu, sigma) - - def __int__(self): - return int(self.mu) - - def __long__(self): - return long(self.mu) - - def __float__(self): - return float(self.mu) - - def __iter__(self): - return iter((self.mu, self.sigma)) - - def __repr__(self): - c = type(self) - args = ('.'.join([c.__module__, c.__name__]), self.mu, self.sigma) - return '%s(mu=%.3f, sigma=%.3f)' % args - - -class TrueSkill(object): - def __init__(self, mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, - draw_probability=DRAW_PROBABILITY, backend=None): - self.mu = mu - self.sigma = sigma - self.beta = beta - self.tau = tau - self.draw_probability = draw_probability - self.backend = backend - if isinstance(backend, tuple): - self.cdf, self.pdf, self.ppf = backend - else: - self.cdf, self.pdf, self.ppf = choose_backend(backend) - - def create_rating(self, mu=None, sigma=None): - if mu is None: - mu = self.mu - if sigma is None: - sigma = self.sigma - return Rating(mu, sigma) - - def v_win(self, diff, draw_margin): - x = diff - draw_margin - denom = self.cdf(x) - return (self.pdf(x) / denom) if denom else -x - - def v_draw(self, diff, draw_margin): - abs_diff = abs(diff) - a, b = draw_margin - abs_diff, -draw_margin - abs_diff - denom = self.cdf(a) - self.cdf(b) - numer = self.pdf(b) - self.pdf(a) - return ((numer / denom) if denom else a) * (-1 if diff < 0 else +1) - - def w_win(self, diff, draw_margin): - x = diff - draw_margin - v = self.v_win(diff, draw_margin) - w = v * (v + x) - if 0 < w < 1: - return w - raise _floating_point_error(self) - - def w_draw(self, diff, draw_margin): - abs_diff = abs(diff) - a, b = draw_margin - abs_diff, -draw_margin - abs_diff - denom = self.cdf(a) - self.cdf(b) - if not denom: - raise _floating_point_error(self) - v = self.v_draw(abs_diff, draw_margin) - return (v ** 2) + (a * self.pdf(a) - b * self.pdf(b)) / denom - - def validate_rating_groups(self, rating_groups): - # check group sizes - if len(rating_groups) < 2: - raise ValueError('Need multiple rating groups') - elif not all(rating_groups): - raise ValueError('Each group must contain multiple ratings') - # check group types - group_types = set(map(type, rating_groups)) - if len(group_types) != 1: - raise TypeError('All groups should be same type') - elif group_types.pop() is Rating: - raise TypeError('Rating cannot be a rating group') - # normalize rating_groups - if isinstance(rating_groups[0], dict): - dict_rating_groups = rating_groups - rating_groups = [] - keys = [] - for dict_rating_group in dict_rating_groups: - rating_group, key_group = [], [] - for key, rating in iteritems(dict_rating_group): - rating_group.append(rating) - key_group.append(key) - rating_groups.append(tuple(rating_group)) - keys.append(tuple(key_group)) - else: - rating_groups = list(rating_groups) - keys = None - return rating_groups, keys - - def validate_weights(self, weights, rating_groups, keys=None): - if weights is None: - weights = [(1,) * len(g) for g in rating_groups] - elif isinstance(weights, dict): - weights_dict, weights = weights, [] - for x, group in enumerate(rating_groups): - w = [] - weights.append(w) - for y, rating in enumerate(group): - if keys is not None: - y = keys[x][y] - w.append(weights_dict.get((x, y), 1)) - return weights - - def factor_graph_builders(self, rating_groups, ranks, weights): - flatten_ratings = sum(map(tuple, rating_groups), ()) - flatten_weights = sum(map(tuple, weights), ()) - size = len(flatten_ratings) - group_size = len(rating_groups) - # create variables - rating_vars = [Variable() for x in range(size)] - perf_vars = [Variable() for x in range(size)] - team_perf_vars = [Variable() for x in range(group_size)] - team_diff_vars = [Variable() for x in range(group_size - 1)] - team_sizes = _team_sizes(rating_groups) - # layer builders - def build_rating_layer(): - for rating_var, rating in zip(rating_vars, flatten_ratings): - yield PriorFactor(rating_var, rating, self.tau) - def build_perf_layer(): - for rating_var, perf_var in zip(rating_vars, perf_vars): - yield LikelihoodFactor(rating_var, perf_var, self.beta ** 2) - def build_team_perf_layer(): - for team, team_perf_var in enumerate(team_perf_vars): - if team > 0: - start = team_sizes[team - 1] - else: - start = 0 - end = team_sizes[team] - child_perf_vars = perf_vars[start:end] - coeffs = flatten_weights[start:end] - yield SumFactor(team_perf_var, child_perf_vars, coeffs) - def build_team_diff_layer(): - for team, team_diff_var in enumerate(team_diff_vars): - yield SumFactor(team_diff_var, - team_perf_vars[team:team + 2], [+1, -1]) - def build_trunc_layer(): - for x, team_diff_var in enumerate(team_diff_vars): - if callable(self.draw_probability): - # dynamic draw probability - team_perf1, team_perf2 = team_perf_vars[x:x + 2] - args = (Rating(team_perf1), Rating(team_perf2), self) - draw_probability = self.draw_probability(*args) - else: - # static draw probability - draw_probability = self.draw_probability - size = sum(map(len, rating_groups[x:x + 2])) - draw_margin = calc_draw_margin(draw_probability, size, self) - if ranks[x] == ranks[x + 1]: # is a tie? - v_func, w_func = self.v_draw, self.w_draw - else: - v_func, w_func = self.v_win, self.w_win - yield TruncateFactor(team_diff_var, - v_func, w_func, draw_margin) - # build layers - return (build_rating_layer, build_perf_layer, build_team_perf_layer, - build_team_diff_layer, build_trunc_layer) - - def run_schedule(self, build_rating_layer, build_perf_layer, - build_team_perf_layer, build_team_diff_layer, - build_trunc_layer, min_delta=DELTA): - if min_delta <= 0: - raise ValueError('min_delta must be greater than 0') - layers = [] - def build(builders): - layers_built = [list(build()) for build in builders] - layers.extend(layers_built) - return layers_built - # gray arrows - layers_built = build([build_rating_layer, - build_perf_layer, - build_team_perf_layer]) - rating_layer, perf_layer, team_perf_layer = layers_built - for f in chain(*layers_built): - f.down() - # arrow #1, #2, #3 - team_diff_layer, trunc_layer = build([build_team_diff_layer, - build_trunc_layer]) - team_diff_len = len(team_diff_layer) - for x in range(10): - if team_diff_len == 1: - # only two teams - team_diff_layer[0].down() - delta = trunc_layer[0].up() - else: - # multiple teams - delta = 0 - for x in range(team_diff_len - 1): - team_diff_layer[x].down() - delta = max(delta, trunc_layer[x].up()) - team_diff_layer[x].up(1) # up to right variable - for x in range(team_diff_len - 1, 0, -1): - team_diff_layer[x].down() - delta = max(delta, trunc_layer[x].up()) - team_diff_layer[x].up(0) # up to left variable - # repeat until to small update - if delta <= min_delta: - break - # up both ends - team_diff_layer[0].up(0) - team_diff_layer[team_diff_len - 1].up(1) - # up the remainder of the black arrows - for f in team_perf_layer: - for x in range(len(f.vars) - 1): - f.up(x) - for f in perf_layer: - f.up() - return layers - - def rate(self, rating_groups, ranks=None, weights=None, min_delta=DELTA): - rating_groups, keys = self.validate_rating_groups(rating_groups) - weights = self.validate_weights(weights, rating_groups, keys) - group_size = len(rating_groups) - if ranks is None: - ranks = range(group_size) - elif len(ranks) != group_size: - raise ValueError('Wrong ranks') - # sort rating groups by rank - by_rank = lambda x: x[1][1] - sorting = sorted(enumerate(zip(rating_groups, ranks, weights)), - key=by_rank) - sorted_rating_groups, sorted_ranks, sorted_weights = [], [], [] - for x, (g, r, w) in sorting: - sorted_rating_groups.append(g) - sorted_ranks.append(r) - # make weights to be greater than 0 - sorted_weights.append(max(min_delta, w_) for w_ in w) - # build factor graph - args = (sorted_rating_groups, sorted_ranks, sorted_weights) - builders = self.factor_graph_builders(*args) - args = builders + (min_delta,) - layers = self.run_schedule(*args) - # make result - rating_layer, team_sizes = layers[0], _team_sizes(sorted_rating_groups) - transformed_groups = [] - for start, end in zip([0] + team_sizes[:-1], team_sizes): - group = [] - for f in rating_layer[start:end]: - group.append(Rating(float(f.var.mu), float(f.var.sigma))) - transformed_groups.append(tuple(group)) - by_hint = lambda x: x[0] - unsorting = sorted(zip((x for x, __ in sorting), transformed_groups), - key=by_hint) - if keys is None: - return [g for x, g in unsorting] - # restore the structure with input dictionary keys - return [dict(zip(keys[x], g)) for x, g in unsorting] - - def quality(self, rating_groups, weights=None): - rating_groups, keys = self.validate_rating_groups(rating_groups) - weights = self.validate_weights(weights, rating_groups, keys) - flatten_ratings = sum(map(tuple, rating_groups), ()) - flatten_weights = sum(map(tuple, weights), ()) - length = len(flatten_ratings) - # a vector of all of the skill means - mean_matrix = Matrix([[r.mu] for r in flatten_ratings]) - # a matrix whose diagonal values are the variances (sigma ** 2) of each - # of the players. - def variance_matrix(height, width): - variances = (r.sigma ** 2 for r in flatten_ratings) - for x, variance in enumerate(variances): - yield (x, x), variance - variance_matrix = Matrix(variance_matrix, length, length) - # the player-team assignment and comparison matrix - def rotated_a_matrix(set_height, set_width): - t = 0 - for r, (cur, _next) in enumerate(zip(rating_groups[:-1], - rating_groups[1:])): - for x in range(t, t + len(cur)): - yield (r, x), flatten_weights[x] - t += 1 - x += 1 - for x in range(x, x + len(_next)): - yield (r, x), -flatten_weights[x] - set_height(r + 1) - set_width(x + 1) - rotated_a_matrix = Matrix(rotated_a_matrix) - a_matrix = rotated_a_matrix.transpose() - # match quality further derivation - _ata = (self.beta ** 2) * rotated_a_matrix * a_matrix - _atsa = rotated_a_matrix * variance_matrix * a_matrix - start = mean_matrix.transpose() * a_matrix - middle = _ata + _atsa - end = rotated_a_matrix * mean_matrix - # make result - e_arg = (-0.5 * start * middle.inverse() * end).determinant() - s_arg = _ata.determinant() / middle.determinant() - return math.exp(e_arg) * math.sqrt(s_arg) - - def expose(self, rating): - k = self.mu / self.sigma - return rating.mu - k * rating.sigma - - def make_as_global(self): - return setup(env=self) - - def __repr__(self): - c = type(self) - if callable(self.draw_probability): - f = self.draw_probability - draw_probability = '.'.join([f.__module__, f.__name__]) - else: - draw_probability = '%.1f%%' % (self.draw_probability * 100) - if self.backend is None: - backend = '' - elif isinstance(self.backend, tuple): - backend = ', backend=...' - else: - backend = ', backend=%r' % self.backend - args = ('.'.join([c.__module__, c.__name__]), self.mu, self.sigma, - self.beta, self.tau, draw_probability, backend) - return ('%s(mu=%.3f, sigma=%.3f, beta=%.3f, tau=%.3f, ' - 'draw_probability=%s%s)' % args) - - -def rate_1vs1(rating1, rating2, drawn=False, min_delta=DELTA, env=None): - if env is None: - env = global_env() - ranks = [0, 0 if drawn else 1] - teams = env.rate([(rating1,), (rating2,)], ranks, min_delta=min_delta) - return teams[0][0], teams[1][0] - - -def quality_1vs1(rating1, rating2, env=None): - if env is None: - env = global_env() - return env.quality([(rating1,), (rating2,)]) - - -def global_env(): - try: - global_env.__trueskill__ - except AttributeError: - # setup the default environment - setup() - return global_env.__trueskill__ - - -def setup(mu=MU, sigma=SIGMA, beta=BETA, tau=TAU, - draw_probability=DRAW_PROBABILITY, backend=None, env=None): - if env is None: - env = TrueSkill(mu, sigma, beta, tau, draw_probability, backend) - global_env.__trueskill__ = env - return env - - -def rate(rating_groups, ranks=None, weights=None, min_delta=DELTA): - return global_env().rate(rating_groups, ranks, weights, min_delta) - - -def quality(rating_groups, weights=None): - return global_env().quality(rating_groups, weights) - - -def expose(rating): - return global_env().expose(rating) \ No newline at end of file diff --git a/analysis-master/build/lib/analysis/visualization.py b/analysis-master/build/lib/analysis/visualization.py deleted file mode 100644 index 72358662..00000000 --- a/analysis-master/build/lib/analysis/visualization.py +++ /dev/null @@ -1,34 +0,0 @@ -# Titan Robotics Team 2022: Visualization Module -# Written by Arthur Lu & Jacob Levine -# Notes: -# this should be imported as a python module using 'import visualization' -# this should be included in the local directory or environment variable -# fancy -# setup: - -__version__ = "1.0.0.000" - -#changelog should be viewed using print(analysis.__changelog__) -__changelog__ = """changelog: - 1.0.0.000: - - created visualization.py - - added graphloss() - - added imports -""" - -__author__ = ( - "Arthur Lu ," - "Jacob Levine ," - ) - -__all__ = [ - 'graphloss', - ] - -import matplotlib.pyplot as plt - -def graphloss(losses): - - x = range(0, len(losses)) - plt.plot(x, losses) - plt.show() \ No newline at end of file diff --git a/analysis-master/dist/analysis-1.0.0.8-py3-none-any.whl b/analysis-master/dist/analysis-1.0.0.8-py3-none-any.whl deleted file mode 100644 index 83d5cb968fdb50f333025e6373172553d91df843..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20459 zcmZ6xQ;;ys5-d2jZQHhO+qP}nwr$(CZQJ(DxBq$A-5a+aIyx#M-#V(YGgCnt7z70X z0007D3fWxYzY74+|BnCae_ca6LmN+LOJ{n0eM>t_7kzy?2Tx!Ch5rx3Qf#51l@_Y| zfdv5I=lFkN{$KEam|I@!`0cUQec%&*8TJ9)sWenc?DIoyR7soi0ohwHxdiQjbwaTd z38OS3RX(@Y&)4@H=KDj1Ee<>EVpr(wobk-(tmmx4C&cf<3y)E~)$1C2`1vc=@8}Ln zDy+ArJCSdY`~&%RV1(PcyXl--Q0lkvu1k?Qk7*9>9oOzIBX6z1S6fBf%~G794y}Jq zAPT9ezHk9iP+cI@*D>id^jL|muFm6EVUbs57FSmERdKryWe3)hVPIuxyMR>IfF`Tq zbsOmY05h;Nd+(<0)*$1oO5;tgo>K;EMquZ#`2&2I_bXvWh^)hL2I| z17~G>+yKFu()MP=Zw(4?(QKP=dw+YuM+HSPg7;2j-1?d)YXVvE_cspcH4P7H8t>X9 z$-8K~aj6Z=9>+@BtGGRGzRHCRhU6@)3b0mMZ$=@#gqB(0;umA97Nx?vd#bN}0lfy} z;Yd0Bm_h=L9LxzDReh=+sqQyzc6GQ6(}8;wA}Dw`ZAY?%p6=s*h_bQRsBXIIk{=Kh%=p0SF&_)1{P3ziH;MZ43;9B3qy8MWw#Ry8Z3dVv>Qit1lxfzTMyU_f(9xE z$-xRT-rK<}TQ!t$x%C0lfk0KWDazv|ck2d5`8_gEM#?oC3Q&q28^HWOubcY$03aIuCAu(7mJ5g$zmJ(1t%%cueHS2E{14a#ze?E0 z+0dKhwocx#-K~{$+Hbj-pRorG)1lQj(VISp^rO?marK}dh8vg%T~19_Q;2B|mO+=# z^hRxDIwpUgy<)y@^@{mmJ(h~E?*p<;=|32ugFpm-Xy=}H!vcpjp4ujqHsOG?`8&|_ z)!wmB>=s~3HPb}pw>{_$s7-Db(Mu>cCRtz!XoyJ(-1zRWk0GzAC?(AsJ|>J?lFj*~j0TGV94eo( z`lh+}Mtv9$?^-OW6qHS0cbDoR2krFwU(9HX=1aq6()u9znPjlet%dQ{Y z0!4#lcXtDvxE{cIs6$O}I8KceBaqSJ>;*0H!ZKrPUOWv@PCtale+3KH=e}p!hY>k% zK*I78q6{PBu!mE6z;B;xMcG5OS>1gb9EqChQpMh3PpZ5_*zDz(1 zBP4X-#*fW0xjCYi@Cl{PJe{}lq{c~-2yeV+EUHqAMgzC~zTsBW9Foxu3cZCPWk9Evrx-+K08c=bG zWTrdi24Z2?EJL_5bTmSL3ed7945d8b=D7FMPjd07fCCwAA_HPK#tG_?#$<50&5pNR z&w>nV_$D$*LCv>HmZ&NSeLq zgDvt#ZWCrVTmeRs&&y6zZ?yo-4fen_!!1xp^g2CNwvrMPOfVQ40Ypzg|7^JphE&v? zd#(n28UnYX@ZkWM6hmYO%>Q)Qsi3c@37w30eO0TmTfSev%x4H75=~|e`3gdpLNF{c zd<68o6*j@pqNPyr9$CG#WZu?=`hc|!mya@nWK!gt}0rt5k%&%fXjE5Yb_6 z+eI_YdnUtOa|{#81I-Jja5-Ey&{Lmv>IEa4lJXL05k4z|qR~;QQ#k_sp@qe$zDI`W z*i*%ns~HIu%lk`E1mjage!PHE6HGJ_*2RQXJ2g5$ySxhz?scR4(hKdqX6_qOBwI;z zMQrZ61yt27>>0k@IYOwyBNNJOt*m2}Gi%B%Ab5j~uhaWWQ18TGI)7JOz&r%v@(4v& z-OR*bQMb;Pn7saJ6o+AA-FY)Jx@k0}j)eZf_R;^e3IP*o-e2;43_{5JWOiO% z+jq9mJvSzH0&MuD3aeTB&WZ_f`_Y=jh`ExpJ8cJK@;um~6O$DE?3jO;1Bby=0XZqO zcwoatfz9-6mClh#5Tpa48Iq1~Vka{%@Z(a(bzpDW2vE&lshy}(cXV5o|5{F}*&I^Rg|Mogp8UweWZ*FU z$*ilxHW6=V`1vv=y?KFTJLfP{n~eR4(O<%?X@TH7bDtVHnm;jmZhZ z5p;%7*C_Fe!e2!?Pf78|AD|*cb1kwDoQ%=xjJeIN5)>u?tn*d?uW8LbmeOZ!Y=luo z8SGt9SQFj0P%XiSUKht(LJy;q31pLwliSHLKRUuGXLW5pMFC;EP6aH|sG6t%$*_&2 zEsRr%&>FyT%ZeFn_idu(j&n9ivR#Y0drg?=>WW^R^}Y%!Weu=*7c4OL7cICz3k$R&KjpuW!{|HB+4I_-n?JMR9D)Y zGMEr)tC*J)&edduM^=cLyg9k?;29f&Sq}!mOoyyerCSrvFQhyFgEC*k$c zywT=I_LMs!w?d7QP`_!-^@xlp4}2*FcDc-z%3za}Pq3Zuj+mCs#;40R?`o7m_T+l1 zk%~UsO*NH`s%h#^ctu}&Ug3&`^fnpU>cL~O&)Wg+#?@D`Fo#R)ITypH$yDkhN2O%(OpY&5l}G z*mLcu{;rZ7LV!Ro(6T9a;9H1uF9J}(OOmop?v&*w2{{+fRm@*O61%e;;@lK$0^7!w(Z)u-05-XVz?VIgfJ;OT;H99zuQWU6R^jF?3b^(? zs8{{*0IZ2!uEF(*xV(G*!J)`@Ab`3F&p$C5c3GQpp&|!gR8bo?v-J`%joOZ3SVKoo z-a#TW%s1Y2U`?ewit1n>77sZF1PPP37+OoF_)LaO?x$+oTvMa1EUF3)hE|0_A!2<1 ztHq%Bb#Wk#AShHS_<%xfGAfdEju93j#YCbL5^HaHUL^U;yyxZ)o@f)mxGMrLH9W)>m|vGH)b0mkh>l7K@h^<`hr;_~@y$a<+SHb9b#zlj z)qJ)|If$QBBdOlW%o+!-Qb2K$7G+`)&NWcIAp-S+;89H`4pjyQ`SdJQ@fNVuM~GyK zjvS6dm20hvTpc`KNO+-nFaz>iBQD99N6Rv(9$UH;TRA&c7%H8WH4pPJju74xNRpXW z(E;zlY#ZwrlY;Y?c2xKre>TkqcBb)LSe{Gyewq4-_~sRLXkke8C94-<)PDjbW%~N~ zVd;g^;uRpVazp)fI&|ju~8XS5SE<{#;$025)r}?0c0g#ct zjAncmJox2p8DT|iHhT8#2Wt0v6nh~JuAz!!1aO@!7!l5#!+QWs z;c>o@FTjz*gjG}CC5u_NHN(``nRp4dim!&*)IYnaTM}ZR&>_rRRyB_V!ljLpb1zwr zthtVCo+UFWfD8}973m}AuaF5)!cZXctt8C#)h)zTrC(wV5-D4j?7{ud8WZx7!zQ8m z1|)M>Yw~MwL#yBXtxGz`jMXNRQ4RWS8B=YWbPshJF!w#>w0zTt;(lxcru*$<83>Z^ zuSFy(o?my6zj<>es70PC&cSBMdh=zB@px;mvsh&tJ03$o8k=rc1&Df4wm@6adBL`` zd4UmKW^KBSNK@{#q|oQFr5(q0W%#@J0A7IY!aHgbQ({!m0ETz_**fv6JtRzamO^t} zy3+Lx-K&w(lnGN)7*3oP=#JI(eN@Lggp!$d3mIl5`Yx;$OlCV$7&;E+kAL{5MzB>f zHY|2Vv{7<3MRpmYr3NhTSZyN&~0b0Inh?UIzgH#tnTgqa(YJ zR&16#kL7nc7LM*KPb=4vkrPZp@mlrS6eX0smH^560YKsm8G!~D&h@&lUKR%MOTvhE^_s}-O3^r!rq!MFaXKyy zDJ+4pA=H*U_wmwZl4rqmG!4~gOs`+=jW?oj#+`m%+7olUhi?F_Q`JwF4ryS#woO3Wo#oK?&tj z&`ga&BN}MW949@!)&};fI#PK9(ZjV1sogTmgw%t;Vb7CLI#Rh|c~;HkmL#og@Io`W zsyPAKxtzsmKdRX{%h%f|Cc06#9re@<#@(vYRq3;L$nz4wsozyLzMnE=RU;WwH{(kC3$9E%pE*>cyGjeur{U>Ez}UcGfOlX+$q@#OJ@-7IqRI&;;tq#afC#}eDOG|x zE?ngWR;>HWN6H;m4ShY{Vq`cReM8Q1$ufnd~mW?0vYlA1LQGx?C2x=Qk|*3Q5dT z?Z*=QSWCw5-VmY(zQ4prst@hr_(4cm3J|906AHo(fqw=ma9DeTVZgAKb1-8$1Qlp zQ7l+l64Z!OY;TGObWKwK06JP^qI#2gv;+TO)K4I*gNJDzFZ-oe{MK zT}L-)xTnsZkiB!owvq;y^8N5L3F>Ibs9Sse;0!N#SButg-AH=9j_$i^0M^-4PZ1L% z;!ZL9F^O)gpI^(mBH#C0iJvR#dJgI}_7^NP+Hyv_B3F&bxnA^f6lad}#(@O3f|t!m z47(J>_wZZkmOds+5St3?>{hx~n`6#mN3_6kED;;~`ou(E+#hCICWy{D(e zb^ACkAV#dr!JOscfMWp|y`_cu6H`O)GpH2?7zPgX?*IFa`f>y!DL`96}I zzF!Vz@X1i3=^byVKVIIU%nC~chs!(E4CE7l0 zPhHl-uoHMnpd?u}&>#2d(JadEts`JC!`B#lD>hi8%v9p#L6mX`g72%KKxbU~ZI>m|(PgIURte72*NJrC2?XT!-tZ#jaBbFT?QwJQFK>qY4F;wqB#3f+0i=+dYK z>t;#|KCPH$TUU*J*0jXq7yr~Nykgxwmf*geSc)`ULV8`1I}`3Kx2Gg|N&@0+Y0sW3 zCnlYBeOj(IR!E6d$7R=neRSk+)`OBgHcCfGjjL!a!^qp}`hJdhGRi&eqV-z#v_L4a zg^#|?yK7>;_$hm1Rcf_P+*9bCBPtij(zpB=8dE{S{1+IW9zUdXpmI4}1b-1>xY$p< zGX`^CP;_R;VQFQ8lsm2w)k?3|tHr|$on9W-9V3Df;xMlesQo73)700JN=Z5nSWWwqw?ZrW;GeOhRIAK{#z0WM(2(5p5L24tlTJEk`O+YPL zlWf+3%d&$dTV=ctLC|i+bj_IpDt2Xnnqb2^7c|Yi%DRi!(*STjZPcH%bI4$2L4{VMe!3CbzsZG> zRhTN_E%kpBGn{Zyc|Rg8pxwi5g?}K$4VF5ED+e0t6-rQ?+YCi|P$vbui!?Hwug%2< z3pRPgd*Jwq$gdWY8zBSs?k|i_j`x$o3Cm#C`%(Wk zstbF+cJnuO=J`6!+5SO2zo?~&TgPar_V;-=v#iuO)DWG%8}`d(S3H+%^{?{bM`u7)hOT#QFcOQ_HP~jI#7$m#Z>Bh2a)Wn2>#dDEx)u~58ngqnxOH_8bm)Zxv)mV+1s^1l+h2#q~;FX~{t zoJ%H$-y%``+?u<=xfK{8Jyn~BB|6G&V2{3lY&I-n7WhSa9l0-nV@kg_({$JU(+ru; zLg~*6oPChtt-yCFZ9ibD#f87ZPHiaD9)t^T4Cb&hml(b*5im?79SSzl1&ok zq66gwcCyRAX6lk9Pi-LrF^;?zP+Qo8CJ>*hX`}}pBs{73KC7Vi0nRV?(Knv;wZjI6 z4|{YT3MY%~&fUp?XT@iOEc$wVGz;Q(@ekG-{!0GH2~_O3TpZ-6vzy2TLyTww@VZ7=glP=Ke3}ArPu)^6A#V?jkDSn`a33mRVqv>p4ZiN3-0#@O4qXbIFg@6O z+i~P6_E}$nlg>xxcKH_09};8ipCM>5kf#5LUX-?LxzgTH^(G@FnGNkldFJD3qBon# z8XsMd5^6Mrv?mA?2m98=LHO9XCxCPK2Y-;;&u3X=^*;eNL5~nWB#`?d2-Qbpfn!6P z9bjnrDFiX16#&Q|*&*?0?|QxsC}HPoM}nYwQE6DGG{z08RDRLVU}x*)dF##3WP{sk zg)s{FB(Wypu$#0+W)@(?ahl>gVcX>Py`X<6$#2=P=|&T?9i~KjJ2?l#{9!leV}3a# zrYZV?YMwF9GLOzCM?sVSY8IafjTQLi7|%NY=*%to;&l4F(0+-)1xcaTvj)<94M3!y z<-91r$sUCFdb($J)a6OVtb#6!a?earC*DXiw*S= zY=8Lwf2U0SKd0>386MjJ3;=+e0002(|8&=!OwFB4ot-W1?f&PLX>Qx?up#)uANXTz z2oRGtbzQ$rMg)OKY8AL95jk#y4QdA;YmCb+qR0Vgmz?pP zD=d8g$Yqk@@I{0!&?q}dNrH+u7Z)3QynbJ&heH=xz~NgERw~IjfZB`hrMTQL0{&2D zuB8kgVd+WrT9M?iJmCHaF*ODtdrfwrdXuxLj-NpZ)Mo@uX$7DvHWXCIW6@Ft-AQWd z0b8LaS-ebjpb2nL)}tyr`r;lEfwv;bW)_oScs2=0TSN$`A(a|XiiYY@tdMjQfbP0P zt7|4F3oBZyHOgO%YxI_6qAk$@d!;UF4J%0?wuPFdqh$kcuvdH<)>WTErs3m}{%i(&a*T zG+2I=l9!f{^#9?Efe9-p$RU111TJi~pxgv@y-&RD4ggX%BP|&bI^@0iZVfi~OL;#A zsYZ>k*yr7g*X{_cnrBq#q8y1TBC3sLR^%0jH@GULtqo@``}B~o`6=<)+48QP1Zg#T z(W>@OXH&6CmO?~YZHOI}aEhfa`dyExwcq>p_d`YtilSs^_ZPO$?y{@D(v|57f(7R5 z=VS4_V%KAZo+oc_d%M_K4OeMN#4aRnPSr#!(KPtdaW@~nx3{gH-r=x1Qwk#37OSw$2N$z zX_F4a7GKLvN!|v_nHn&={q5Y=Qc|mFk=}=JF3rvbA^KYmNh%8$vfKs?UJ!*HrtOny*Lck+2o%CBwUBCFSDC#y->_ObmXo@$5&oHA-A;U3tSlS7C7EHId#Rkc*(}a?5p)>vkn5E*E7D@zHyo+Cos|Mv zBPjNz+5My)87d^jWC2Eu#+!pdZf6N-RTi{v;bJsDGprF=7@E=L-ZY_zc&p6-!~tIO z^|_jxXTO#BVLsYI7mXg|rKCr+j3?~}wgx3LsDe@_`;p~8y-|P4D zhw*=bWp+~Hd2GdX{IaD^3_a79W?|f=7%8o%aGxhTXtLy(7OsOb=L=a&ns5H9WtpG{ zGGfZ*_EkakCgC zRARD`olOC^cHb7c1KoKEgAC%t0O9n5ZY0=;M6&o>LWI4PYcxK;wk@k{n~O+wW`>>x zo_>k+(To2?N*SI8J1Cc=vB+-$x$<08I|TC-?U*29^iEWd%NDV1!|g}7$kag%VaJX^ zgVekF4cn%NEw#Y05qhlw&3IZ9rhIFk>q%(U!>*If{2^zAa z@HE_Ln{NIeJL!Lk4{Rp*Xm(Hl07kg~YbUu_x)|Eom>N3$Zzq+iYg?bNq4>d{@Z<0- zG;UV9Cyh5KcQq(bZ1T7Oe>Z>>H?lTjX)-18NJ{zp%qN~~dxW8JA*eOYea`(RyMe|J zF@pME^tu(bJWOZ;w{LH6bANcj>$|Q>`uVm7-HUOIqu%TvHaohSAM^$1AGoVs@$Nhq zE9l(_a@*Zogz~FHL$|Hv))>qSvedH0sSnUFSnTW8qO$A(xu{i`Yk(s;~*4JKj^z7Zzi(O`|G+T~h|=tsB-^p+*p zvE?9oRc5z2UuXkKS1q!rM(>qcvK z^-)C;v#VLCqBI1bu9fK9jr@AJkuHNoZJyI8iFg^l2MD7h$kO z5fQ&k<~<~vd<2|PmJAy0EZ>_m^el?EEHSK0-Ii>OU*#fuvDMU;wz18gWo%=TRa2Bk zb;GL6FmBVAjOk>Gq^NWV z?#-c|;VFNTij%|mkG`D;&{2+7SIUfP+i(q5)Z6n+ZEs_b6y-T?LJfi7ld39M?SfSb zdS400BvadAd_sQ#x0!?Q`gvAP^}$0VCp}!6p$XT_!;?8cI)HD z`OH<{rOP6fmOaDd|E2gfrc4)MQufB;A6{>ryY)tL)4Vpsq2}KTABM~G#Tc%;4^cdb zuz3e9l-1PN|2$Y?dn4a)%jq@)j$-jeRMlDTM>?rU18m3_f-c>_0X1-Vz;4_wn77~Y zFUJQ#S5ShPuWRxgFFj2=rGO;$^ih!xpN)u=>>cY=@+eSY>BI8MwFI9OQkhu@a0Tmx zaRKJx&_QJd7D>>ihubXSw^?^e;#;KrDDp{|6)!{thv_Ybg>MqL6LrciYPq<*aHY|B z#j#M>%I!bVA09YCC^7^5JEx%A`b;l~I?6l_9n6l92~_>D#oVaOI=PBr_C=B~yIFE6 zsNr=s)z#>sXIs%8t!AU=xYY>g;>K}kaeuJj%JcLh7YH`7N6CS`|0DXavmzI|Kk8>5 z?w;hj#(ghNR^#X;l|*K-chxM;{;N3O`MVl6m4q=*Fsi8gsU6K;nUmh4?p-s*EpAHq zq7%}YeB(gX#CL^>Op3wG#F#yn+(RcoORYgKy?f}|8)Pf1Qx>raE&piNhmKsQV_Z5^@p33arjJ$*ADX7dymPF*BM5|dXd6+?2zqGuKxmyS zAsQBaa@;7|aBY_Q>x+w3avp-OcDg zuGJLelZ#5H#o%cwF8`0%1xc`o-V10STUX3`GA-&9% z)SQYF*ig!%c5jot=s43_|t{^{~tlQ|Bc#}Eq$3~JOBXL0002g|C9c^IJufSTU*-L z{5NS=d99PT#}fB-PyYpYG!u3V36Ypus#n$ApETz>rQUYfkXCYg!H1FxA|XO70Cb#4 zo&NLt&0q&3QD4+`qS1yn^7>zOd3$+DlEwCsw(qaI zu0l)2An1S`>ai&gwrD$rV3|85D>YNj#!kcFd->2eKr`Fpor zHhVV6Ed^Q@L#=BU&9;xiiMq$DfGGr-V%ues`{SBNV_5UdY?nl0(`<|=_?m@Rh19Ws zI^{y0&;Bt(F+9yqyn*q10u2E7L{33S1tY@|kU!@f|gx8g4wT^ss+WQ4?0ZKVz1-(;X^PAP%(KuH^%xD62t z!0zOTs3UiyWJWfi1Ay&<_{3P2^`Ch%&3d5E3GlD4vKn@#0Gt=^nl?6M@|-JpQiE#5 zl5_ncZ)zP}K?(k=;Rq!*TmPC3$J#^~i+fQ|jfJ8LTH8usE@eT5z^paWJ6Sx47ikh1 zEUHlikzOB39H(oA1n3a?!tUT-p{%I3eY2Lkqc3dym zCPjF^!Adsi==b|z{Gfe5Xy8q%f4}#V4mFbLZ~OV|{rDi~J7I17`{lN|_rpZ?DYuUn zUuAisgH+S$)oK-=x_tKPFEb73 zr@UEy>m}Mj5n~wGP?65Kga#Uyy&wb5b*~WaW8+a7i8~seycf^O-+&)`rpY_`` znW~~eFDx`ugd7isiK!(xVRiRldh{8h7;7Y@q zwtg5tcVrmH8iy!vZ7x~$6ynf1BPbx=U^*Ih(Jf&6Epqq^rs&ep>O)()pm2a5^%rsF zqC*cQ_^bq-^>-aJ&zkN!GN%;XtL2$)6>l#^gFgjh1E29#LUp-5S3BEw@GyQ2ovvN* z>J2);{&qY(5q0qn&A<`1;VEB@xk4ESXbi>ee}qycb{n~D4a(zrh}DVnEq=TP^;cHs zm3!MNId*KCobZKN;q)6%wKeP|mxH&tZU zDlfD!o(@i=zm76I-STDTFH;(MG^ZAxf}JYuL1~x2+_<`LU0Om3Py0g3%q484*A-Zd zo&aUwBmlir2-GAa4U>o{{}IMTaAL(dI0|YQ79QOjsyj3H=pF5w-qKAQxXOpU`NY9B zUqz(3RLfogwOpS1v`d2V?~oKPWymd(k;gJ3=p7p+eBEODyA(Y+Wpm~}T;+*hI1O;pz>Lm><8!)e?sEgX;Op!#$i{_~Gv7>~a=_(zP2+}rl;i`AMw}PLlOrwv zL6FTxUjDIp09k;}VfJ0jK-{3B#@ik8EB`PC^?)#bt0(!s;Dcy7n*$R6c4iwK8ORqO z$ZfG+nU>^@!zC{crWF_iyX=78TBTgNz$u@>UCGb`DtcK{5%dG{S9Qp*=E?TaNP;Dz z5_)Ym6Jn%5f}4%|Mboc;;Ybh>%i`(TSoIN3fE)2IVn(DOE+>(+F>nQu9FKclw7eX& zH&T<~r7f2%3wvKHeia5aa*Oc}w@WSM4mfhVS51pqiJVF71*YVj`PrXrsB<4&Kiy8h>=~{L9=42`;twCh zI6{yCEA=DH=y}(Md_UHJo$WXj1i!q1SRD_8ut&hQZ)x5}zXP}I`QSSLi>tlZ-X))8_MkgpqBDvDsgccEAeyHjp|)Lk(qy!ACG(wOjiDYp9y|oEI|)V+?&lr+s$n-S*WF zdM^6_-SPUwO$45yFA^;=N|}wfl546)rrDbj$P4voFM^%T{t@2^P9%sobLaQAX>K30 z`!U*<)7p`$$LIrdns&o~V3vFx3GqZXPQ2`1>Us)HN4ZOD1!y1KKYc~DDR91c8DdrD zik>VN%mLx}$N~8NH>g3KKu!Pd{$G4ZIaWW~3X?BjH)o_QYk;r|XH>no`VoV`zi~1A zki?Iz%j*y32B52{*u#m{ixCe9i)HAC31^gEUyh6(b>`xEzTPktz$CNU79~|@zAch_ zxd{uMaN^s6zomO5gs+nj!^(@N)6ebI>F3Dm<>~YJ@#Hi0#fPt{f=A&h{ve4 zPUbeT6YPGnv1^ApCjc?Y7;?v~`2v{@FfMz@ZxP3;P=v)>suyFDcA`-nw8k-8%Y+zX zm9fKe4(a!=AZi93&s2J##n*eUz0|~D*bcYge-kpX32%WBzW~?vR{@#HVQ(li? zpInKd;0=N=*_^)a6ni&S@#p61d2#x28ePjsj9yD7eEuP!pYtX^zgJ-M11+si@@ z_9GoSft)^R0e&u}Gu zgJJVH=p?{BGZ?!2d`txjHpwNFP?iA&O%Mqp2gRFHFf0nZy2`5Qgf6#t)FbYLS`u_D zlHDpk_K9MtXy82;6A&l1;T{p1bGCa(0O&eq&?}f117o?eyOb(ArtQ1d4y(HE^nwE?ui4U`wj1-1(-qVPD$i=TDiCn*6KRAi}2c1O~${&(0F`-<7>En$c966Bz@-MyY)MU)fxF|^q z6QMN`icN|j7x77$bw0*MBy3M066}+j4BsdYd`OJyQZm?GNkRsB)G(~Za*5OyFdL4; zyA;Xae0YSu_&v1?t}yQ#V9BE}Ga#Z>33O zNl=SEdHxKXmgNKXOx|<1f7x?6($ibv(EUjnEU|FtG?>k}Uw3A(aZW@dg74siv8r}t zPC%p}7bO1??^oOrn*{=Q_nb{h$nulWtSnkt;;D|5gV$Mvx3n$c8?R;n{}FQ%groz;_! zPAqylR_0!&t&2455v27-i(ZiuM~lU66?msjY?oj5PKv6_U=Ie2iByEH(4g4c2w3d+ z8O$F= zKrchkx`m}mWiiLWZ_3(wfZ$|xZ@cR4R3F38L>{mBAkX%xfPi~IHdp}5dt>;FXTOV4 zXEW)e;gIovuLo6}hQ)AP5#lZt7%~wsk>Su)rSftW$9|(EcF=K zJB91oz801AzJGc@Xdl`->hlsfV4M(%6KY$A?`bjmPN75AWqdfefte3NSSnetySN-S zu|20USqd0$7VfCdG0E!lNyfrh*WS2i0P3b22Qh}(&^A+Q*j|%ex^*#R*QYAKT#iJ+ z(;>*DVv8DLBVs+XFw~)$WL}Q1n;e*@N_NpcC){!3+|k3z=kozhfjYiSAL?y>`}GoO z*Jho@zXLE#9+vFTBwBPC)jYI=dK?JW?uA064hIgd8--yGefgeSNY7@BI#hm*Pni$wfOJAxp))56B9_IIDwN{s^gC@{`fxphhp?W8RmP`*)5w$hYoqB^> zG7c6yvg^9`H&>DlB61+`q<@-X$z3Ib0%_dH5e{rIs@=NsYs z(FZtSlYitWlaWSy8h?-}Q2`MAxr}C^ZSEgz&cmgUD){Mw5IFH&GofbwdU$%uQA*^W#2SlN9VHWI2zqQ%3eJ1K1InL5BpT`I3cO7_` zL50t7D#q_qM(J6s?DJI%^WofyIR2DxgW^2;uxK+(mNt_{=&fu&jTykVCMf5CP6y?7 zbl)TAK8dAs-*dCY(jAM8YbF$v9D3z)HWDn9}Jr1oWtfLOr_6w===Cj7%*Bk^H|ujYZ16$>*HD95N6j#_?W6ra+av z8PlQw7NB_626?6@Fm^@*-wz2;Dj$LXHGsI&GD|+2j?}Z+D?yN$n)&5x01C_ zr&QIWhuq_zsI32pX&sd%&CTYku;PPJIlcl^+2A)$^7aR=w%LoY`Hkp3mZQ{Tw|&7#y{c~pvEhC%_rukxrgn=!*#mhMhVg&51S3Fy0Y%0p(N&3UeM%WKD6$~w&$^X zK;S=5mlzyBFQD6k>eGLJ+UlXqol^aEKK<&G2r+l zdhP+P%BQ?94&Fvm;s`D#lNg2aT<)cX(5_5&JvPuG3BOz5mVmbtZwfVjr_TBU_2m`d zruXz#=kR_`G*I-kpkqtvgvQfuRxx$KNC{0^&Qu`BvFtt#rK*l)OX9J)LHS zcYpqqBNN%8S$#O8#l7h=*N6blQv0_d@z`}<%ZO3HkSeakmbAzHJiEJ~ae&p#K>O6G z<~oF#4sPcg$LlvYq7|#_d8mPnBj~?d2h+4PX>oU=^_y^5YV0$pL8f)iR%5H;n!yIj zyenPYA6c*50~s=LHqG<_h0v~A&D-66(Sp%inhx0u^RkNav3t7TFiccTw@>YOV=#PO zgz&>K$GU)sC$e;y7|6e;lI;+Y7G^uvn1%rboIhdFS2BbFpUs;g4DR$o*X+Gx{*RV` zi{iXH6KH=^DU$Nf?`H=Tdxh6KhF!@+kAB1!NKLte>$dHl`_3{&d?TbH{#InK8ohxx zAbE)Je!5^AYFIA2mz@TK;p&H__qvlnIFYRIFcf^{svhK&+lHozM>VnE4R-ZxksJRe zaabBJ$MY6f>LrtlYOzXOc*)~+H!f~ULYBNwwC{>fUVh-+!>$RXgaAj};@Gn788gk4 z{KfHFM%%cGqRWR_1cgik4Kub%icfQ2oC~0s9Sq(|(Q2C66$a~M;C&azXtLYxtsn-N z+@j!{K7fV4LqO1hXY*~rxk)znnVoZdJsCh7@4=({TqhfVtu&&zXrRueS<2zwG;dX} zSTa4P&vxY^!&eu@a@(vl<35(7T71n|8e9IYKt#|2iQM|y(A{bpE!XR4j0Hs^8~`{1 zHXLw^9eaL6YBJr1>?*>hrBVt2&rJ^1C}k0&Nmh-XuK)}5Q?90(dt)%bZoQbnK%qTY zRE+QYxP%Nj`UMaTQYk@(-(}FgRFPm{A(zpGMb2?M$EqXOI!5>eD%bvw_XLvz&1_2( zyR##rsU!iVSEflM6z^Q6r2iDkpJ8`8ah^pI?I}T z5F}TE{Fq1bRBeBB2d!^`_qTt8{n}_wUxGpYA)j)0_+488z8<5YjZAGMiu`QuZ#D1Z zXuK#`oT!WpF(~v!Uv|WTV|?>Xq^}p|!e(q*KCtr>jJc^dlf4{iskXL;{Uz#xY2@3K z^waYB>^3I^`Mt^;1FHRuAU}jX!9aD%2c|o&{UwN=oV(N6X8&iVfw}cYA(E#|ejkq& z$ydXjMR!Kx-ONVj&qIkjlRO^xGcU;tsP~-5P;ufmmG;?@boq0 zH@)|HqH~&pRC4&zqcbX%dNC+i)*~ftOy^Vrt`ZtdMlsT8Exd5ft@f4}`vYgQ${h(7 zLKy(v<5Ne=7&Y>a9_3R;L#6pn{A`%==n$^9Heb!PH%%k6=|pA7HMV_zVs{FnWIqR@ zo<@&zt*s2?*jfsy8K}9z(j3{tKjwWZ;iMAN71QI$CchZPF2H0-u%{XnXx?(cb0tvY zUuL{c9fY{rz((vvlRXs=VfF56h`vi94iOpgc3iOOcpgSe7sSDKZ1j$Cgq+I>dw9mC z&^&2-*tCt_aaF_kQ^)_Ok28;kvVG&YwGxAnmu%UxWEmlmEer-j_Ux2>$u^iQW0$pf zi8OX1L`e)XA!Oe*)@kg7pJg(Z5&h+HInzZ>+V*d6`Xfu;m57FM(j7FD6+at=My6XfD}BsR7&|&zgZkS;Yde`kLKMjP z>qr5dwt=4E&3#t}p8KxaXrP~!-i>K)4Z97b@`Tio7)=}hPk840NC@;P1?Gr8Tc5sl z^?mzj-%u?c`7f@KwiIvL0Knrbj zS27VK_a>hXdugEZo4y#=FF3BF^3RC}za`cpQgV?>k786K#L2$9?xTXdd3aR#WOmZ} zR8i^DbLt9qrr03}o-_x0fg#2}XhPn5s|*=s9V|I_^nQ);;K^K6&jn9SEa2O@>l=xP z?O7LtnAeyjj#T;@aw(|vq+AF>$ZVmNtTtm6v`T^3zg!NEOt`$O5J0tSzijr(3nrhp zRVVh0dZ$AY?W{tm_ql(-yS>q1DODk?M3qQ;@B{kEL)6tWEdrVKVtN^7o^PwJdzkHL zG~PjLxIGV=feEoS!}e}^3ktqsrkrEAltsHw&1X`!Uf#1^0ZuM2#eZ}##fm42UcX7S zhA->yqZJD;pu7sWxtgu!6&RPOQ)E^&J(0`1yZ4>kE-h`vzqu*-rL%Kouy)<~lD$b? zMyeDzaY8mxUW$E`Pb=dpN3DOlg_+`FFWRHQhz+V4Re(n`$tBAq?3Gp{I(xU54@9!KfyElZJ zSq&fk`51|@{lYTsRCK$=SQTpsa|bvU4aYHjPZs9fW&VR5+sWndLf0FPS+x+#MnBA6 zQai$wkwq#FjPP4K_iMaGT)iA|P0v?mwHT-+_vw&&WTIP5a0L@v7s;`n>`XD#pHzzp z5>!Lj-f;c?FzjO`;6uFI8@;bnL!QwnrB_caJ{{WGXT=79*ML}|*tWW!CgwG1gqYAw z?oF8CjUz#BYm4|MN~-Y}WlXB})$c0J8$0B{{Nnh{qrs~1wjRR)>*Re*x>s#}+6XEAiY%sK_1WKnWDq)s&7G1WyL5o(Fsuvo)7@x_#b(Z%q_F>;9 z{KsXKDqHvCyd>6uD}EUros#f`-n~>ALP^W6Zs~O8_ zq-|Bjw*qso*SH|Lx?;Q3G&C;1`tXV+)CF}n?cNu}s45Fq*skx6jbHtdi=bAu_E)*J zU`27SDo~M<9YZ3B!2=PvA~AVumDiDK|If~J?T=4WzBST&bl|#u6=Bi=l}0iR0)tmy z_SdZG@mJ=QEqVIRVG`6*e0LJ8Gl`y+Zfs$S<8azOQm);~+Iv>de7Q`*N`}A;3U8ve z#c<-pz=)`UnVp}mLeH(`YyC#LuEBREQZ+VVW#z22o*j>FZtrZTNBcE8*s*&`j4fr) zyrf%xByXLSJ72}DbkMxM?Q9<~F4r)-r-yHO($ZYibr2^(u0JqDOP-=Q%h0hsW2=X% z>33T%o^TAvKWNd2l}AHdfY}EOexI*iY{&q1LmF~aHRn3%mF7~o;kX~x*gRj?l}1_5 zQSRb=#fez|+pJib2X=KGJ`K9h7jR1mrt#^xsF1|SVxE2}4p2*MLD6{5K0lS+vS2?g z5LWl6WnyUoXbuo*LR#*iWfa%AWap5KB#KAHt?fZXlH z5QeQOfygq?nhm#IFTX;L*(>9^;dF(7{WFH6o_lZDb4ZD1BXSZF*3*B%{at+h?A%=f z?V$hoVEqgr3DZCt6kTs9J?jb|5b%vX!^7o`!<4Goq^i&z@O#ETI%tnO9}?ECw|{d_ ze@RO*5eVM|<48A*mC3R04xjea*WWBkh_6sBj@1z>b4ve z_K7XH=@_HzK!hJ)lRL=N1ZQk~txI*Pus*pqBPkj@#LK4nRii|wb0{wdg>$g7@f`Kz zUPqv|oF?#Li|r%E-k6?1>I&YpV$=#)w%>xre>gUwwQ3A~*a*wk$;KaYP?xw$;7F$L zU(?%PfAkJn7bOR$`Jtk@f0?Y%6dNDSbYsN?7>-dn%}+5@JB&xFu%q;tLgZdFbdV4gne6QM+Sw$3uTg zcN$0^vS->$$7klxA0<*FOcuv;^~3#)-A8}YObiiX6!I4~-3-m!n9bf*NILar_Ha>}&_htGaR!siN%_td9k=VQf)t%xZ<{4}!%LK#kHdIdln3mC^GF+5{Y6%6 zrw+m6Nn0zt>$?3Hu#?+-rBn-qn;k+-n>mlOot&f~e)vk{y-zBZ=)K?=%luAywTF~T} z;#E9ov^GAu!kj%P@dGASEIM&qnLd{hD;=?D=0gkUywSM=NyTMh%YzEVtubykVou6V zO%_eWGgo4i!*Pphn30jT6a&15YU`MIIl;)vV3qfLmv*UGW2#Ptzi%hU1(arLfG^7r z@x2^)7g(yU%YUDWW$6JB|3e67TS{a1Lr-qIN5Wn|V*7rb;p%2x8UgU%Km{|;r>0)0HmsCpcXQ8JGDTWiE<_wPJL^9P(A{nY_5Ec3n(qC&HW6=hJz(*1*=aM{4P<*V7{E{Ev_>#ih4Bp(Eq)*G;{i+E~qXjYJU2m>?Offiw|+Myp`X`tAFm* zDvAdoz{Yx%PWOP}DL{WuZ5#)ptHgFX$wX`;yIeT0%fu7s;`D$4wO*y}kR+ zTGi2NI6_}?8!$ixb^*}~D1eLQX})^oW@j_==Iqza&xpt6 z@2fWauiL-f%ucsLJ5BgiE3|*!Cn2c~mpV=O7e%ytohJOMMC62E&30-J|0x(b&G?n2 z_k@8Xeggk4F8&{b?^(uKui6R2O5il(lymJY;%uTnK|D}Dg*cn`Ph)VQhJGX>%|U*^=-Cax3^8&lce4Ip0AgOk|>!CMQTaPmb-8N_ctH-CMd~C z`jJy>yDEn;y|E~|fv$He$3qSWSJr9mgj|aok z!Qd3q;osxoN5RQQzrt@-)o~dF@1>Du<35mI?Dqfn-+XoS^@}gRd(ZuUdV1Q{{{#5X z>Hp!`;OwKIf8YPV`0sgA$J4lukA6(bDlPK!VAvZx*n1H#lJh_^$AdlfRj|o}y;rNn zA}()rreGOQ-o*1Hm?qarRxFcgFuo05r*)hMuO`K+PV;#%E6U*YRT8{}ug|h9jq^$J zVDCSRMRK%+vgg57T`#Ni$B*Y}eYG0*CdJ}$jUXz7FzgS9kLwcu994B0*U9|$!QQh~ zeN~j_1lOtVbrLURhNEN=r`dU6gZt|oQ?Z^0d*7y$B(DI8uU`BRe3j%$8E3)wt8tc2 z0`V=lKJN7&?0ue8lQLb_M3Ns~eDmVFzrT2}_k9-E(7=U}^jQ{HRXR%(!0-FAn3wTl zfrxz@=kpa3>m1T=0lgfbj_m^9Edc{+$E(|_P8OKuyH~;2`7~K3@K0Vp*n64$bCs4! zb;MB4gHK1ty;BRrqtA-GhRjE=ZnQ3iZ-3eOU-f_gw152M>;(G%1irThP^ ze{yyz#{bFixc>zE|MYBd-~YeH@1ancUKZn`PA3(qOaW>}=fUTo7Y5IydiT7Tt};;8 z9tM9e(;5^CRPUaZpr)0R2DG3Wf>nho&jDvq>g7QTpnQ_8rT{3-;ZqW1#RQbLDX2t~x+rf0 z&^MF(IxUNQ0V+^%9hYf5=4MD>qS`A!Wt>2>3ecJs>CXTYwbjKctFOu=#yUVtT#{lb ziceN$iBQK$KDk1bDkx^Mcw7s8`S=)A&P7rMvlx`P5{p(zy;=fg9_&TYHEY&U6pVuX zK@a{18fO1=AHO~fCRd;+f;K%jntYumH-HS2X<4Ru9jdV!MXE#;br8Psjy3G>s{}v* z8=>nI3HUn-CRq~aSj|#&R^?F9tMG9j<=XZOsf8K3fD3B)gBN%<0}dMuCf z(=4rT5u(9_uoMQdMude`b-G+4WkJAKP&e8JOmM3TKrP~>VCZ-;p2kSDcyg7bFlJE( z`>HfyWCb*xd+w*S#>SAOCx{v?9)fdPh68;g%!;B5CX6~r*2!d5J7eJWcb|WE4iKRJ zj7Bl^Nr@AIo8I7dgPu!RiO=>iA%~tuOwx^g+8uWdWlzEboX7&AN&j`V9Uc9hN z49>l?TE)`g)sN4q*ozos&EMkN#Hr7RF_xJiS-K!;#`Kg_ELt=Q zk{{;+LrX+ZsAE<*ox!}CCiyhGwZZv#9sW(R(I6AAZeem-#wEx|-Nh*waso1XWL1ilB;*1H$Zk3D~Avh0_!c)u}lb zt4X9+7vU7vQj^4KgO5+VGm@>ShMoWAVZ0|T0F_Q+QqZSAv+iV6N@IW|oiUT^?-3Iw z>^N#aX086cmgZKcw*Ev^ekGCmtn}66mBYwjF6FylPQ6htSB6L&=N=+*ud#728VQ) zglE3COW8D$YLnH^pmyTg-R*dvzH8($D>KQk5W7&c;@$+)lU?mU8JTGc^EAK7g-SE% z(mE%e)bT3IZe`xY0qElv4_J?Ubk})`5Ow09ARs#J$K+G^_)2=1)Z_ zRza&gNv2A=+pJcUDEEu;Pq>00M8a_l)+fABHCviD#+Hsmno&-JtR<2<} zhjEb`&&3#+Xp+$rk_9v$VQ{guV!;winBQELk<30mwaZ*eqo|dbP*+DkN2Ecwm_%%Q z0-2E5EQocZpvp}wOat9cDA;3Hqi?OyXDeLap(&GWNz#Z|?pi@!0uJSq>LM zvTa>$YwYr3Jwicf2^?*|{5i^TG@{CFFHL)Z2dvR2*rI&iQ@NT9t=H15y+4+y;R+2$ z{H?T|X>)?zbY!znJd8Gr(LyUHETJD`cj0^olPZh`va~jtlU9Wm@wuBDi;zAk?Y4Y_ zbtaUt7K5DaiFY8t9FyF@P$IjB+;^X(Zeg!t8Ic)Jp=FmC`X#*3ZH3_iCL)p&d5w!m zR&^>6U{ewq*9usl*h#++?79YC(pSz3*+Ub%&rJ5Uhg?)-)3%(`1Yp}%4z||XoQ(~w z<2MQGXk>RXHMbdv$z@z$`5NM3{@5n?S%csWivVXD?2^f=Fvs~-WDJ53)>1$swB#6L zqq7D2X^U~h9LQ<_l4(|^BE71l-U0rT^BpoB?~-Y_OD0EKXTCN#-QaH&gV5DzLi7yf z(-z7Dg(ne=)pC~B-Qeab1!;t7s|?r=*y^T0<)K338*R_XoJm<0Y$a*zGq~Up6iQD~ z7IBtBE^1m4`>o3~Pa)M(iF%3~VpWV{*Xrt7iWY4k4FU1l^=;|zt3&LRRH)Gf6{vKo zMUX?xH+t$}d+a}k!6+D5j0HhXWG~Sjdt#qfrBBa&h7GH+ z$P=p)h+LY6OW(*jZY(Gm;CKb>fVNh(eQ9@%FK*%laHXTo#x$9hgtEOU)|^dYqHWJE5qRPqOUk6yMV=#{ zNb%gz+}n*xp5<==%Nkss1=T-SF;495JW#gek%(`cb}Xr^dc+1W+!esCed^g6K=qTR zc65!QHI4R^v9WRIK3eQegTb^5FWG7#_PEO8hLs_u*&;g|34UVvW*84^cuEg@>-E~F zW&{;X&V`BKqu98^P@=)hZGO?_V@SMPeL**%tj%@*w@Y{ zf5BA^zRU~l(EI6rS7bCjQ-Z(zl+nXq(?u2clIe=_bwn+slZ=83E_r~6M}YN#0CiyO zOQ|$ioFdtbccqu9G+SR*$$DU^B$(~gvoI;DcV(7UDcZ{Vob4^@%V*YRg!`2(*6MXR ziPf`dK-c;6JesJ@B`dj<8%ruxZ;7a+S3f@2jixyyn$fnD52~G^3I8jbEDuR@)Wgz{ zZ44CQLUpG{iI6Djw{SVMT0R^9Qmy=}c^c&6g^znxrU$*ffOC z$}ps}umwP7a_mPUS;-EOu3Z$MzSr8V;=vx<#z~hBf5heP7x>r_GGIv-*#~>mWERMw z6w0NX%tTsS0V`|u1*Y8O1~rV)!9fSBgkt#3kSzEb@ltUF6zI`oh`K-snS120RfeKm zr(@`^BCR*cib`2g#fE`~${h;Y8q5Y2h$y*r@K@-9kW7OxQ;d)l9d4c>7(z11Q0F3S ze2Z!mP5R!8tNM1Cgfk#a-I19inn_1Ap@v+tqrRZ=lfL1D^Ofvm@R9yH-=v)sKFdcJ zcA67{@YnqkulY;7)?X~5MQodmq;WP9)>&0;m*95^7SokWR%*f_7D#jQLV-2fvPjVd zi%5YJBznb6(SnI;A`f23RV}fXH*|@R%b@{In2sguaqTdpP!^ooAWtWF%(*y^)mAEC z^Ttr{Mpv)KVF@=W3*0lp5koWN1|(InP{28i%Xy{vr@`d(FLw{^f0T2QHBd&64_Ro7 zy-(H{uVJi@5Qgkr(Oxwcy!bbXfc2-|yn$l@e2`I^`# zo9(O1TdTd?6*HQ%~8b?gu`-Dko3e!vnQ<?bGpS)nw2?Xq z{;`GBx{CKjazN(;CHZDDFSFY=GQ+4{|6_m+UjJi=EnffQ82)ZVxB^3w1bYN>;>BNqYn%A)?H zFK2{qpi<;a&}sHOESo9?rmB-=1rsU!*TeN$iEI-o4r?ik-NvlRNnH=LNs-JV=o$s< zVsL(b>4m6SyQMyn&5xB+23W>M&QSmwqYF!P%)wBlt>PVbntpYmgOz^xg1%h;RX<|CqP+RE{n>1A6E%9a0+O3gKsoFS(!?vxP$UI96TEI4-dmot4bCa z)JyG(I*(s;+{Koi1Nz~cLj+i@7NNuf0f{7u0E5BiNGCP7N$S0v8ZXyH=4q{b$E;i_ z`%>tJFSGbIDWTcIQWJ`E?4!V~x15@Z2odTuli;;ugYB`|JmRc(S9S1%pc%7{dA^?eWRgl6oL##VSaXUT|>{ zE>_SD>3k6bueZHm`*zEyv~ENq;%3?EI8uoYrr^e%!qgZ(R1(iZIFlh36x}U~p1aae z@Pe`%^%?Wg6t}m66yB*-*AiITuy3HBY&yHsmoe*7a6N zHfuwgtg02r5Map!@@K$GKPN)B^0Y|m6T<7SR}O{oML@LbvGBpxZNg-S%Ft>fTNSY_ za%;6nusc?}i0i9f^-oyZzbyg=V|mT(|pDR*QTfc1|3qs}Wtd|qLD?*MY| zTBk+d2-Iq{$+T8X_ATjyY1-$MAy(HaYJ@pff7}8?8EpMf^yiImv~AZ12EOh9!!iK- zz(9r_z-Uvk4@Rs$ZUZ8$%6=g7W2*rYf$&aN$Wk0Pspt$wHG0}5UxU&9n(c&T8$eB{A9Nt(#=;%2T0}zNee;`9}Ie+ z-B~_&Op4I@(y+c7Ll(-2JR;G|H%W3qERjc0ED|3ght))~B@;Gs+;S3Yb^}_GGZ5CN zc|fQ;XXJfxep5k5s_JcmH#c|hVabx9i$W7~?b{3jaCxW_* zt?7OL*|X3=)qGVb;~xWMesFNsFBUg7@U{~+vWu&w>F$qe$XDUwcLzaW9sJ_{$>MQ> za~wJ}M5MqVnUA5tCE|yxM4<2-1`JS`Jj{8(>;t@bmDT$^jHa-nyi!CD9!ewm;c&_h zP`F7on2PG`eLp<@*%i-#g~UmIkZLnfmZeaccF z3|hM^C#+)X(C?=C#`1*N#%NmXAK*YffUHS8S}bb%2OOL2iqAn00B`SRg*X)NBa+lYgOxe-@K5I z2IHEgTz(7Qf#G}{^A>C;nMnDy2)d}vIfEQVPXlTvs+&Ky}lKXG->f(m_(g{4{gF4ZzhSwQ;V`<(^x$- zsh0++`JqW=>%E~OZRBE$*zbXjn`RD+h0V@1*09>o%UZV1S2!kMMZ-(DEfR7G(@LqC z4wfHLwI~WY{ey-Tyds*s_*>8p#FB^SHz==>IV|*PjCf^3VY*>js8rHCp-kU`6lun$T*^uVO(Xox0ilX600Nn^AqtW4TRgI1D_4vOpYz+=3= zXRcp_xxOzGIy>oMF3!MCS6-h?gYz~z>9{?ly5@l0yAC?dYPDo|+-?N=AmMEKQ< zM}?>5c)4l2M?vsu3_GX_R<#Y}4bS;s74a=rm3rF^f(5Mspy7oe5`FwW*4U zWI)S7*{h5S)5UUyv6=vrNtwizSo>L@7#S`zE{Ym&hFOXkwkXEzha#s)%Z~2Zz%{ma zfim>1tYzWG47}AS`kSs18oPpcgUw=`B|U%0(9q-zvP0@ud&|t&4)X5&c(?^WyJ=1S zadKf&J41=gzE3Vo3FLG_3wsHWjdX*vaLFy74F-c`_3YX1-nd)tQB*YZp&eSiY}9!C zyXQM^e0X-$y(VL&0>9nXN6X>fjhi5w1=gZU?&VceUj6QOY<81R=AO-OA)ek!@<@&m zwaVA`FP55|<>nPl`F+T*%_3_1uGU>;)Z6WA39ofEexf>Fkwx*u+U81X;mC|iV&}Y} z&QW?V65h$dVK6v5?wt+Jp5pn5!AZX#nBXc498w%#Ot3b%Q6RnQj|dnZTb30lYRIg7 ze?xs6Dx|x*!j9XjX3lh%d|<;e#S1g6y=3qDP|^GGIVj#?nUwW5|du+?8puBQ3s7ZAya1 zLa_oE8G5@jd&F{l;u+^{qmU;Et`45{hc%jSj2l$e0~=HQbaRD~fFb{%Mv%+t>xaRM z!mD@N=8RONSZ;n*;9lt6RNhNv%YI+_=9FNVx{PJ_6 zm@)GBA#K&Yrs)(Ga5XvACw0=Q_Bq{u>#m1i@cz$XzkhbD?*Dw!hkS$6;o$!M z*Wdj9&(D7N{MiwQd4Fj{@p-37>3EqZb59y>qaOLrV(9}!)5B5Ffl(} zr9iic^4gu^*I|5Ebq%FkEnXF}n#M;uPCf-srwc>rH58!)P>L6MA}*_3viV};?VWwT z8RIQ4?=(i-4)=JPQ8%|l^)~NLQE=??TLXSKZq|6l*S4YQ$iD0eV0X!=dgF z<#e~OsjbmaIov*N;md%e7+VIjEpJL4>@#02B5YIyEV0nZFmObmXO*NP&T!xBaG?#h z&_#-QGZDQRnyFu`m%D&wg6Irief}ExNk>v8e=$xIAwp$bOuYL7-{)B|#(RgUb-dF- z(XL^nqO3XZ?_*dO%HHFDF}askQG5+_M+jN~| zw{j~*6I7@>=z$_j(`;Leo@91t?oL=cWoT*!rGFKCiq<#oC>Q(Th-rTe1vv84(~i( z1jZflDp4?|K0}B2wMNbTsJI^$?{icz@o;nEy$lIcSH2$-_e0{t9TI41elJ79T2#K9 zUQK@PyK%m7w{Hvp20{NCvBp;mUSu;3?S*&fg4}ILQP~ecLw8zBWZXc-bQXxC~AB|et zW$Xcq$nl;``Js!gRk>yA5j=aHOaRXti_F4op=@12_$JM#W(fUBu^6Yh6UP z1TMcR+5Coa+p>5<2N`7$79)hS0D)Jhh61V-82!tRafLcQa$YyyzuKx3#u*7`Q)!2O zc)!fyC`fHqyCFs!_-PGAcIgs9A&`c+@yUb0?g!Ze$Wfi$1ouO}+4V=&L1;Gxfmf4> z+w7(cM&acKQaONSAsL3#gKpZ@vdr8`L;Dm!8%fCg7jNZy6C%h?=a;v!2pk+c3sO^B z1fA0$?D#q_i&Z{F194U9CGqoDUwjJ*7^+x#ZB(N{A05=l*_8s(qBm@h7CX{}VN?Zcea%2JS7{0$~Y#iZ}!PNYjvF8l@+{gmV99jt4S^%N! zWfR`~*3EZ$h3xC1r<0oIh$zX?7CH@Ste-cxhM5FECuKqT-9X&ZV{{}vOvN}*RPf{n zF2`1oXK2@;hc7F&R`NG+Iy7$4h?34gdw78qRo-c92F5B6n=E=E7>sMXv~K+PBM|u= zNXHn-acdavouNxW-hKpc7_ax^O|rsnTyQ_iKGae6(f#kk|F!%7)aVx?t`hkr{eSvr z{nG*Y|MXA$$4|~q$p5E*@Bj0g`~N)u_T7Aa*y~37zVN0w`rDwp4u^x5PC4ulb0GKC z74IFifXXM?YDz9bynHF}ip^v5Ktz_d4zb_7CskCvn> zx<7Ina3o}kj@c+k=m~$Lw1#Aimi#7x6yT>>j1h++KaduQc~AwDc!{^%R~uc3hIHR< zzdv9PfV+AS?cg?~62y$8YIH8!UKH2p-V@i$tf;eeEPBtE31+Y570o*x8>a7u;ykHY z@2cbjUJQysAo!-osqp1V7O?QI6I{)bV~+QNbyc%-$??$V-y^S%#*TBZNtXrsjT@WM z;{sZGgGUPSnu2xDG zL1*wd-q7GH)HSu$EsQ!)>vLEX`|ru+bpfFncFqH(aAOHH*Bs>a!MSlL0~^OsLug!e z&IPR#hOb)C9=kGJ-L!6(^re=rYPT#0j9cI&1P95Lccd|H1Ia> z+g9KsvmOC@B>q|zkBn#IP9j|M^0KnaGB;TkldDPuZjmxqPJN|ufZ7ZkZFQ+EbaOSi zQdjt@yG8k|f5$v?j!U78LdhGI_QaQ->;Sp!r4aqFWU2L8igJ~cJ&Q!VH&q=qv~Dt@ zw(4~oH=QrFL3tY#*EBgN5=#zQN^&_}Bvs=kKc1)%U;@-m`0^l=j{Rn+A9W3(xYo9@ z&Q~ij4#Fcc4(yDMrCD~Dy=-ilqvh3xFnX<JpFeJqq?8tJk~^&5`ri8aY}d(Du=O@c#(T zKJmAPNv}3sF#`N(O;#m(Ro=eQ%X>zzXnfy`gKkBZ$cQpTOw*R0(eQ@I1fBGlG zd;8C?@uL+~6wOu`p9a=)V$l?jt0KcYpyaAwthG=I)J2i0CD-H%1cq8`;W0^-w@TMC z7YPrTL@NEMhqeO+W`VJ2NLlL!Kc`DSgg41;Wvl`5wxS1ne4jaKYFGr+voW4S5_w+G z>w`UU+U9FY{Q@uO6NNYfE|X!7>C)bEHz1{_E0+Ij669%)L4W8&cmOX4X1Ev;+dnME zKY`XP7ZVT9G59A35bct-ndWF0Na(gtc$HVNiCe6fMi2wDY!FX>!V9dXbQc{%TB?si zb4}s%_?Et5E&)=+t+DSEw7iZMD-lSFZVDC8%M{~DLE%x~aWPq}&|sDqHQm4 zLS80X5i$f`Y7hbQ32-C$f5*lGC#7k6eHToQe3%@)113>K7(f*UJ^^3S9emGL?V*i! z^3QgRo!$oSmiXE|uRa+3B4&hqw#4Q8fCI9 zJ!TzI{NP0hBJW@4z2n)xrO@NI&H-+*!te-16*k;W00$Z|G}X) ziL-zGD|iw7``=+8YvsTH7zW#_baDTWkE{PU2tMXI^!)RjFIAPXDOx0bRZhCWl^H$! zCY_3CiH?+)=c+>}AXchLHdG zHqd2-7yV19lk+um=^PrU7a_uR13vUUq<=SJGr+niTMIG+iHs+cR%a}tgJcPD8P>tu zf-*Ej&B8;uYV~4LPA;&(CEX|F#3~cokeY^fnW&%X)Arc^3(!a;U&iDno0RnNV0$=^ zgPW^#awRu465I@=%g;gK%4=ax*#*p`D9S0ijwXD9AE$nIO1BshCu6KAB+50Ifb&Z2 z1@{oS)E(3rAC)l0;61{1m*y!BA&Uc^ZKoT!QT@t~rs(yzK#NG7PRvF_UQi5kzPUA| zL23#Hm92_$srqK|Vmyt5^|@Jc-4(jAL#I`q{&ST?lOiLlNyV%jf&!0WZb&$|rlJJ2 z-dnoNDvDu|q3WHb88jkPs6gS>f>FM>>@@oa!|1rxcQ9Pm0Q(Auu(zh&bVb=)pd`it zZX?5DWR=0vFfPmZ7FBk!v~W4Bz9~Q<0PPjk@i>b(Kgmba#n;m>7>BZRzCk$3X0=JC z8x(tXt!O28+Ft?|9R-a7GX_vm-nagnI$3qmC0+);O@?vt? z^n-^k<+g;TW=M5Q)$mG&6*rcvX?lGua<4$n&^PeowrQ`h!4g z>MUTSJ~s-64s^WwQqF7C%C34II7CJTTV@>?SqIImO^I@GF@+&BhB6mN04TOjlKCg_ zt%7@3=~<=ooa5WlNe~!wlMYNMLpIC~{4>5>>$4$=CTY^}dZy&VaGMByM`fU^CNa%P zVhf|eNYZ|+yH0b@hI5b>$!XB(uJxYyR(b4(b!1>s#p#aWIG#@1O^a_c3PPJkI2=MY zNXifVwxde%T&UC)IwIoA_TX1!IEQ~&WJzoNo%BZ^%WT;@ketKB|C2Q9dKY|6y8SJl zHpsQHJB?oXZ3v7e4nbrP>W6~JYyT{vZ0!hsteTgmbuIzo0D2LbPqo`1&dd za1~}p?~H{-W#_KjeKGZm^c=NW$eH&rD7NfV{OjN0zl=Qz4H?$X=&c+@-IkTL0NmMq z&jK`VwBubI1b_Z>aM1gyNb~UG;N#x$3|BIfB+Hm!_<%GkZZ3sx2&;;e_hc@;TOi|P zp637JS^wX`$5rq@pw`QG1P*!rN7RT|?6T>>0iKgQO3K*;=A#h*c4=dCg#Va(ATrCX z+1&m8{ny5BygUkkHa^VXQ^8dQ@89BP)6hSG9S!=X?n>**TU=7MMPjhBlNaX_8jngR z6fMfGEe@$yHHQcqjej4cc?WUx_LZPNb+kHu9s0 zvkcQ4BLXPe#kO~u$(6vBK=1(1Vu>v{FosieOI13ZE!khq*%oa&iP?&c@jX2F8Z{!A z7WWz0K1QR)sMT~*P~dgYlt7%jms5-63$;wqQKXvkW@!kLM#HN~y1d=oZg#4}d>C4# zR$g+jT3oy*G7$7#setwNhy?Vs)K9W|n+pHOrO7imL(&Psgp96|za;x9&uqzol?vk} z6HxC#&b!p7M-==VR3gDoT}j}}jTDZA!zAcm8ke03+q8*FETuuz1}exXGii^I7sWI& zyb)#KGV^rTkK#ydhB;nh-*jS&%nV6Y#dF#MHWhHzN+o>X!8Gf_I2lSR&dds| z8=UxN8qCpfyX%^sc94UT@UPFai{RL1VSVL|$CWISSdjE<_>sxj6+yhJid*6!3l4w@#ej&j6+ihJOP(H^no1cO4W(3OlhxfD=`_pcM5FBt{m?4P zCaZP=P??VF3uhl{1|g=$Wu8FD%tFts~oLmg>YE*06CoN1?E3(b^Oba@pGT z#3~V+vI;L47Y7H@CB*(h#zxk}4l2)_bMO~fg~ERnvE^et;-BYyv+c*#r#Oco@~mmG zp!||ZKw@%(aO|oW1I^H-7|xFvLu9ZQMv#m%jr!^c4oIg+h}Z{;U+ulE;wEo1BlPXj zvo)J%yBrW`c5qceCQW^c*h-Z5b0>axwj|z;dF&cJtm3eRMr<2P?930}muXQ7rub5g z$xUoQ@~9h3Z=n_G#P+G@(O|%A%$FAc;j(f4Wt_Q=eLPcg9e4tapSCiJ@ToWE8CgM3 zdBP86tV#)>QQo=2#&{MJ%YUs>@6^j=s~On2`9JPr}bURWja zk&j+9yTYxa*&iN&jsC$rBs4i|v-k8wdhBY-Kp;fJ%~^U|W4g3pG6%S~F)>UHUtP`y-T1 zp%IIBtOK~NiKLI0L>LKMTvK+%6IAwsvRyiPDvxW=TKHq?7y>2==esz9lc|Xo7*J=c zXAQ!WD@Kw8l;m+D^f>(ap&U(Gpolf?|4qu`^YogIq_w$}+YtM`&W5hPIKQ;N3M$J? zm3cko```|)l`FB?#LFS7pq}8}0`^LhZNrA6?qb!vGL+N=03u)@`ah@3a5Hd1D!u0E z1Z{rR+_C7b(04=cJiew^uY{s+?b`0H-?fKw+i7`FaF&@XnXq@Cff@dP30CeWdRgPgh{?5T~gCY;| z$9=p@9@OcD4;*e|Ch?TdVV1=WmTGWtF9=>0L@rtyUL`(A(*!SW%VQ9*be&;c@XqFV zm1gx39@D@!d)g%EtviNos(iqdD}DQsrudCc@mZ%Eg=14hTN_(WHp;tlwT@;_Y+}i5 zO=PdjRX%~NcU6!h+=c4~H{$QKjBla^sNQMboYU+&c2t-NFb)!Wa3kT%uQ$H01~RBv z>$fc$2rKDQ|7y`dN>}A*9m}YkN7RX0S8s5NTs`3jabA>*b(?KDl;2EAtS}==gog;w z_DjhVX|*+;64hj|&eFvZG?4Kk4X z^|K$0%!7X4&JNv75-=X8Sz6!9BjMyUq1VL^!>~4{`Tx|Uw`}c>;D6gdIN^= zi~`G%B%53%lQ(!BX6isGlL{(9IA45AI8*4nf!?1YZJAijz#%kvuN!b+t0cd+%ycjl zL6YahRYLNNuUU-XC3JS;a2qYZ#?1t?8e9Y!3tK$(B{Gf^G^FBwrVnc_9l9-e-&i9e zxYg6Bi2Q7MKi1j>bX%Ztk6=Pu@R2BaeK^MdT`E;Ue@9iRZ)|r^KHi--%9-07s;G*| zoa7;8>(Jsse!e&wT&jKU%={qhlYTPli_rq99`L4oMCw5ZEw!oK+q~Jwvi0CqQp+v5 z-eTFCE-TuTx=QC)NqOLn^(;z$7U#IBq&>4IMTy73GVJ}NR+~crP44*}SZ(`KG~;`0 z1#epMs!>mpd#`|bT9VMdBFpf$m?2Fi)SMzl4Yb`lq@a|@x5*9hxkY%e zjRLpkc?VQKk>5uHw#TNh1w@OJh-LDmxGfRSP&>=U&9X>z-JNBS?%W1v3&HLeHrLUO zx`zUnysfL8HR;H(C_J(k8?KHm$*MfMO!ps2Y4oTQiZn%e;lj$}%|s6?+XcUPTt$Q* zZ;a!kDKPkmhN*=esUqFr$p2wO>ta;X)Z4gY-F`*~$yHm&D(jL8HpvolnHGwmI~01C zQ8Knrd38M+Sbcf(zWUNwZz04cEPLsNCw#(dP`cK(^FbdEFQWgb-|fIS*~}g_er3x# zIm^xb;CMo~%~A=k-_pjWd%A@vpmFv0jbyKD-Wr1r3S)UBWhwC;n2_W#E24&&=mOY(y!AXkx`c8N^CpW@GQ#&J&bb= zbc6?iG-zbN?=6dE*uj~G=ZtMNaV|7`BB41*h0z~2dC-Rr_~_@vZgJcEZ)Ku`a=yqE zm}YY&y7>Fya}(^W?%89ENYN5ap-~mCPMh_61AFN2c-zyhD3A4Yy`dMdKFBATL&nRX zXoX>;&ejMEMjmy3n$lVVWGK{RZb;~CZjV}ha|{~`El*LTuU$gbxi#j4-E$b^!5P=y zuGH6?nuk)&a?~Z2SKf(R_=KdBck=m)`!qd~zeRJCUx5q4K^NzE8tr_BW!^A-T(+-} z6<)p(B0fg=t!q@O3i)__-PHydy)n2^ib`d`$IhZI+3{_WLo9yR)o$c*pagYvdjLrH zcnW66lWn&xC)?CHf0J??o&?Bpi^tnN=h{SOlZ)6Y8qswi5$?TAlhQkBQjpO%mFgX{ zt+On~8*apSuf$nBnmtyu@nm;OH?XcGlcjaHJEdifPB{@V4H?owpyN(zM%K2`_)x5r ze9mT~jPORgP!7PI#;HQIG8$dBOv>3?6eANcQA%V)ct)FfgS z-@+n6GLor>&Nx+03Y0PQ#%fX2HisH+=NkRcb(zjL_HNSz6bsYstO;AYL3e!EQZMX= ztL!i@wyHPV@oczg`G9u~D~RJurjgeC(~ddj>Drv;+|pn%VN0+Q%oKd;jVen+w2n)P zaG~wlR08Q1yi;e&LGS5IB@fzp(7{-i=x#q>ZL%gt6Zb>4d+2TpwuBs!L>L%DYbe=MQ)hj?Cc5~cNW-zPiD{rP z1G;HLVLEP6mOZvlF9906(L0REhd3(hooTZxD*n))hE_nq677|jq^D1tw|x+H-l=RM zDn`sfJqRNZU??em#$CrtI)Rr`OwKg<-+m(4-Ws;4L>|+pmpk=D8`K-xE@q%NrtQog z^p5ArT}QqLg*Ic)Lmu#~B~(o#P*X$bHs;~3f0FYc>Xo0}rp&PncaR)aldA-GX@nlU z6ILyB^5fcq+GZFU;VuS~<0DNYawKrA_wuXvIDNyCq_fNLgzdrJx#yfEl#=A3RQz0l z%Ef4bL3d8k!kCM6SRrM`fH5jTT>2pNl66hn=mxxw*Klj*Fd1-;!Q+; zL-_x(ZbHM{k=Dm>qy@EGk| zx3|y408ehxv&Ax48k^hMxh}{(=8wUY?jR0+WHWL z;1VQwfbP8+X08@^#++UM8oaKi3N;aU2rz~z;f;MpBp4UAdDc$MDSv+~ukmgF@ITF&hve4W>b|i7^t%c*!@!i@oRyDO(;tXJGcCz#i z?HBK}yUB|Oc#uQk$G1_|X45gua#m?+cHU%gej7ua)UaDD()?}Iw0=3J zXRZSB(tecAw^bYZds*ajuEyRQsxaV#wUvi1_?Xd%I&d3df)yHm?FLRnKnP?+PsudY zYgxl?)M9q+Y??WN<|BbZ$F47#Mh;w+O5nwtVehA1pRVsHPB`ykg!G#ZMX=(=xE>## zDy7NV-Pkb^Q9D4L#$}iBn?&&<9~Q8+R4-7+sE?vp4H{gmumN~Krx!& zJ$ihYoNnHwjY5n{UEYelPq!A#K&=+LWl)`1O!{ipQ|&)jG1f@I1y3;jI#Ji|9i0^#pihh@WEz)Z;9g)F5 zt}s$ET}p^yOYts9fL0Z`=pvhxWl>`s>nN7A)c5^#^;J)eGrrAbthl!&oUGvAC{NaP z$EH+6x8j-Pu7(Il6kH6>FFT#{?Tbwi!7z{EumKD^ZEH-?VPyXCZQ*)i~4?XBWJ z4z}Wk{2_2-!tI9L(KhsqyU2W3-sr2r*3MU#DKulcUrg0VXLfV-^d%&Y;)C~gi}@g! ztx5`13yM{GO$XO>-YBNEeCX`H28T^t4;3a>@hbKp#!)4#G3YBUw?7R*X*S7QW|2;( z87{U6^+$|?dBAGw$H%B+tB#mNpi3fIjyN78<9R6R?#YnneDG@Ogj6@0C%_=4PT2Lw z5>`u5^KMRzy+{~2N53e^Q~)5OypKjslP1T%(Jt;X#D(8!os0G;+~^x*1%4NqjjLaP zc2QhKd4>y@z~a=;Z1z!vnO}n@tp0p9p~A^)5lw8*725^alB=t)0_|H$GySq zb%@D`){66X zG`Ow?V&Wdi;W}iL^U;?vkhx(Admjf)g`Mf5-wpa?CB^#0O%j*-WGLpr!X`bJzwZT<1rM+s2J(qfm zV{mso|90o@(TDid$tcE&(Ahq2+nUZy>vp(3@WOYsLpR7Dc6zOV4bNqBXgFLS?yy?- zRlB*kQIAj!Y^A^l(a!kM{qK+Vi}N_Utnk{eZD>ST74Z*6S3!p`8a*GWQ zTA<~VY&A{D*p72+R=~0$n5HG!?rt&EuQAOU+sy=Q2I}%HKGXb3p^BmimwZqm%`y8t zHlSDcDYgs-Z7eZi>*3d;kbj*x;Vqw~l0GArsV9YYK833+9S z>vo~5sV3q-Y&m2oYqhc3=ZLW)gCGUK=pj5b7>O_X1N7)k*twVgRWF&(kJ5ZrJbo{t z@$BrZP5#p}%KyRebTBxD^ufvC$=OH2$-VslJ@o(g-+XoS^@}gR`^Dye_|NJ8)8X-x z`}zOZ_&tYJ*fg%=qaS&~I}e7v!GpaQ@gg}7BriPJQ(py}JlK1+S}bB%8>>vg64q=O z?0%YD!%Vh>i3St|)-GO6iWP11qRAfb-g*gNpJkcY$obD=ksK|d?0Ili*URes@#A?4 zbI!OoDHe}w1W_RbR7)P$CH^_8YFvcPVeWfIlh--Hb*e*UMrJrdx2xm1Tq`+=upyTEsNWgfNT)ooQL z3(WG}tKjQ=n&7rOSqxq#|6GBdQynps^Wf9baqrZ^@aVH52X(DJ!sF!7{$yQ0hOT>q zUVjhv?w3IK>%YzN|J8Rty!`BoSG{^&|043g|K#M%k^g7?Cxd(W|7-kc%>xSdo?7?I zs9IK{;tDgdOy(eYDqe%=WD>Ah5|>W0wA)#!?owv2UH{nI$eNz7QcO;6qbxxoYLNY0DRcv-$%yxI{q8_-*7M-oZOH9U*mT_|KHF5TgJaBAAgbgzkhP- z&i^MT{rmC%Yy9vE?s$*l_3zOIhxqTCv_7Hf7*zzhrAyvf1vUIF76(89sh$TXL9_Xob^x9|Ks@h^nU*TReoZ#oqD}4ii}Qk z>$e{J6!QK!`sj}KCCBV{gNMP(WSPYiap3K?Sd~F>ljGIzC1&Pg!j)pA+$f&zi|u}P zI~eV^?A+MdBHF(h?YHgU$lO!C^P!P_>&+<%l`ed+K(~E~CcNE3aee_^bGt>BS(VU` zJ8yN#(xNTZ$mpk8nolG7L0zIu&fr-V$tC>gLVJVk)6#FhYo{!4trS`kx+!XTXn#o8 z*1KLd3MUYGNGTIrX`^6&%SM}oH&L5nb_;gcQL|9mzM&?wOI+DANz?Cuw%5>I<=$EH Z{&)Yo|K0!Ye;@Gg{{uiF_T2zz0RRUFw=Vzy diff --git a/analysis-master/setup.py b/analysis-master/setup.py deleted file mode 100644 index 6fb83631..00000000 --- a/analysis-master/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -import setuptools - -setuptools.setup( - name="analysis", # Replace with your own username - version="1.0.0.008", - author="The Titan Scouting Team", - author_email="titanscout2022@gmail.com", - description="analysis package developed by Titan Scouting for The Red Alliance", - long_description="", - long_description_content_type="text/markdown", - url="https://github.com/titanscout2022/tr2022-strategy", - packages=setuptools.find_packages(), - install_requires=[ - "numba", - "numpy", - "scipy", - "scikit-learn", - "six", - "matplotlib" - ], - license = "GNU General Public License v3.0", - classifiers=[ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", - ], - python_requires='>=3.6', -) \ No newline at end of file diff --git a/data analysis/__pycache__/data.cpython-37.pyc b/data analysis/__pycache__/data.cpython-37.pyc deleted file mode 100644 index 58b83c4a48a2c2984475e33e57f4ebec6897e590..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4239 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 BYJUI# diff --git a/data analysis/__pycache__/superscript.cpython-37.pyc b/data analysis/__pycache__/superscript.cpython-37.pyc deleted file mode 100644 index 3f36ad6398b65079f0f52d839784607fc670fd4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3965 zcmZ`+%WvGq86Q5EyQ^Kv^1BEe!f@Iy;0TKPaty%?b4dB zQhXwkOcs7Vm2?J-4szkkQS8fP9Q*sR-s^dirf+QKr=g6+%9eMUTZWuNUsA0;Tw_-e zkMko?<^KO69wb>(j6fZSNk&E-<$PtEpS-zEnuk1y^6VfPHnzzpI7KqUQiXO*n&%UJ zKoN&L*-z3$o;27ije{Ue(;)ESM3KamM=D!Y^lGWfSaCK?vdZkWIpd7OWIlNda7FDO zH`P8!j_b84p2%N6YLcsPfZ~%RI!v28M9`fAA(|F4A1__+=lf6NNUm)7!{&;#;ffs) zH(?~FIP8fpVmTF=ANogFBVTpu51Ok~Ca42joAdc7iAH`>_){3aa@>j%J8z0NoY9c3 z1ALmvB(1}y@FyZaN)SgYORnZnZI2=n$)q}e(_3K{rYA*G-1f8Sct6x&a6)GDW7|L5{xf>CN9uJKw;7`$tf1*47hh#6=8Jja{a6_`I z?9?!5Y-)(AcxpT`=f>Ow&aP>WhSp1)yJrS(U1!whmRw-G8e>DIUCVSdbY8lpRT^jJ z+ySjy)7CU>l}>4%nsXPFUQOw1=#fmxPOW(hwDp>{pH0xDkpIBl(yS!jsR6zeQJ}G7%^;_~-3Ky1pWuTjB z?;ry-!&*~rNuot&Ju%I)MG6C-hp`MuvjCNO97^QAz@@|lMTgp;WdoUO4!Qyc zo?45hLZEsDdhsqocoNG*CV8gZ%6!TVCkYIpTohn}Hf^9EL5vL%3qkchF!kB z^Ygn;el2n;FvSxocD8qR-Y=v;9U7iIAyI!3z)y;X7=%?B`Ynh{x9YZ zFm5$39pozInsMlgy<;E80S0oLb5F9eReGmp>6RXj_vkUp7{+$#)Q7c8v$Qbo;NWyv zV|QQ(8~YF1;xd48sNNQ;SC#(d8D$)f)%sB=l5jtb7ie#RP+byRpnD6n4nIUQXs;WH z{5L`?D3X{qkZtq<;dQ`E)*2#WX)ZEb3r(Br-x!oAc88|eTzLF5!kEC^0>-2m&=?Dt zkAcCbp|qtVVZu{`UU=OZ8fUiVa=|ru3s-B&<$>$PTe#Xwt`4~V{uZw8l4}iI|9T5o zuj10>ufMChNZ%w1^oNDII7)E60vZl`-$Z zNvf~aJ=e5zE=FlnNaYCnh=w+ChFqfv6t}3OsA!}UMd%4y@eVqM+R&-n4ZvoLh?5?w z4V1<@V!n$362;V&z=qx7f20j1gMFQy;o^Db))x*E!W2F5t4mc4Mc@ZICv-3m9B~mu zae=xk)Lo_SUFu%bc^$YvqZL{uvttUvEES}>vQ+S|nAB<^KhG>=|BKRwx__EG$A2sx zZs-gz=Pv3%3!EkuvC=)WI72AirO}e)Z~G9A)^` zjW7Q5o3H-%U|@+4F;}MG$~wwZFkAR#Qr0ltQKl>qEel)Y<2s9<2e^3xTxZcJF4SeZ zujX|I~_^FuUKSp5Os(&y9)wwE%bUKiI1YH;U($p7Ab5$S_enUa6D&0cL qyEo4HG>z}mRa4wVXTN7#wqxSPrPp<++IqHk!}Gig-c`@_Zv79Y;1Nv# diff --git a/data analysis/config/competition.config b/data analysis/config/competition.config deleted file mode 100644 index 511e258a..00000000 --- a/data analysis/config/competition.config +++ /dev/null @@ -1 +0,0 @@ -2020ilch \ No newline at end of file diff --git a/data analysis/config/database.config b/data analysis/config/database.config deleted file mode 100644 index e69de29b..00000000 diff --git a/data analysis/config/keys.config b/data analysis/config/keys.config deleted file mode 100644 index 77a53a68..00000000 --- a/data analysis/config/keys.config +++ /dev/null @@ -1,2 +0,0 @@ -mongodb+srv://api-user-new:titanscout2022@2022-scouting-4vfuu.mongodb.net/test?authSource=admin&replicaSet=2022-scouting-shard-0&readPreference=primary&appname=MongoDB%20Compass&ssl=true -UDvKmPjPRfwwUdDX1JxbmkyecYBJhCtXeyVk9vmO2i7K0Zn4wqQPMfzuEINXJ7e5 \ No newline at end of file diff --git a/data analysis/config/stats.config b/data analysis/config/stats.config deleted file mode 100644 index 5b0501ac..00000000 --- a/data analysis/config/stats.config +++ /dev/null @@ -1,14 +0,0 @@ -balls-blocked,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-collected,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-lower-teleop,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-lower-auto,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-started,basic_stats,historical_analyss,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-upper-teleop,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -balls-upper-auto,basic_stats,historical_analysis,regression_linear,regression_logarithmic,regression_exponential,regression_polynomial,regression_sigmoidal -wheel-mechanism -low-balls -high-balls -wheel-success -strategic-focus -climb-mechanism -attitude \ No newline at end of file diff --git a/data analysis/data.py b/data analysis/data.py deleted file mode 100644 index b7fec8b5..00000000 --- a/data analysis/data.py +++ /dev/null @@ -1,102 +0,0 @@ -import requests -import pymongo -import pandas as pd -import time - -def pull_new_tba_matches(apikey, competition, cutoff): - api_key= apikey - x=requests.get("https://www.thebluealliance.com/api/v3/event/"+competition+"/matches/simple", headers={"X-TBA-Auth_Key":api_key}) - out = [] - for i in x.json(): - if (i["actual_time"] != None and i["actual_time"]-cutoff >= 0 and i["comp_level"] == "qm"): - out.append({"match" : i['match_number'], "blue" : list(map(lambda x: int(x[3:]), i['alliances']['blue']['team_keys'])), "red" : list(map(lambda x: int(x[3:]), i['alliances']['red']['team_keys'])), "winner": i["winning_alliance"]}) - return out - -def get_team_match_data(apikey, competition, team_num): - client = pymongo.MongoClient(apikey) - db = client.data_scouting - mdata = db.matchdata - out = {} - for i in mdata.find({"competition" : competition, "team_scouted": team_num}): - out[i['match']] = i['data'] - return pd.DataFrame(out) - -def get_team_pit_data(apikey, competition, team_num): - client = pymongo.MongoClient(apikey) - db = client.data_scouting - mdata = db.pitdata - out = {} - return mdata.find_one({"competition" : competition, "team_scouted": team_num})["data"] - -def get_team_metrics_data(apikey, competition, team_num): - client = pymongo.MongoClient(apikey) - db = client.data_processing - mdata = db.team_metrics - return mdata.find_one({"competition" : competition, "team": team_num}) - -def unkeyify_2l(layered_dict): - out = {} - for i in layered_dict.keys(): - add = [] - sortkey = [] - for j in layered_dict[i].keys(): - add.append([j,layered_dict[i][j]]) - add.sort(key = lambda x: x[0]) - out[i] = list(map(lambda x: x[1], add)) - return out - -def get_match_data_formatted(apikey, competition): - client = pymongo.MongoClient(apikey) - db = client.data_scouting - mdata = db.teamlist - x=mdata.find_one({"competition":competition}) - out = {} - for i in x: - try: - out[int(i)] = unkeyify_2l(get_team_match_data(apikey, competition, int(i)).transpose().to_dict()) - except: - pass - return out - -def get_pit_data_formatted(apikey, competition): - client = pymongo.MongoClient(apikey) - db = client.data_scouting - mdata = db.teamlist - x=mdata.find_one({"competition":competition}) - out = {} - for i in x: - try: - out[int(i)] = get_team_pit_data(apikey, competition, int(i)) - except: - pass - return out - -def push_team_tests_data(apikey, competition, team_num, data, dbname = "data_processing", colname = "team_tests"): - client = pymongo.MongoClient(apikey) - db = client[dbname] - mdata = db[colname] - mdata.replace_one({"competition" : competition, "team": team_num}, {"_id": competition+str(team_num)+"am", "competition" : competition, "team" : team_num, "data" : data}, True) - -def push_team_metrics_data(apikey, competition, team_num, data, dbname = "data_processing", colname = "team_metrics"): - client = pymongo.MongoClient(apikey) - db = client[dbname] - mdata = db[colname] - mdata.replace_one({"competition" : competition, "team": team_num}, {"_id": competition+str(team_num)+"am", "competition" : competition, "team" : team_num, "metrics" : data}, True) - -def push_team_pit_data(apikey, competition, variable, data, dbname = "data_processing", colname = "team_pit"): - client = pymongo.MongoClient(apikey) - db = client[dbname] - mdata = db[colname] - mdata.replace_one({"competition" : competition, "variable": variable}, {"competition" : competition, "variable" : variable, "data" : data}, True) - -def get_analysis_flags(apikey, flag): - client = pymongo.MongoClient(apikey) - db = client.data_processing - mdata = db.flags - return mdata.find_one({flag:{"$exists":True}}) - -def set_analysis_flags(apikey, flag, data): - client = pymongo.MongoClient(apikey) - db = client.data_processing - mdata = db.flags - return mdata.replace_one({flag:{"$exists":True}}, data, True) \ No newline at end of file diff --git a/data analysis/get_team_rankings.py b/data analysis/get_team_rankings.py deleted file mode 100644 index cec2aa08..00000000 --- a/data analysis/get_team_rankings.py +++ /dev/null @@ -1,59 +0,0 @@ -import data as d -from analysis import analysis as an -import pymongo -import operator - -def load_config(file): - config_vector = {} - file = an.load_csv(file) - for line in file[1:]: - config_vector[line[0]] = line[1:] - - return (file[0][0], config_vector) - -def get_metrics_processed_formatted(apikey, competition): - client = pymongo.MongoClient(apikey) - db = client.data_scouting - mdata = db.teamlist - x=mdata.find_one({"competition":competition}) - out = {} - for i in x: - try: - out[int(i)] = d.get_team_metrics_data(apikey, competition, int(i)) - except: - pass - return out - -def main(): - - apikey = an.load_csv("keys.txt")[0][0] - tbakey = an.load_csv("keys.txt")[1][0] - - competition, config = load_config("config.csv") - - metrics = get_metrics_processed_formatted(apikey, competition) - - elo = {} - gl2 = {} - - for team in metrics: - - elo[team] = metrics[team]["metrics"]["elo"]["score"] - gl2[team] = metrics[team]["metrics"]["gl2"]["score"] - - elo = {k: v for k, v in sorted(elo.items(), key=lambda item: item[1])} - gl2 = {k: v for k, v in sorted(gl2.items(), key=lambda item: item[1])} - - for team in elo: - - print("teams sorted by elo:") - print("" + str(team) + " | " + str(elo[team])) - - print("*"*25) - - for team in gl2: - - print("teams sorted by glicko2:") - print("" + str(team) + " | " + str(gl2[team])) - -main() \ No newline at end of file diff --git a/data analysis/superscript.py b/data analysis/superscript.py deleted file mode 100644 index d57eab26..00000000 --- a/data analysis/superscript.py +++ /dev/null @@ -1,374 +0,0 @@ -# Titan Robotics Team 2022: Superscript Script -# Written by Arthur Lu & Jacob Levine -# Notes: -# setup: - -__version__ = "0.0.5.000" - -# changelog should be viewed using print(analysis.__changelog__) -__changelog__ = """changelog: - 0.0.5.000: - improved user interface - 0.0.4.002: - - removed unessasary code - 0.0.4.001: - - fixed bug where X range for regression was determined before sanitization - - better sanitized data - 0.0.4.000: - - fixed spelling issue in __changelog__ - - addressed nan bug in regression - - fixed errors on line 335 with metrics calling incorrect key "glicko2" - - fixed errors in metrics computing - 0.0.3.000: - - added analysis to pit data - 0.0.2.001: - - minor stability patches - - implemented db syncing for timestamps - - fixed bugs - 0.0.2.000: - - finalized testing and small fixes - 0.0.1.004: - - finished metrics implement, trueskill is bugged - 0.0.1.003: - - working - 0.0.1.002: - - started implement of metrics - 0.0.1.001: - - cleaned up imports - 0.0.1.000: - - tested working, can push to database - 0.0.0.009: - - tested working - - prints out stats for the time being, will push to database later - 0.0.0.008: - - added data import - - removed tba import - - finished main method - 0.0.0.007: - - added load_config - - optimized simpleloop for readibility - - added __all__ entries - - added simplestats engine - - pending testing - 0.0.0.006: - - fixes - 0.0.0.005: - - imported pickle - - created custom database object - 0.0.0.004: - - fixed simpleloop to actually return a vector - 0.0.0.003: - - added metricsloop which is unfinished - 0.0.0.002: - - added simpleloop which is untested until data is provided - 0.0.0.001: - - created script - - added analysis, numba, numpy imports -""" - -__author__ = ( - "Arthur Lu ", - "Jacob Levine ", -) - -__all__ = [ - "main", - "load_config", - "simpleloop", - "simplestats", - "metricsloop" -] - -# imports: - -from analysis import analysis as an -import data as d -import numpy as np -import matplotlib.pyplot as plt -from os import system, name -from pathlib import Path -import time -import warnings - -def main(): - warnings.filterwarnings("ignore") - while(True): - - current_time = time.time() - print("[OK] time: " + str(current_time)) - - start = time.time() - config = load_config(Path("config/stats.config")) - competition = an.load_csv(Path("config/competition.config"))[0][0] - print("[OK] configs loaded") - - apikey = an.load_csv(Path("config/keys.config"))[0][0] - tbakey = an.load_csv(Path("config/keys.config"))[1][0] - print("[OK] loaded keys") - - previous_time = d.get_analysis_flags(apikey, "latest_update") - - if(previous_time == None): - - d.set_analysis_flags(apikey, "latest_update", 0) - previous_time = 0 - - else: - - previous_time = previous_time["latest_update"] - - print("[OK] analysis backtimed to: " + str(previous_time)) - - print("[OK] loading data") - start = time.time() - data = d.get_match_data_formatted(apikey, competition) - pit_data = d.pit = d.get_pit_data_formatted(apikey, competition) - print("[OK] loaded data in " + str(time.time() - start) + " seconds") - - print("[OK] running tests") - start = time.time() - results = simpleloop(data, config) - print("[OK] finished tests in " + str(time.time() - start) + " seconds") - - print("[OK] running metrics") - start = time.time() - metricsloop(tbakey, apikey, competition, previous_time) - print("[OK] finished metrics in " + str(time.time() - start) + " seconds") - - print("[OK] running pit analysis") - start = time.time() - pit = pitloop(pit_data, config) - print("[OK] finished pit analysis in " + str(time.time() - start) + " seconds") - - d.set_analysis_flags(apikey, "latest_update", {"latest_update":current_time}) - - print("[OK] pushing to database") - start = time.time() - push_to_database(apikey, competition, results, pit) - print("[OK] pushed to database in " + str(time.time() - start) + " seconds") - - clear() - -def clear(): - - # for windows - if name == 'nt': - _ = system('cls') - - # for mac and linux(here, os.name is 'posix') - else: - _ = system('clear') - -def load_config(file): - config_vector = {} - file = an.load_csv(file) - for line in file: - config_vector[line[0]] = line[1:] - - return config_vector - -def simpleloop(data, tests): # expects 3D array with [Team][Variable][Match] - - return_vector = {} - for team in data: - variable_vector = {} - for variable in data[team]: - test_vector = {} - variable_data = data[team][variable] - if(variable in tests): - for test in tests[variable]: - test_vector[test] = simplestats(variable_data, test) - else: - pass - variable_vector[variable] = test_vector - return_vector[team] = variable_vector - - return return_vector - -def simplestats(data, test): - - data = np.array(data) - data = data[np.isfinite(data)] - ranges = list(range(len(data))) - - if(test == "basic_stats"): - return an.basic_stats(data) - - if(test == "historical_analysis"): - return an.histo_analysis([ranges, data]) - - if(test == "regression_linear"): - return an.regression(ranges, data, ['lin']) - - if(test == "regression_logarithmic"): - return an.regression(ranges, data, ['log']) - - if(test == "regression_exponential"): - return an.regression(ranges, data, ['exp']) - - if(test == "regression_polynomial"): - return an.regression(ranges, data, ['ply']) - - if(test == "regression_sigmoidal"): - return an.regression(ranges, data, ['sig']) - -def push_to_database(apikey, competition, results, pit): - - for team in results: - - d.push_team_tests_data(apikey, competition, team, results[team]) - - for variable in pit: - - d.push_team_pit_data(apikey, competition, variable, pit[variable]) - -def metricsloop(tbakey, apikey, competition, timestamp): # listener based metrics update - - elo_N = 400 - elo_K = 24 - - matches = d.pull_new_tba_matches(tbakey, competition, timestamp) - - red = {} - blu = {} - - for match in matches: - - red = load_metrics(apikey, competition, match, "red") - blu = load_metrics(apikey, competition, match, "blue") - - elo_red_total = 0 - elo_blu_total = 0 - - gl2_red_score_total = 0 - gl2_blu_score_total = 0 - - gl2_red_rd_total = 0 - gl2_blu_rd_total = 0 - - gl2_red_vol_total = 0 - gl2_blu_vol_total = 0 - - for team in red: - - elo_red_total += red[team]["elo"]["score"] - - gl2_red_score_total += red[team]["gl2"]["score"] - gl2_red_rd_total += red[team]["gl2"]["rd"] - gl2_red_vol_total += red[team]["gl2"]["vol"] - - for team in blu: - - elo_blu_total += blu[team]["elo"]["score"] - - gl2_blu_score_total += blu[team]["gl2"]["score"] - gl2_blu_rd_total += blu[team]["gl2"]["rd"] - gl2_blu_vol_total += blu[team]["gl2"]["vol"] - - red_elo = {"score": elo_red_total / len(red)} - blu_elo = {"score": elo_blu_total / len(blu)} - - red_gl2 = {"score": gl2_red_score_total / len(red), "rd": gl2_red_rd_total / len(red), "vol": gl2_red_vol_total / len(red)} - blu_gl2 = {"score": gl2_blu_score_total / len(blu), "rd": gl2_blu_rd_total / len(blu), "vol": gl2_blu_vol_total / len(blu)} - - - if(match["winner"] == "red"): - - observations = {"red": 1, "blu": 0} - - elif(match["winner"] == "blue"): - - observations = {"red": 0, "blu": 1} - - else: - - observations = {"red": 0.5, "blu": 0.5} - - red_elo_delta = an.elo(red_elo["score"], blu_elo["score"], observations["red"], elo_N, elo_K) - red_elo["score"] - blu_elo_delta = an.elo(blu_elo["score"], red_elo["score"], observations["blu"], elo_N, elo_K) - blu_elo["score"] - - new_red_gl2_score, new_red_gl2_rd, new_red_gl2_vol = an.glicko2(red_gl2["score"], red_gl2["rd"], red_gl2["vol"], [blu_gl2["score"]], [blu_gl2["rd"]], [observations["red"], observations["blu"]]) - new_blu_gl2_score, new_blu_gl2_rd, new_blu_gl2_vol = an.glicko2(blu_gl2["score"], blu_gl2["rd"], blu_gl2["vol"], [red_gl2["score"]], [red_gl2["rd"]], [observations["blu"], observations["red"]]) - - red_gl2_delta = {"score": new_red_gl2_score - red_gl2["score"], "rd": new_red_gl2_rd - red_gl2["rd"], "vol": new_red_gl2_vol - red_gl2["vol"]} - blu_gl2_delta = {"score": new_blu_gl2_score - blu_gl2["score"], "rd": new_blu_gl2_rd - blu_gl2["rd"], "vol": new_blu_gl2_vol - blu_gl2["vol"]} - - for team in red: - - red[team]["elo"]["score"] = red[team]["elo"]["score"] + red_elo_delta - - red[team]["gl2"]["score"] = red[team]["gl2"]["score"] + red_gl2_delta["score"] - red[team]["gl2"]["rd"] = red[team]["gl2"]["rd"] + red_gl2_delta["rd"] - red[team]["gl2"]["vol"] = red[team]["gl2"]["vol"] + red_gl2_delta["vol"] - - for team in blu: - - blu[team]["elo"]["score"] = blu[team]["elo"]["score"] + blu_elo_delta - - blu[team]["gl2"]["score"] = blu[team]["gl2"]["score"] + blu_gl2_delta["score"] - blu[team]["gl2"]["rd"] = blu[team]["gl2"]["rd"] + blu_gl2_delta["rd"] - blu[team]["gl2"]["vol"] = blu[team]["gl2"]["vol"] + blu_gl2_delta["vol"] - - temp_vector = {} - temp_vector.update(red) - temp_vector.update(blu) - - for team in temp_vector: - - d.push_team_metrics_data(apikey, competition, team, temp_vector[team]) - -def load_metrics(apikey, competition, match, group_name): - - group = {} - - for team in match[group_name]: - - db_data = d.get_team_metrics_data(apikey, competition, team) - - if d.get_team_metrics_data(apikey, competition, team) == None: - - elo = {"score": 1500} - gl2 = {"score": 1500, "rd": 250, "vol": 0.06} - ts = {"mu": 25, "sigma": 25/3} - - #d.push_team_metrics_data(apikey, competition, team, {"elo":elo, "gl2":gl2,"trueskill":ts}) - - group[team] = {"elo": elo, "gl2": gl2, "ts": ts} - - else: - - metrics = db_data["metrics"] - - elo = metrics["elo"] - gl2 = metrics["gl2"] - ts = metrics["ts"] - - group[team] = {"elo": elo, "gl2": gl2, "ts": ts} - - return group - -def pitloop(pit, tests): - - return_vector = {} - for team in pit: - for variable in pit[team]: - if(variable in tests): - if(not variable in return_vector): - return_vector[variable] = [] - return_vector[variable].append(pit[team][variable]) - - return return_vector - -main() - -""" -Metrics Defaults: - -elo starting score = 1500 -elo N = 400 -elo K = 24 - -gl2 starting score = 1500 -gl2 starting rd = 350 -gl2 starting vol = 0.06 -""" \ No newline at end of file diff --git a/data analysis/visualize_pit.py b/data analysis/visualize_pit.py deleted file mode 100644 index afd10e20..00000000 --- a/data analysis/visualize_pit.py +++ /dev/null @@ -1,59 +0,0 @@ -# To add a new cell, type '# %%' -# To add a new markdown cell, type '# %% [markdown]' -# %% -import matplotlib.pyplot as plt -import data as d -import pymongo - - -# %% -def get_pit_variable_data(apikey, competition): - client = pymongo.MongoClient(apikey) - db = client.data_processing - mdata = db.team_pit - out = {} - return mdata.find() - - -# %% -def get_pit_variable_formatted(apikey, competition): - temp = get_pit_variable_data(apikey, competition) - out = {} - for i in temp: - out[i["variable"]] = i["data"] - return out - - -# %% -pit = get_pit_variable_formatted("mongodb+srv://api-user-new:titanscout2022@2022-scouting-4vfuu.mongodb.net/test?authSource=admin&replicaSet=2022-scouting-shard-0&readPreference=primary&appname=MongoDB%20Compass&ssl=true", "2020ilch") - - -# %% -import matplotlib.pyplot as plt -import numpy as np - - -# %% -fig, ax = plt.subplots(1, len(pit), sharey=True, figsize=(80,15)) - -i = 0 - -for variable in pit: - - ax[i].hist(pit[variable]) - ax[i].invert_xaxis() - - ax[i].set_xlabel('') - ax[i].set_ylabel('Frequency') - ax[i].set_title(variable) - - plt.yticks(np.arange(len(pit[variable]))) - - i+=1 - -plt.show() - - -# %% - -