From 994e26c835baad1b49f12debdd7bb1203df918e9 Mon Sep 17 00:00:00 2001 From: mhjensen Date: Mon, 30 Dec 2019 15:13:28 +0100 Subject: [PATCH] added code to pca --- doc/pub/DimRed/html/._DimRed-bs000.html | 2 +- doc/pub/DimRed/html/._DimRed-bs018.html | 42 ++++++++- doc/pub/DimRed/html/DimRed-bs.html | 2 +- doc/pub/DimRed/html/DimRed-reveal.html | 44 ++++++++- doc/pub/DimRed/html/DimRed-solarized.html | 44 ++++++++- doc/pub/DimRed/html/DimRed.html | 44 ++++++++- doc/pub/DimRed/ipynb/DimRed.ipynb | 90 +++++++++++++++---- doc/pub/DimRed/ipynb/ipynb-DimRed-src.tar.gz | Bin 191 -> 191 bytes doc/pub/DimRed/pdf/DimRed-minted.pdf | Bin 262351 -> 262342 bytes doc/src/DimRed/DimRed.do.txt | 46 +++++++++- 10 files changed, 284 insertions(+), 30 deletions(-) diff --git a/doc/pub/DimRed/html/._DimRed-bs000.html b/doc/pub/DimRed/html/._DimRed-bs000.html index fcef4f476..db2c29bdc 100644 --- a/doc/pub/DimRed/html/._DimRed-bs000.html +++ b/doc/pub/DimRed/html/._DimRed-bs000.html @@ -224,7 +224,7 @@ MathJax.Hub.Config({
[2] Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University

-

Dec 29, 2019

+

Dec 30, 2019


diff --git a/doc/pub/DimRed/html/._DimRed-bs018.html b/doc/pub/DimRed/html/._DimRed-bs018.html index 9796a01be..2df856349 100644 --- a/doc/pub/DimRed/html/._DimRed-bs018.html +++ b/doc/pub/DimRed/html/._DimRed-bs018.html @@ -226,13 +226,18 @@ The following Python code aids in setting up the data

-

n = 1000
+
import numpy as np
+import pandas as pd
+from IPython.display import display
+n = 100
 mean = (-1, 2)
 cov = [[4, 2], [2, 2]]
 X = np.random.multivariate_normal(mean, cov, n)
+# Print the X-matrix
+print(X)
 

-Make thereafter a small Python code which plots the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \). +Make thereafter a small Python code which writes out the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \).

Now we are going to implement the PCA algorithm. We will break it down into various substeps. @@ -254,6 +259,20 @@ When you are done with these steps, print out \( \mu_n \) to verify it is close to \( \mu \) and plot your mean centered data to verify it is centered at the origin! Compare your code with the functionality from Scikit-Learn discussed above. +

+ + +

df = pd.DataFrame(X)
+# Pandas does the centering for us
+df = df -df.mean()
+display(df)
+
+# we center it ourselves
+X_centered = X - X.mean(axis=0)
+# test that we get the same as Pandas
+print(X_centered-df)
+
+

Compute the sample covariance

@@ -296,6 +315,25 @@ Finally, collect all these steps and write your own PCA function and compare this with the functionality included in Scikit-Learn. Have the input be the data and have the output be the principal components and their associated eigenvalues, sorted in descending order. Can you think of a way to make it more efficient than the algorithm outlined above? +

+ + +

#Now we do an SVD
+U, s, V = np.linalg.svd(X_centered)
+c1 = V.T[:, 0]
+c2 = V.T[:, 1]
+W2 = V.T[:, :2]
+X2D = X_centered.dot(W2)
+print(X2D)
+#thereafter we do a PCA with Scikit-learn
+from sklearn.decomposition import PCA
+pca = PCA(n_components = 2)
+X2Dsl = pca.fit_transform(X)
+print("Check that we get the same")
+print(X2D-X2Dsl)
+
+print(pca.components_.T[:, 0])
+

Finally, try out your own PCA function with other data sets. diff --git a/doc/pub/DimRed/html/DimRed-bs.html b/doc/pub/DimRed/html/DimRed-bs.html index fcef4f476..db2c29bdc 100644 --- a/doc/pub/DimRed/html/DimRed-bs.html +++ b/doc/pub/DimRed/html/DimRed-bs.html @@ -224,7 +224,7 @@ MathJax.Hub.Config({

[2] Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University

-

Dec 29, 2019

+

Dec 30, 2019


diff --git a/doc/pub/DimRed/html/DimRed-reveal.html b/doc/pub/DimRed/html/DimRed-reveal.html index d27980dab..7024e385a 100644 --- a/doc/pub/DimRed/html/DimRed-reveal.html +++ b/doc/pub/DimRed/html/DimRed-reveal.html @@ -148,7 +148,7 @@ MathJax.Hub.Config({

[2] Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University

 
-

Dec 29, 2019

+

Dec 30, 2019


@@ -1032,13 +1032,18 @@ The following Python code aids in setting up the data

-

n = 1000
+
import numpy as np
+import pandas as pd
+from IPython.display import display
+n = 100
 mean = (-1, 2)
 cov = [[4, 2], [2, 2]]
 X = np.random.multivariate_normal(mean, cov, n)
+# Print the X-matrix
+print(X)
 

-Make thereafter a small Python code which plots the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \). +Make thereafter a small Python code which writes out the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \).

Now we are going to implement the PCA algorithm. We will break it down into various substeps. @@ -1064,6 +1069,20 @@ When you are done with these steps, print out \( \mu_n \) to verify it is close to \( \mu \) and plot your mean centered data to verify it is centered at the origin! Compare your code with the functionality from Scikit-Learn discussed above. +

+ + +

df = pd.DataFrame(X)
+# Pandas does the centering for us
+df = df -df.mean()
+display(df)
+
+# we center it ourselves
+X_centered = X - X.mean(axis=0)
+# test that we get the same as Pandas
+print(X_centered-df)
+
+

Compute the sample covariance

@@ -1109,6 +1128,25 @@ Finally, collect all these steps and write your own PCA function and compare this with the functionality included in Scikit-Learn. Have the input be the data and have the output be the principal components and their associated eigenvalues, sorted in descending order. Can you think of a way to make it more efficient than the algorithm outlined above? +

+ + +

#Now we do an SVD
+U, s, V = np.linalg.svd(X_centered)
+c1 = V.T[:, 0]
+c2 = V.T[:, 1]
+W2 = V.T[:, :2]
+X2D = X_centered.dot(W2)
+print(X2D)
+#thereafter we do a PCA with Scikit-learn
+from sklearn.decomposition import PCA
+pca = PCA(n_components = 2)
+X2Dsl = pca.fit_transform(X)
+print("Check that we get the same")
+print(X2D-X2Dsl)
+
+print(pca.components_.T[:, 0])
+

Finally, try out your own PCA function with other data sets. diff --git a/doc/pub/DimRed/html/DimRed-solarized.html b/doc/pub/DimRed/html/DimRed-solarized.html index f34ee2991..0edd74866 100644 --- a/doc/pub/DimRed/html/DimRed-solarized.html +++ b/doc/pub/DimRed/html/DimRed-solarized.html @@ -179,7 +179,7 @@ MathJax.Hub.Config({

[2] Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University

-

Dec 29, 2019

+

Dec 30, 2019












@@ -1012,13 +1012,18 @@ The following Python code aids in setting up the data

-

n = 1000
+
import numpy as np
+import pandas as pd
+from IPython.display import display
+n = 100
 mean = (-1, 2)
 cov = [[4, 2], [2, 2]]
 X = np.random.multivariate_normal(mean, cov, n)
+# Print the X-matrix
+print(X)
 

-Make thereafter a small Python code which plots the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \). +Make thereafter a small Python code which writes out the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \).

Now we are going to implement the PCA algorithm. We will break it down into various substeps. @@ -1040,6 +1045,20 @@ When you are done with these steps, print out \( \mu_n \) to verify it is close to \( \mu \) and plot your mean centered data to verify it is centered at the origin! Compare your code with the functionality from Scikit-Learn discussed above. +

+ + +

df = pd.DataFrame(X)
+# Pandas does the centering for us
+df = df -df.mean()
+display(df)
+
+# we center it ourselves
+X_centered = X - X.mean(axis=0)
+# test that we get the same as Pandas
+print(X_centered-df)
+
+

Compute the sample covariance

@@ -1082,6 +1101,25 @@ Finally, collect all these steps and write your own PCA function and compare this with the functionality included in Scikit-Learn. Have the input be the data and have the output be the principal components and their associated eigenvalues, sorted in descending order. Can you think of a way to make it more efficient than the algorithm outlined above? +

+ + +

#Now we do an SVD
+U, s, V = np.linalg.svd(X_centered)
+c1 = V.T[:, 0]
+c2 = V.T[:, 1]
+W2 = V.T[:, :2]
+X2D = X_centered.dot(W2)
+print(X2D)
+#thereafter we do a PCA with Scikit-learn
+from sklearn.decomposition import PCA
+pca = PCA(n_components = 2)
+X2Dsl = pca.fit_transform(X)
+print("Check that we get the same")
+print(X2D-X2Dsl)
+
+print(pca.components_.T[:, 0])
+

Finally, try out your own PCA function with other data sets. diff --git a/doc/pub/DimRed/html/DimRed.html b/doc/pub/DimRed/html/DimRed.html index e5348ddef..c0500aa5f 100644 --- a/doc/pub/DimRed/html/DimRed.html +++ b/doc/pub/DimRed/html/DimRed.html @@ -184,7 +184,7 @@ MathJax.Hub.Config({

[2] Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University

-

Dec 29, 2019

+

Dec 30, 2019












@@ -1017,13 +1017,18 @@ The following Python code aids in setting up the data

-

n = 1000
+
import numpy as np
+import pandas as pd
+from IPython.display import display
+n = 100
 mean = (-1, 2)
 cov = [[4, 2], [2, 2]]
 X = np.random.multivariate_normal(mean, cov, n)
+# Print the X-matrix
+print(X)
 

-Make thereafter a small Python code which plots the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \). +Make thereafter a small Python code which writes out the data. Note that the function multivariate returns also the covariance discussed above and that it is defined by dividing by \( n-1 \) instead of \( n \).

Now we are going to implement the PCA algorithm. We will break it down into various substeps. @@ -1045,6 +1050,20 @@ When you are done with these steps, print out \( \mu_n \) to verify it is close to \( \mu \) and plot your mean centered data to verify it is centered at the origin! Compare your code with the functionality from Scikit-Learn discussed above. +

+ + +

df = pd.DataFrame(X)
+# Pandas does the centering for us
+df = df -df.mean()
+display(df)
+
+# we center it ourselves
+X_centered = X - X.mean(axis=0)
+# test that we get the same as Pandas
+print(X_centered-df)
+
+

Compute the sample covariance

@@ -1087,6 +1106,25 @@ Finally, collect all these steps and write your own PCA function and compare this with the functionality included in Scikit-Learn. Have the input be the data and have the output be the principal components and their associated eigenvalues, sorted in descending order. Can you think of a way to make it more efficient than the algorithm outlined above? +

+ + +

#Now we do an SVD
+U, s, V = np.linalg.svd(X_centered)
+c1 = V.T[:, 0]
+c2 = V.T[:, 1]
+W2 = V.T[:, :2]
+X2D = X_centered.dot(W2)
+print(X2D)
+#thereafter we do a PCA with Scikit-learn
+from sklearn.decomposition import PCA
+pca = PCA(n_components = 2)
+X2Dsl = pca.fit_transform(X)
+print("Check that we get the same")
+print(X2D-X2Dsl)
+
+print(pca.components_.T[:, 0])
+

Finally, try out your own PCA function with other data sets. diff --git a/doc/pub/DimRed/ipynb/DimRed.ipynb b/doc/pub/DimRed/ipynb/DimRed.ipynb index da58c850c..97b75d4b2 100644 --- a/doc/pub/DimRed/ipynb/DimRed.ipynb +++ b/doc/pub/DimRed/ipynb/DimRed.ipynb @@ -10,7 +10,7 @@ " \n", "**Morten Hjorth-Jensen**, Department of Physics, University of Oslo and Department of Physics and Astronomy and National Superconducting Cyclotron Laboratory, Michigan State University\n", "\n", - "Date: **Dec 29, 2019**\n", + "Date: **Dec 30, 2019**\n", "\n", "Copyright 1999-2019, Morten Hjorth-Jensen. Released under CC Attribution-NonCommercial 4.0 license\n", "\n", @@ -1104,17 +1104,22 @@ }, "outputs": [], "source": [ - "n = 1000\n", + "import numpy as np\n", + "import pandas as pd\n", + "from IPython.display import display\n", + "n = 100\n", "mean = (-1, 2)\n", "cov = [[4, 2], [2, 2]]\n", - "X = np.random.multivariate_normal(mean, cov, n)" + "X = np.random.multivariate_normal(mean, cov, n)\n", + "# Print the X-matrix\n", + "print(X)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Make thereafter a small Python code which plots the data. Note that the function **multivariate** returns also the covariance discussed above and that it is defined by dividing by $n-1$ instead of $n$.\n", + "Make thereafter a small Python code which writes out the data. Note that the function **multivariate** returns also the covariance discussed above and that it is defined by dividing by $n-1$ instead of $n$.\n", "\n", "Now we are going to implement the PCA algorithm. We will break it down into various substeps.\n", "\n", @@ -1154,9 +1159,32 @@ "source": [ "When you are done with these steps, print out $\\mu_n$ to verify it is\n", "close to $\\mu$ and plot your mean centered data to verify it is\n", - "centered at the origin! Compare your code with the functionality from **Scikit-Learn** discussed above.\n", - "\n", + "centered at the origin! Compare your code with the functionality from **Scikit-Learn** discussed above." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "df = pd.DataFrame(X)\n", + "# Pandas does the centering for us\n", + "df = df -df.mean()\n", + "display(df)\n", "\n", + "# we center it ourselves\n", + "X_centered = X - X.mean(axis=0)\n", + "# test that we get the same as Pandas\n", + "print(X_centered-df)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "### Compute the sample covariance\n", "\n", "Now we are going to use the mean centered data to compute the sample covariance of the data. Recall it is given by:" @@ -1213,11 +1241,43 @@ "\n", "Finally, collect all these steps and write your own PCA function and\n", "compare this with the functionality included in **Scikit-Learn**.\n", - "Have the input be the data and have the output be the principal components and their associated eigenvalues, sorted in descending order. Can you think of a way to make it more efficient than the algorithm outlined above?\n", + "Have the input be the data and have the output be the principal components and their associated eigenvalues, sorted in descending order. Can you think of a way to make it more efficient than the algorithm outlined above?" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#Now we do an SVD\n", + "U, s, V = np.linalg.svd(X_centered)\n", + "c1 = V.T[:, 0]\n", + "c2 = V.T[:, 1]\n", + "W2 = V.T[:, :2]\n", + "X2D = X_centered.dot(W2)\n", + "print(X2D)\n", + "#thereafter we do a PCA with Scikit-learn\n", + "from sklearn.decomposition import PCA\n", + "pca = PCA(n_components = 2)\n", + "X2Dsl = pca.fit_transform(X)\n", + "print(\"Check that we get the same\")\n", + "print(X2D-X2Dsl)\n", "\n", + "print(pca.components_.T[:, 0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Finally, try out your own PCA function with other data sets.\n", "\n", "\n", + "\n", + "\n", "## Classical PCA Theorem\n", "\n", "We assume now that we have a design matrix $\\boldsymbol{X}$ which has been\n", @@ -1502,7 +1562,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": { "collapsed": false }, @@ -1549,7 +1609,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": { "collapsed": false }, @@ -1573,7 +1633,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": { "collapsed": false }, @@ -1597,7 +1657,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": { "collapsed": false }, @@ -1621,7 +1681,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": { "collapsed": false }, @@ -1675,7 +1735,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": { "collapsed": false }, @@ -1698,7 +1758,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": { "collapsed": false }, @@ -1745,7 +1805,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": { "collapsed": false }, diff --git a/doc/pub/DimRed/ipynb/ipynb-DimRed-src.tar.gz b/doc/pub/DimRed/ipynb/ipynb-DimRed-src.tar.gz index 81a89e92bb1e89620a3981bb34796056b1869387..9652539851cd353f902cd2b775b030c1c2435581 100644 GIT binary patch literal 191 zcmV;w06_mAiwFSw1qxmO1MSaC3c@fD2H>uHia9}h*^@rlzEoUiSbg;2uXp^Y<$Bw&nY0GG>rqm z^;UZ6tz%|fr8x^_g?igJw$=5AIm;>V%s+8#q=nsKaE(@=v=f!q7bs*~3LeRBP!(`3 t?DPdj+x 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 p@d{;bWOHB_%~qMhXji|JeWl delta 5285 zcmV;W6k6-Xfe_Du5U?@=e@%^&q*-hdT}uE#wrW-9xmg1wsq1K4fQ8fiC>u!KtSaMq zZ;`0t5tC>cHmQk0WB6}9uRycof}Oqh)=sC)J7DeH0D|N?j~$-zi2tl_d`S9x%FJz| z(hNk=5crv7`D16FSnw0I69q7kz#nLnrK(JIl-6JgjH&^%5e&{Ae>N$j3;6-j1sMTd za8$$qBe%UBCZ6kPC%MM;k4RfyHt*ds!MQr#P0Vqgzm^?2@@VM#9^y!jLo*=v&NlPn z!8n9LDoOGne+u#!Rg_i<049rnhbO0KVU)a#_dJ??)3Mk%>c(kq#?~sZbC$)Q+{}D8 z_Rmc190ewNgtqTHf1!rvyC{ZHBFBPa_sp<=%-#+*9D}I~tDi1-7NQk(ICeo$p{P`b zD1$n*81tskannyIMWJe?S5ViQp{rQqjCn07s-} zsAcYs@gGQU@2vM$>JW-gg^3no9Kqq1>2~qcVMOo+D|bhg3ub2w0l*a5&8y_u3IH&`t1|$bH3mGPz^}_JQsh3EVy8E@WtCKDM>^8iiSucGy((4 zF6=>8L2BZZe@E~LJc15jjwlL12mHUKtpptjtBecQ^q^6T>7(NgLZG?rN%ad>;-v-v zdy?|7J>&-By~HYA3<2KG4f~YwP?S9^s@P4ql~>4~UUI~Z5GOCqm{qFOP{vK7icHM- ztM#aJNKfu?{%YS&HeGEF>~+#n0c4)A4*0Wr8~e`ZQ-{jl_n4`)bxV4h%eL-9dV z{NuQk#X$)z1Zm|L=y2DTd5n{?tv;I3|lumaJm7G>X@yDQ`mH2 zTZb;2caJl#bam_j0HhurR;>%0fp>G4M&sg?rfb)iM zvWh$FPQtZ!eJ%8LJIKJ&rIHih5_ha zKKTQH}9Desp~WF{Hm6kB2iT#CT2Tw`Ix1POEu@YMrVQ* zpSGA&7}N~A(u%>K$|8Rs1@g-;Z=&CMI~-TgYX$Roxt$)G)}C~5vXEV{-Z$Bq2^Pz& zf8qu2t_d&LH8GQr{csHQl>6aAB>*bw|}iL_krgYtZpO#J)QCNXK^#bTH4y zLMHKGq5I(?8{`o+IvKlQi?Buf6_5<6WulrWM#1Rmr}HV2!XqcIq`;0Z!+&sOp`Go) zo6?nFM0$J&x-pahL6*mES^y>gL*4xbe~ajuMmVAOU|!|_`*GK>#YBWfLXwUh)}>2)CjB;`P1&R>`5T&}Pt+16!QovmAQOaeY*w2Kr@;>7rCgnTBR{q6@(S zPkVdLK@Bkxqt=!a+0?y3cFo-?maIAC05A}J1$h$A5PV*c{^&X}I)K|J=Laf*r~5;= zo$I=Fp2O2ml4WSZy9auvk%GxUe|y;Q2g?BW4D|M?=<)lOo-vi7y*2MOp_cMsT2-tA zi_KudTY)cRH18LxWTa6Ze+9BVx{-83V%6!DXCQHOA)fJegf4KBE{rXPE!)E~3zNFa zh5H!cNQ$KZ%-vBFC(o+vPyw6%-gyYfLC!jOJY%{!RJT;z3K0tN>DbOyf41Vu@lQlL zgC}eTPgz13#+0vZ_r069^!EG#bYx-)15e-t4WgAv#IyTG9&b~C1IBX`-z_8Mm&`xy zyMr;zX=h-hGcclyIqnQhunYUf>1X)`ff{W3uAZW{ z1v&BHBsI4G{eE~+1P#f~zAhFB zHX7ZHetZp@E^Zcd@#j}`_WP%+S8sk4Y*COxakaSGEtt%SW_+PCMX1UbSKGzsg&}{2a3owzNEA_$2ZGO5>>sXP{qq&W99^)5 z$OYjV8Y)S6kuNs;SD(Mq#TGArT2LY|b#F=c3q}cNhF7!r?bY9%wST0X>^1V5TI*9{ z&q5HHi~r7^f!C8gVTc0i7a9gKN9i~zB2?z6u9D{BS}oQSs;`bJI$=(<(!r=?<_nPn ztvPh07_S*7&D={oh!IMIu~?Q9rL%<+iZC4I)gBCftPfk~Kcgd6^-XoSE9nyXGQ zeQSP{`_bX&otvC&ICWdDNs&eS-T6eoCsdL!Iy&SRN@+rLxPLYuY1lL8BAFza39G50 zzdqw2l2fAcnX8t;Qz!m`KhWOF0m>p)`+@+RV>6&VD55lg`15 z7dgl57@uwhnv+@2Z6#jfF-bo><1z`AIZ8n>7o@l-$p6fho@nzQW>H2N(USWtn!PNG zQc~n|@1m$U%71(2-@0wpt=7CKToyG0@1%vV$juQs%)k0VGiseVwZM8co;KEH?VECg zLcXmJH_aoKGB;Tt+;4l@8;RQfZmUgaQfAqwBO@y~5!6Qp*g9=&_j0u7YxlgZ!?~D(H8X~y$O6jA zvMKGUW0f-HdByeeIH`n5LokhmRm6Nka86H8BOw)u;Eb3Wpd$m(8FK~=xHMLbH{Mm{ zaO|uFp?^VN1CzSpy-nP1Ts`2#p&AV6>GoyQxS=})pjiW9Jsacr-XJXS*p@??c6GpR zsr}jBR$WEBo*-eUhZ%R+N|FKru@)%xnTLKKSA|@5_25oGXUq23`N2xC<^91u{QBXK zZUFqf#{9OrY$pa+#U%0>QlD^weO%?-s#0jcAb+cTp_a$Ka)Z*n>FfO+RBk+B^-%8b z8c*_t3fc98d+Xm&y{-4vp@$ty&)*u&l5N4ZD-D^|3NyRT#N6IJIMYs( zAau}x$TH{9C3i~f&0fc5sBvi89hwD{YI$h8z1tpCiMrkqf2vjY1ZPTM+mHe7ERn3LJ6#3HsH<=kn(Nlcz+3|FK2uD8XH%L08XFeBl+@{w`0s2PfLY>v*RS)2Z9+i~lVB$0w`o_y_Q$HdnI8dh!& zG2mC%hf@;0c#2ZufzD3;s ztN6x-6M5<|KBf2?1jC}v0L32Bre&PPu5SIsnlC;1SbF60*T>;DT%egEZ!(6bF6(XY zMztsCt6>-+_3DzDLN}}P&*e9hwCpn5BXm`{8!Y~m)PDj3Ob4KZ znNdcJH>Pw?#6%grt;ZY)g1g304841D!eJ27AkvRdxAv`RB!&~Im!8hEwNMC}ki~Y# z!^T#4=N>$D)LIY*^GECUE5SX6P%>bM^i@GVqIgYcreZ!_1>9)?BM@2H^rjSmvp)xB zp@a6o`~)4C3B1j*?=6rwD}S$DKJ{IhKMxink%wZ0sd|X0M6}fdZVx(T;@La=QM{!=*n4edqx&_Am%KIRC`d1{N@GY7@tF zos0EkwaWE)p%1V~<$pp@MHuz1icgfe?Kn)^V4Ds+8$Dc2vakv>#?EIN2afKCNj(V* zlUL` z!XhyA-f6*YNd)plJwFBh?7IqOL_s{R%vGPh^j*bA^yI5bq}IsEBtk7>X``z}}Q=CP~%<#&J(^S*-Tz~%)s%2@uy zdGhE(lD*?tPbgEk9~$SU+@D1G6dY8b*d<#@#j;ZkN{d5@65{`k{S)5;fK@gBx1_!7 z390Iwp@|pIwGO8Gjsgt^5u*0>GiTYg-9DksS|J1!vwtB_Jm(QcDfvRBWE@TLCr^sR zH6ePRzEqre&3A1!vtTBsd!4z%j6R`1pH5|*pz`FAd7`&xkg1Aq`d9(17QoC$|J|Me z)){1Z^nkJp0JJ^W5go6^m^td+zqVlEwHx1c^$Fgf5*~+(UprBSGad``z2-QpsX@8s z%p&d%^nWn_k*=$Ek0VlczaE}%8h^d-lx;6vvSgf4CeCSbRz9g10bGyQnY=r1#e8LF z8fm)lsQs>K{l&CDvd%sEdXAB4xI(ISc{*H4V}9s;AiNF+LzXMl(4@MME(*GeR>nHAXcyIW$E@HZ?gxK{hxvMm0t;K3xhgOl59obZ8(n zI5?M({sSw2yH8X>6vy%NyTbqrEXzw#Q4|#r5M3XO-~&WOeDF{KSMUwBu{W_ZF&2|p z(Mx4z!atzVN=vOMs7Q=0YGY%R?`b~y%{?>s-jg$CX8$s?I16)d8%(zR(MW;}axyFU zvj7!aq@Az>x?nGK!fx2bz6h021=SSVU_W&8tPyH|WVuCueJzE9q&mn$JrtmU>ORtT zXp*&A7#Ah3IGfcj-L#Lbq)Aw{JNFJ$%E{QRtIRl)QQv`r!zapeQSkdG#0^hZArTPQ!p~ zY0bBPoPt3Zg0nCJXJk+Neakr*hH*GAd$#O_QMdpX;S!9&WtfI5vKOy>^HsP8*I@!C zVT#^pFwVdY+3T{OxCytQ%oauw?~r^@^RU2@k;S`kkEKubK0ILAM0yC1;4v)15>-?9 zImy=&_cUd)l?`78*_St-Dx{LPkfOBm$5W+ev^wsoO8U0osao22=_#k|$!AYB%H9?| z)hhetmp|1h%+GqtD|~q8sb1l0qo;zx`lzP{h41e@alI4QBJ}|?Ps}_q^TdrvMPS^C raVN%|7kX;5J^zt!~LANC(bie8|Vodh8WHwq;sMNdWw_#_