From 792671c9183e3e471e01c2ce90dd9089389d1e40 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Sat, 19 Jun 2021 21:33:56 +0100 Subject: [PATCH 01/19] final CI --- .github/workflows/ci.yml | 14 ++++++-------- Cargo.toml | 2 +- pyproject.toml | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 060f98e..cfc1a89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,8 +79,12 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] exclude: - os: windows-latest - python-version: [3.6, 3.7, 3.8] - + python-version: 3.6 + - os: windows-latest + python-version: 3.7 + - os: windows-latest + python-version: 3.8 + steps: - uses: actions/checkout@v1 @@ -133,12 +137,6 @@ jobs: MATURIN_PASSWORD: ${{ secrets.PYPI }} run: poetry run maturin publish --username __token__ -i python${{ matrix.python-version }} - - name: PyPi publish nix - if: matrix.os != 'windows-latest' - env: - MATURIN_PASSWORD: ${{ secrets.PYPI }} - run: poetry run maturin publish --username __token__ -i python${{ matrix.python-version }} - #windows autodiscovers 4 python versions - name: PyPi publish windows if: matrix.os == 'windows-latest' diff --git a/Cargo.toml b/Cargo.toml index b391e34..3a6ff07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-dtw" -version = "0.1.11" +version = "0.1.12" authors = ["Christopher Fleetwood"] edition = "2018" description = "A rust implementation of dynamic time warping with python bindings!" diff --git a/pyproject.toml b/pyproject.toml index b8c4ccb..ceb3bc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ build-backend = "maturin" [tool.poetry] name = "rust-dtw" -version = "0.1.11" +version = "0.1.12" description = "A rust implementation of dynamic time warping with python bindings!" license = "MIT" readme = "README.md" From 77c5c9a518e8a00a6f7312b6e070f34b118c2cb8 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood <45471420+FL33TW00D@users.noreply.github.com> Date: Sat, 19 Jun 2021 21:50:04 +0100 Subject: [PATCH 02/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2566a2..584bc97 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@

-
RustDTW

Python extension backed by a multi-threaded Rust implementation of Dynamic Time Warping (DTW).

@@ -31,6 +30,7 @@ rust_dtw.dtw( ) >>> 5.0990195 ``` +For more examples please see `examples/` ## Developing From 0fa17253cdb60a58a50dc139ae6d2251dc448c0a Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Sat, 19 Jun 2021 23:02:38 +0100 Subject: [PATCH 03/19] speed comparison --- .gitignore | 1 + examples/speed_comparison.ipynb | 217 ++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 examples/speed_comparison.ipynb diff --git a/.gitignore b/.gitignore index ea8c4bf..30e2343 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/examples/nilearn_cache/* diff --git a/examples/speed_comparison.ipynb b/examples/speed_comparison.ipynb new file mode 100644 index 0000000..a7109fd --- /dev/null +++ b/examples/speed_comparison.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import dtaidistance as dtai\n", + "import rust_dtw\n", + "import numpy as np\n", + "%load_ext snakeviz" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/fleetwood/miniconda3/envs/dtw/lib/python3.6/site-packages/nilearn/datasets/__init__.py:90: FutureWarning: Fetchers from the nilearn.datasets module will be updated in version 0.9 to return python strings instead of bytes and Pandas dataframes instead of Numpy arrays.\n", + " \"Numpy arrays.\", FutureWarning)\n" + ] + } + ], + "source": [ + "from nilearn import datasets\n", + "\n", + "development_dataset = datasets.fetch_development_fmri()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/fleetwood/miniconda3/envs/dtw/lib/python3.6/site-packages/numpy/lib/npyio.py:2349: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", + " output = genfromtxt(fname, **kwargs)\n" + ] + } + ], + "source": [ + "from nilearn.input_data import NiftiMapsMasker\n", + "\n", + "msdl_data = datasets.fetch_atlas_msdl()\n", + "msdl_coords = msdl_data.region_coords\n", + "\n", + "masker = NiftiMapsMasker(\n", + " msdl_data.maps, resampling_target=\"data\", t_r=2, detrend=True,\n", + " low_pass=.1, high_pass=.01, memory='nilearn_cache', memory_level=1).fit()\n", + "masked_data = [masker.transform(func, confounds) for\n", + " (func, confounds) in zip(\n", + " development_dataset.func, development_dataset.confounds)]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "#normalizing to zero mean and unit std as per \n", + "normalized = []\n", + "for masked in masked_data:\n", + " normalized.append(StandardScaler().fit_transform(masked))\n", + "\n", + "normalized=np.array(normalized)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "#Now we need to accurately time how long it takes for each of them to compute the matricies\n", + "def compute_rust_dtw(timeseries):\n", + " #implement rust_dtw\n", + " connectomes = rust_dtw.dtw_connectomes(\n", + " connectomes=timeseries, \n", + " window=1200, \n", + " vectorize=False, \n", + " distance_mode=\"euclidean\"\n", + " )\n", + " return connectomes\n", + "\n", + "\n", + "def compute_dtai(timeseries):\n", + " #implement rust_dtw\n", + " connectomes = []\n", + " for ts in timeseries:\n", + " ds = dtai.dtw.distance_matrix_fast(ts.T, compact=False, use_mp=True)\n", + " connectomes.append(ds)\n", + " return connectomes" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "rust_dtw_result = compute_rust_dtw(normalized)\n", + "dtai_result = compute_dtai(normalized)\n", + "\n", + "np.testing.assert_almost_equal(rust_dtw_result, dtai_result)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "scrolled": false, + "tags": [ + "outputPrepend" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "*** Profile stats marshalled to file '/tmp/tmpn3r74e92'. \n", + "Embedding SnakeViz in this document...\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%snakeviz compute_rust_dtw(normalized)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " \n", + "*** Profile stats marshalled to file '/tmp/tmpu4wnvyz2'. \n", + "Embedding SnakeViz in this document...\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "%snakeviz compute_dtai(normalized)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see, we achieve a 5x speedup compared to DTAIDistance Python OpenMP implementation. If you can get the C version to compile (I certainly can't) please submit a PR." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:dtw]", + "language": "python", + "name": "conda-env-dtw-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From f7881d363457da73307be534b8c87bb9fff213b9 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Sat, 19 Jun 2021 23:11:25 +0100 Subject: [PATCH 04/19] time.png --- examples/time.png | Bin 0 -> 35469 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/time.png diff --git a/examples/time.png b/examples/time.png new file mode 100644 index 0000000000000000000000000000000000000000..fa0198db337bb7991d7bcfbf5b8f32e84950bf14 GIT binary patch literal 35469 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 Date: Sat, 19 Jun 2021 23:15:17 +0100 Subject: [PATCH 05/19] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 584bc97..ce43dee 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,10 @@ poetry run pytest ``` ## 📈 Performance -How well does the Rust implementation stack up against some other Python/C versions. +
+
+
+The above shows the performance of the rustdtw implementation vs the DTAIDistance OpenMP Python version (more benchmarks vs C implementation coming soon). ## ⚠️ License From cd8e8f0b75f64e3d0cfd7f4ab6588948eb1a82c7 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Sat, 19 Jun 2021 23:42:34 +0100 Subject: [PATCH 06/19] moving --- examples/{ => speed}/speed_comparison.ipynb | 0 examples/{ => speed}/time.png | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/{ => speed}/speed_comparison.ipynb (100%) rename examples/{ => speed}/time.png (100%) diff --git a/examples/speed_comparison.ipynb b/examples/speed/speed_comparison.ipynb similarity index 100% rename from examples/speed_comparison.ipynb rename to examples/speed/speed_comparison.ipynb diff --git a/examples/time.png b/examples/speed/time.png similarity index 100% rename from examples/time.png rename to examples/speed/time.png From c08ce5337637a382d89b4c30bb883f01ec649e82 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood <45471420+FL33TW00D@users.noreply.github.com> Date: Sun, 20 Jun 2021 15:29:46 +0100 Subject: [PATCH 07/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce43dee..471c1fd 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ poetry run pytest ## 📈 Performance
-
+
The above shows the performance of the rustdtw implementation vs the DTAIDistance OpenMP Python version (more benchmarks vs C implementation coming soon). From 738d068474a105f71cb37cbec0e8854fbcf71620 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Sat, 26 Jun 2021 13:50:48 +0100 Subject: [PATCH 08/19] DONE: 1. Changed from a stringly typed system to an enum 2. Added addtional test 3. DRY --- .gitignore | 1 + Cargo.lock | 2 +- build.sh | 2 +- poetry.lock | 2 +- src/lib.rs | 54 ++++++++++++------ .../test_dtw.cpython-36-pytest-6.2.4.pyc | Bin 3216 -> 2963 bytes tests/test_dtw.py | 24 ++++++-- 7 files changed, 58 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 30e2343..ad8670f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /examples/nilearn_cache/* +__pycache__/ diff --git a/Cargo.lock b/Cargo.lock index ec21772..fb6ca33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -801,7 +801,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rust-dtw" -version = "0.1.8" +version = "0.1.12" dependencies = [ "criterion", "indicatif", diff --git a/build.sh b/build.sh index 9351282..04281cf 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ #!/bin/bash poetry install poetry run maturin develop -poetry run pytest #-s \ No newline at end of file +poetry run pytest diff --git a/poetry.lock b/poetry.lock index fed02e5..c0f863c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -165,7 +165,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = ">=3.6" -content-hash = "28403ad63ef46258f70b803ce1d69233bd1191cd511ecb182571d8c777f9456e" +content-hash = "f840df1eb202d2ca178ec69be8d951f20242329600b6bab9f4d2e38769315c8a" [metadata.files] atomicwrites = [ diff --git a/src/lib.rs b/src/lib.rs index 2d45959..a13b9d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,35 @@ use indicatif::ParallelProgressIterator; use ndarray::parallel::prelude::*; use ndarray::prelude::*; +use pyo3::PyAny; +use pyo3::exceptions::PyValueError; use std::error::Error; use numpy::{ IntoPyArray, PyArray1, PyArrayDyn, PyReadonlyArray1, PyReadonlyArray2, PyReadonlyArray3, }; use pyo3::prelude::{pymodule, PyModule, PyResult, Python}; +use pyo3::conversion::FromPyObject; + //TODO: //1. Work out how to cargo doc to documentation -//2. Define less neuro specific method namings -//3. Finalize tests +//2. Define less neuro specific method wrappers + +pub enum DistanceMode { + Euclidean, + Manhattan, +} + +impl FromPyObject<'_> for DistanceMode { + fn extract(obj: &PyAny) -> PyResult { + match obj.extract().unwrap() { + "euclidean" => Ok(DistanceMode::Euclidean), + "manhattan" => Ok(DistanceMode::Manhattan), + _ => Err(PyValueError::new_err("Please provide a valid distance metric: [\"euclidean\", \"manhattan\"]")), + } + } +} #[pymodule] fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { @@ -20,7 +38,7 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { s: PyReadonlyArray1<'_, f64>, t: PyReadonlyArray1<'_, f64>, window: i32, - distance_mode: String, + distance_mode: DistanceMode, ) -> f64 { dtw( s.as_array().view(), @@ -46,7 +64,7 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { t: ArrayView1, window: &i32, distance_fn: fn(&f64, &f64) -> f64, - distance_mode: &String, + distance_mode: &DistanceMode, ) -> f64 { let m = s.len() + 1; let n = t.len() + 1; @@ -67,10 +85,10 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { ); } } - if distance_mode.eq("euclidean") { - f64::sqrt(dtw[[n - 1, m - 1]]) - } else { - dtw[[n - 1, m - 1]] + + match distance_mode { + DistanceMode::Euclidean => f64::sqrt(dtw[[n - 1, m - 1]]), + DistanceMode::Manhattan => dtw[[n - 1, m - 1]], } } @@ -79,7 +97,7 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { py: Python<'py>, connectome: PyReadonlyArray2<'_, f64>, window: i32, - distance_mode: String, + distance_mode: DistanceMode, ) -> PyResult<&'py PyArray1> { Ok(dtw_connectome( connectome.as_array().view(), @@ -103,7 +121,7 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { connectome: ArrayView2, window: &i32, distance_fn: fn(&f64, &f64) -> f64, - distance_mode: &String, + distance_mode: &DistanceMode, ) -> Vec { let mut result: Vec = vec![]; for i in 0..connectome.shape()[1] { @@ -126,7 +144,7 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { connectomes: PyReadonlyArray3<'_, f64>, window: i32, vectorize: bool, - distance_mode: String, + distance_mode: DistanceMode, ) -> &'py PyArrayDyn { dtw_connectomes( connectomes.as_array().to_owned(), @@ -143,9 +161,8 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { window: &i32, vectorize: bool, distance_fn: fn(&f64, &f64) -> f64, - distance_mode: &String, + distance_mode: &DistanceMode, ) -> ArrayD { - //Connectomes is a 3D array (n_subjects x n_samples x n_features) let (n_subjects, _n_samples, n_features) = connectomes.dim(); let vec_len = n_features * (n_features + 1) / 2; @@ -181,20 +198,19 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { pub fn vec_to_sym_mat(vec: &Vec, dim: usize) -> Array2 { let mut full: Array2 = Array2::zeros((dim, dim)); - for k in 0..vec.len() { + for (k, entry) in vec.iter().enumerate() { let (i_l, j_l) = ind2tril(k as f32); - full[[i_l as usize, j_l as usize]] = vec[k]; + full[[i_l as usize, j_l as usize]] = *entry; } //cloning is slow, will improve in future full += &full.clone().t(); full } - pub fn select_distance(mode: &str) -> Result f64, Box> { + pub fn select_distance(mode: &DistanceMode) -> Result f64, Box> { match mode { - "manhattan" => Ok(|a, b| f64::abs(a - b)), - "euclidean" => Ok(|a, b| (a - b) * (a - b)), - _ => Err("Please provide a valid distance metric.".into()), + DistanceMode::Manhattan => Ok(|a, b| f64::abs(a - b)), + DistanceMode::Euclidean => Ok(|a, b| (a - b) * (a - b)), } } diff --git a/tests/__pycache__/test_dtw.cpython-36-pytest-6.2.4.pyc b/tests/__pycache__/test_dtw.cpython-36-pytest-6.2.4.pyc index 59427002b52c7a32990e58f94fc6b6156655bce7..bbda0e0797ba2419b811dc1c48fe2992359c6ce4 100644 GIT binary patch delta 1376 zcmah}&5smC6tC*;>F(+2o*8CYc6ay?95&kWu_16kLUaua35HByA*g*B+o_hF$xQe9 zx>|x!HwKQNVEqBclX@^A;mC#mfrClMi+b|p-4pLs<1Pylx>LXRs=DgE-+QlK-(661 zz2iaP{l4$Fj~~?;`-?sE7^pAf$iKMPTih7eBTq2FCyl6~r62iP22mgkVcudFv<@20_-Xy8#_UF7%8erT(YjNRwVbX}HyoaYJ1LGoDzIo!9L%w|kW?8(6ufbgdcHn#) zP1>PpuBwOT+5h;NnxBLGT=lh;ZXiE!aVKDc8F&N@0y+%=Ldc;>Ix(!Ejau1}NiGLM zULQ>3bS;)LPIuz^Ca*5GMvgSE3#fc=A5ur`$Gt7bM5b(#;bE4h7&N9t zvr%t2Z?&JNqGD%$_5dEq$pW*w+~Pj>ILxVUodv$4es)ei_|rMWXJ+s-Q0aj3*AHFr z)c*Rew{`GDy$X32wa|oJC?fwFWJM|l5(>HYLKak3-#dF4_f@WI9o{j>IqXqC)^GE9 zb<{hyOsufXaVoNDX_3^~E?e6J%tJ%Fe9_4d-a_jnIk6C!)#h*z&3`k1y*Pz^1oH@2 zz(fo!c#T$cnEOeqVy#t$r98pw1Ul#*YA^NsQ==EYiyOB5F5+xkA?bGLLY`KCcyk-F zc9VZM&zZEx4mN^G{U+=?)>r%kCfFsm%05Ji7tiTGPrh`{_piQp;rz<~2SBw#U$DiW zZf6uc?Iwsvd6I9G-qmfKoT9Y R*bpwgh8y79aDCTtohKV=2)_UT literal 3216 zcmc&$O>Y}T7~Wm4?e%9qo3^3A@=;pTlC(`fXhpzPLnI`c9vX4TWxMf=9jEJEGqY~$ z$hlwyr;0m5RZmD<`2qX^B!0jMi36wHI3Nx=@w~J4CM^LjNO`R#}-<&`vP(zEP|0<1NNogDm~B_~-DygTMS1CN{Rs z7;k1K&ELullRLbF<>-#(Y5phnp23QH=D--353N0GV9BpzdppZ!23FH#vlMSyLz`9N z+;)CwJj_XkerI3|O%cK?kQZ9$C*Q=p)o-jhV=u!_u&F_&ZNj(XB6>>$dx-weOg3#B zd)6LWtYdDsUUczWkNR%RWqi@y@&#)|j5Ako*$ZN~5p}w~823Hd4qNWZ`t6Ta+A{XT z2G@_auv+~aIlp}{j?_nvGj=A&&TM`E3Z78~-fIMH#{IBnD^n^nR=J&a$f6xpVWXFL z9ehpekHu+^^V#}=wY+>VefE5}f2k(jFQ2wry5%S*ByzhGW>q zs!=wohIk!)&#|J6fM&%HaE`D-^8(zU)?{X@Fk(m&M`V0a4{?*_G+Q#yvE_RpJIf|C zTP9Ps6jHVf(4Vm73(b&(Ed}ipTZ$vLOtH$}u%$X;OBHOH-o5YwTVCYIW*-C!*}oA( zkXmgNI)1q2$DmTp7B(V+@+(M3M4k+x^f<6%HF#<|g-^Ot zdR>a3d2{}+2s(=qDS{|ZL{g2QH8_MIjTvZaf{IqAWev?egVc6ljC2(!!AM|ZKrKB{ z#J@zgm4gmiroqanXyw1xKVvL%soJHAmyzKVY(9N9S6{2;j+Bc^sH()x!>3iAG7gzmrxT@sWI@3^Kj=izFaBxI51^36 zxZUB<#Wt53N-x%JCav4y)Kp(dLS)$UrfrsSi3_+(OGRrT{OVGz!Wol&h|TyUh=&r5 zHbq1%9WjZ%D4H(ilgyrl5!mPk7Pc}fQ4S@_@BXOiwAu?Bq=#+{&R{d*5<8S1xXh1z zw<)5I8@2=Pi%{xNEO{(l>>a&;>j2#^Gefbc3o-f@l7kDLi`D4G+#aziYNRhkuS3Y+AIPm2~Hxc{sp4krFrJL?!GKU$| z^X{G*$t;v3>WK!Ii}VE${DrXF-*u1gZHcHCG8(bZkl7J##9QR2afEV`=XoX}zO?#d^EG=+zT|a`mZb59J!y(}(f+?#5|{A_f(~JVva~+Rl31dMhSXK^ zXd4>mMO>o@EhNFy4S4^QhgXivgO}!f8IK=kMT?e(sjh;c7a!3-mu~gu<-C`O$NMHi UlJMlJET@92 Date: Sat, 26 Jun 2021 14:08:08 +0100 Subject: [PATCH 09/19] Update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 471c1fd..5883027 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,19 @@
+## ❓ Why rustDTW? +**rustDTW** is a fast and user friendly implementation of Dynamic Time Warping. In constrast to most popularly used DTW libraries, it requires no C compilation (with frustrating build errors) with little performance loss. It can be installed on all operating systems and supports Python3.6 and above. -## ⚡️ Quick Installation - +## ⚡️ Quick Install To install rustDTW, simply: ```shell pip install rust-dtw ``` +## ❓ What is Dynamic Time Warping? +In time series analysis, dynamic time warping (DTW) is one of the algorithms for measuring similarity between two temporal sequences, which may vary in speed. [1](https://en.wikipedia.org/wiki/Dynamic_time_warping). This has applications in speech recognition, time series classification and neuroscience. + ## Example Usage rustDTW was designed for usage with timeseries data from functional brain regions. However any data represented as a numpy matrix can be provided. @@ -40,11 +44,8 @@ For more examples please see `examples/` - rust-numpy - Rayon - ### Setting up Dev - -Here's a brief intro about what a developer must do in order to start developing -the project further: +To get started with development, simply clone the repository and edit the main library code in `src/`. Once done, simply build and test the code with `./build.sh`. ```shell git clone https://github.com/FL33TW00D/rustDTW.git From 6c5d9d59ae7a7ab64ab6952af2355a773118183a Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood <45471420+FL33TW00D@users.noreply.github.com> Date: Sat, 26 Jun 2021 14:10:52 +0100 Subject: [PATCH 10/19] Update README.md --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5883027..ca1a54f 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,6 @@
-## ❓ Why rustDTW? -**rustDTW** is a fast and user friendly implementation of Dynamic Time Warping. In constrast to most popularly used DTW libraries, it requires no C compilation (with frustrating build errors) with little performance loss. It can be installed on all operating systems and supports Python3.6 and above. - ## ⚡️ Quick Install To install rustDTW, simply: @@ -16,8 +13,8 @@ To install rustDTW, simply: pip install rust-dtw ``` -## ❓ What is Dynamic Time Warping? -In time series analysis, dynamic time warping (DTW) is one of the algorithms for measuring similarity between two temporal sequences, which may vary in speed. [1](https://en.wikipedia.org/wiki/Dynamic_time_warping). This has applications in speech recognition, time series classification and neuroscience. +## ❓ What is Dynamic Time Warping +In time series analysis, dynamic time warping (DTW) is one of the algorithms for measuring similarity between two temporal sequences, which may vary in speed [1](https://en.wikipedia.org/wiki/Dynamic_time_warping). This has applications in speech recognition, time series classification and neuroscience. ## Example Usage From f3cdeb7b6a24ab99afcbfabd44287a1a1e3ea74d Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Sat, 26 Jun 2021 14:14:27 +0100 Subject: [PATCH 11/19] update version --- Cargo.lock | 2 +- Cargo.toml | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb6ca33..e59cbc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -801,7 +801,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rust-dtw" -version = "0.1.12" +version = "0.1.13" dependencies = [ "criterion", "indicatif", diff --git a/Cargo.toml b/Cargo.toml index 3a6ff07..0d1d717 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-dtw" -version = "0.1.12" +version = "0.1.13" authors = ["Christopher Fleetwood"] edition = "2018" description = "A rust implementation of dynamic time warping with python bindings!" diff --git a/pyproject.toml b/pyproject.toml index ceb3bc0..f67419e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ build-backend = "maturin" [tool.poetry] name = "rust-dtw" -version = "0.1.12" +version = "0.1.13" description = "A rust implementation of dynamic time warping with python bindings!" license = "MIT" readme = "README.md" From df2d8e965aee41f9f9f59ab8d46c3646d8baeaa4 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Thu, 1 Jul 2021 18:55:07 +0100 Subject: [PATCH 12/19] wrapping connectome method with standard matrix naming --- src/lib.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a13b9d6..f50daf9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,15 @@ use indicatif::ParallelProgressIterator; use ndarray::parallel::prelude::*; use ndarray::prelude::*; -use pyo3::PyAny; use pyo3::exceptions::PyValueError; +use pyo3::PyAny; use std::error::Error; use numpy::{ IntoPyArray, PyArray1, PyArrayDyn, PyReadonlyArray1, PyReadonlyArray2, PyReadonlyArray3, }; -use pyo3::prelude::{pymodule, PyModule, PyResult, Python}; use pyo3::conversion::FromPyObject; - +use pyo3::prelude::{pymodule, PyModule, PyResult, Python}; //TODO: //1. Work out how to cargo doc to documentation @@ -26,7 +25,9 @@ impl FromPyObject<'_> for DistanceMode { match obj.extract().unwrap() { "euclidean" => Ok(DistanceMode::Euclidean), "manhattan" => Ok(DistanceMode::Manhattan), - _ => Err(PyValueError::new_err("Please provide a valid distance metric: [\"euclidean\", \"manhattan\"]")), + _ => Err(PyValueError::new_err( + "Please provide a valid distance metric: [\"euclidean\", \"manhattan\"]", + )), } } } @@ -108,6 +109,22 @@ fn rust_dtw(_py: Python<'_>, m: &PyModule) -> PyResult<()> { .into_pyarray(py)) } + #[pyfn(m, "dtw_matrix")] + fn wrapped_dtw_connectome_py<'py>( + py: Python<'py>, + connectome: PyReadonlyArray2<'_, f64>, + window: i32, + distance_mode: DistanceMode, + ) -> PyResult<&'py PyArray1> { + Ok(dtw_connectome( + connectome.as_array().view(), + &window, + select_distance(&distance_mode).unwrap(), + &distance_mode, + ) + .into_pyarray(py)) + } + /// Dynamic time warping on a 2D matrix representing an fMRI timeseries /// /// # Arguments From ffec6a2e55cbcd2e4cf82cf6db4705a68c68acc6 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood <45471420+FL33TW00D@users.noreply.github.com> Date: Thu, 1 Jul 2021 19:14:05 +0100 Subject: [PATCH 13/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca1a54f..bb6a588 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ rust_dtw.dtw( ) >>> 5.0990195 ``` -For more examples please see `examples/` +For more examples please see `examples/` or explore the [wiki](https://github.com/FL33TW00D/rustDTW/wiki). ## Developing From 7f62e2e4fa668358db8fe46802e425ba2c4f8e5c Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Tue, 6 Jul 2021 21:54:09 +0100 Subject: [PATCH 14/19] Adding age classification example --- .gitignore | 3 +- benches/my_benchmark.rs | 22 -- .../age_classification_comparison.ipynb | 224 ++++++++++++++++++ 3 files changed, 226 insertions(+), 23 deletions(-) delete mode 100644 benches/my_benchmark.rs create mode 100644 examples/classification/age_classification_comparison.ipynb diff --git a/.gitignore b/.gitignore index ad8670f..c636a15 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target -/examples/nilearn_cache/* +nilearn_cache +nilearn_data __pycache__/ diff --git a/benches/my_benchmark.rs b/benches/my_benchmark.rs deleted file mode 100644 index 68c8a75..0000000 --- a/benches/my_benchmark.rs +++ /dev/null @@ -1,22 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use rusty_dtw::*; - -fn criterion_benchmark(c: &mut Criterion) { - let config = Config { - mode: String::from("euclidean"), - window: 100, - vectorize: true, - }; - - let mut connectomes: Vec>> = vec![]; - for _ in 0..100 { - connectomes.push(construct_random_connectome(10)); - } - let distance = select_distance(&config.mode).unwrap(); - c.bench_function("dtw_connectome_list", |b| { - b.iter(|| dtw_connectomes(connectomes.clone(), &config.window, distance)) - }); -} - -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); diff --git a/examples/classification/age_classification_comparison.ipynb b/examples/classification/age_classification_comparison.ipynb new file mode 100644 index 0000000..bee5306 --- /dev/null +++ b/examples/classification/age_classification_comparison.ipynb @@ -0,0 +1,224 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "marked-jones", + "metadata": {}, + "outputs": [], + "source": [ + "#Modified version of the following script from nilearn: \n", + "#https://nilearn.github.io/auto_examples/03_connectivity/plot_group_level_connectivity.html\n", + "from nilearn import datasets\n", + "from tqdm.notebook import tqdm\n", + "\n", + "development_dataset = datasets.fetch_development_fmri()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "mighty-mitchell", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/fleetwood/miniconda3/lib/python3.6/site-packages/numpy/lib/npyio.py:2349: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", + " output = genfromtxt(fname, **kwargs)\n" + ] + } + ], + "source": [ + "from nilearn import input_data\n", + "\n", + "msdl_data = datasets.fetch_atlas_msdl()\n", + "masker = input_data.NiftiMapsMasker(\n", + " msdl_data.maps, resampling_target=\"data\", t_r=2, detrend=True,\n", + " low_pass=.1, high_pass=.01, memory='nilearn_cache', memory_level=1).fit()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "weekly-balance", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "47f269cfc2b54b24b4207fa8bda2fb7c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data has 122 children.\n" + ] + } + ], + "source": [ + "children = []\n", + "pooled_subjects = []\n", + "groups = [] # child or adult\n", + "for func_file, confound_file, phenotypic in tqdm(zip(\n", + " development_dataset.func,\n", + " development_dataset.confounds,\n", + " development_dataset.phenotypic)):\n", + " time_series = masker.transform(func_file, confounds=confound_file)\n", + " pooled_subjects.append(time_series)\n", + " if phenotypic['Child_Adult'] == 'child':\n", + " children.append(time_series)\n", + " groups.append(phenotypic['Child_Adult'])\n", + "\n", + "print('Data has {0} children.'.format(len(children)))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "stainless-revelation", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PROCESSING: dtw\n", + "PROCESSING: correlation\n", + "PROCESSING: partial correlation\n", + "PROCESSING: tangent\n" + ] + } + ], + "source": [ + "from sklearn.svm import LinearSVC\n", + "from sklearn.model_selection import StratifiedShuffleSplit\n", + "from sklearn.metrics import accuracy_score\n", + "from nilearn.connectome import ConnectivityMeasure\n", + "import rust_dtw\n", + "import numpy as np\n", + "\n", + "kinds = ['dtw', 'correlation', 'partial correlation', 'tangent']\n", + "_, classes = np.unique(groups, return_inverse=True)\n", + "cv = StratifiedShuffleSplit(n_splits=15, random_state=0, test_size=5)\n", + "pooled_subjects = np.asarray(pooled_subjects)\n", + "\n", + "scores = {}\n", + "for kind in kinds:\n", + " print('PROCESSING: ', kind)\n", + " scores[kind] = []\n", + " for train, test in cv.split(pooled_subjects, classes):\n", + " # *ConnectivityMeasure* can output the estimated subjects coefficients\n", + " # as a 1D arrays through the parameter *vectorize*.\n", + " if kind == 'dtw':\n", + " connectomes = rust_dtw.dtw_connectomes(\n", + " connectomes=pooled_subjects[train], \n", + " window=100, \n", + " vectorize=True, \n", + " distance_mode=\"euclidean\"\n", + " )\n", + " test_connectomes = rust_dtw.dtw_connectomes(\n", + " connectomes=pooled_subjects[test], \n", + " window=100, \n", + " vectorize=True, \n", + " distance_mode=\"euclidean\"\n", + " )\n", + " else:\n", + " connectivity = ConnectivityMeasure(kind=kind, vectorize=True)\n", + " connectomes = connectivity.fit_transform(pooled_subjects[train])\n", + " test_connectomes = connectivity.transform(pooled_subjects[test])\n", + " \n", + " classifier = LinearSVC(max_iter=10000).fit(connectomes, classes[train])\n", + " # make predictions for the left-out test subjects\n", + " predictions = classifier.predict(test_connectomes)\n", + " \n", + " # store the accuracy for this cross-validation fold\n", + " scores[kind].append(accuracy_score(classes[test], predictions))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "indian-calibration", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(0.9466666666666668, 'dtw'), (0.9066666666666667, 'correlation'), (0.92, 'partial correlation'), (0.9600000000000001, 'tangent')]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "mean_scores = [np.mean(scores[kind]) for kind in kinds]\n", + "print(list(zip(mean_scores, kinds) ))\n", + "scores_std = [np.std(scores[kind]) for kind in kinds]\n", + "\n", + "plt.figure(figsize=(10, 8))\n", + "positions = np.arange(len(kinds)) * .1 + .1\n", + "plt.barh(positions, mean_scores, align='center', height=.05, xerr=scores_std)\n", + "yticks = [k.replace(' ', '\\n') for k in kinds]\n", + "plt.yticks(positions, yticks)\n", + "plt.gca().grid(True)\n", + "plt.gca().set_axisbelow(True)\n", + "plt.gca().axvline(.8, color='red', linestyle='--')\n", + "plt.xlabel('Classification accuracy\\n(red line = chance level)')\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "billion-charter", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 54da87dbfbdad010f3c01c13fdce359854005b15 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Wed, 7 Jul 2021 00:16:37 +0100 Subject: [PATCH 15/19] Adding ABIDE example --- .../classification/ABIDE_classification.ipynb | 502 ++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 examples/classification/ABIDE_classification.ipynb diff --git a/examples/classification/ABIDE_classification.ipynb b/examples/classification/ABIDE_classification.ipynb new file mode 100644 index 0000000..160f630 --- /dev/null +++ b/examples/classification/ABIDE_classification.ipynb @@ -0,0 +1,502 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "spare-number", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "#Modified version of the following script from nilearn: \n", + "#https://nilearn.github.io/auto_examples/03_connectivity/plot_group_level_connectivity.html\n", + "from nilearn import datasets\n", + "from tqdm.notebook import tqdm\n", + "\n", + "abide_dataset = datasets.fetch_abide_pcp(n_subjects=200)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "frank-glenn", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['description', 'phenotypic', 'func_preproc'])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abide_dataset.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "difficult-multiple", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/fleetwood/miniconda3/lib/python3.6/site-packages/numpy/lib/npyio.py:2349: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", + " output = genfromtxt(fname, **kwargs)\n" + ] + } + ], + "source": [ + "from nilearn import input_data\n", + "\n", + "msdl_data = datasets.fetch_atlas_msdl()\n", + "masker = input_data.NiftiMapsMasker(\n", + " msdl_data.maps, resampling_target=\"data\", t_r=2, detrend=True,\n", + " low_pass=.1, high_pass=.01, memory='nilearn_cache', memory_level=1).fit()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "cardiac-canadian", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ff312cd1ea494c188b7c4b2a694c82ab", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "0it [00:00, ?it/s]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset has 200 subjects\n" + ] + } + ], + "source": [ + "pooled_subjects = []\n", + "groups = []\n", + "for func_file, dx in tqdm(zip(abide_dataset['func_preproc'], abide_dataset['phenotypic']['DX_GROUP'])):\n", + " time_series = masker.transform(func_file)\n", + " pooled_subjects.append(time_series)\n", + " groups.append(dx)\n", + "\n", + "print(f'Dataset has {len(pooled_subjects)} subjects')" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "varied-federation", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(196, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(206, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(78, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(176, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(146, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n", + "(296, 39)\n" + ] + } + ], + "source": [ + "for elem in pooled_subjects:\n", + " print(elem.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "occupied-photographer", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "200\n" + ] + } + ], + "source": [ + "print(len(groups))" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "structured-defensive", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PROCESSING: dtw\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/fleetwood/miniconda3/lib/python3.6/site-packages/sklearn/svm/_base.py:983: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.\n", + " \"the number of iterations.\", ConvergenceWarning)\n", + "/home/fleetwood/miniconda3/lib/python3.6/site-packages/sklearn/svm/_base.py:983: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.\n", + " \"the number of iterations.\", ConvergenceWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "PROCESSING: correlation\n", + "PROCESSING: partial correlation\n", + "PROCESSING: tangent\n" + ] + } + ], + "source": [ + "from sklearn.svm import LinearSVC\n", + "from sklearn.model_selection import StratifiedShuffleSplit\n", + "from sklearn.metrics import accuracy_score\n", + "from nilearn.connectome import ConnectivityMeasure\n", + "import rust_dtw\n", + "import numpy as np\n", + "\n", + "kinds = ['dtw', 'correlation', 'partial correlation', 'tangent']\n", + "# kinds = ['correlation']\n", + "_, classes = np.unique(groups, return_inverse=True)\n", + "cv = StratifiedShuffleSplit(n_splits=15, random_state=0, test_size=5)\n", + "pooled_subjects = np.asarray(pooled_subjects)\n", + "\n", + "scores = {}\n", + "for kind in kinds:\n", + " print('PROCESSING: ', kind)\n", + " scores[kind] = []\n", + " for train, test in cv.split(pooled_subjects, classes):\n", + " if kind == 'dtw':\n", + "# Having to do it this way because there are different time series configurations in the provided\n", + "# data. Otherwise rust_dtw.dtw_connectomes would be easier\n", + " connectomes = []\n", + " for subj in pooled_subjects[train]:\n", + " connectomes.append(\n", + " rust_dtw.dtw_connectome(\n", + " connectome=subj,\n", + " window=100, \n", + " distance_mode=\"euclidean\")\n", + " )\n", + " test_connectomes = []\n", + " for subj in pooled_subjects[test]:\n", + " test_connectomes.append(\n", + " rust_dtw.dtw_connectome(\n", + " connectome=subj,\n", + " window=100, \n", + " distance_mode=\"euclidean\")\n", + " )\n", + " else:\n", + " connectivity = ConnectivityMeasure(kind=kind, vectorize=True)\n", + " connectomes = connectivity.fit_transform(pooled_subjects[train])\n", + " test_connectomes = connectivity.transform(pooled_subjects[test])\n", + " \n", + " classifier = LinearSVC(max_iter=10000).fit(connectomes, classes[train])\n", + " # make predictions for the left-out test subjects\n", + " predictions = classifier.predict(test_connectomes)\n", + " \n", + " # store the accuracy for this cross-validation fold\n", + " scores[kind].append(accuracy_score(classes[test], predictions))" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "optical-satisfaction", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(0.6933333333333332, 'dtw'), (0.6400000000000001, 'correlation'), (0.6133333333333334, 'partial correlation'), (0.6933333333333332, 'tangent')]\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "mean_scores = [np.mean(scores[kind]) for kind in kinds]\n", + "print(list(zip(mean_scores, kinds) ))\n", + "scores_std = [np.std(scores[kind]) for kind in kinds]\n", + "\n", + "plt.figure(figsize=(10, 8))\n", + "positions = np.arange(len(kinds)) * .1 + .1\n", + "plt.barh(positions, mean_scores, align='center', height=.05, xerr=scores_std)\n", + "yticks = [k.replace(' ', '\\n') for k in kinds]\n", + "plt.yticks(positions, yticks)\n", + "plt.gca().grid(True)\n", + "plt.gca().set_axisbelow(True)\n", + "plt.gca().axvline(.8, color='red', linestyle='--')\n", + "plt.xlabel('Classification accuracy\\n(red line = chance level)')\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "perfect-surveillance", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 4c844b213adf4aa63ebd7583ce8fb0f6604a0166 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Mon, 19 Jul 2021 20:33:03 +0100 Subject: [PATCH 16/19] modifying cargo.toml --- Cargo.lock | 415 ----------------------------------------------------- Cargo.toml | 6 - 2 files changed, 421 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e59cbc6..2b76384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.0.1" @@ -23,39 +12,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "bstr" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cast" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374" -dependencies = [ - "rustc_version", -] - [[package]] name = "cfg-if" version = "0.1.10" @@ -68,17 +24,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "bitflags", - "textwrap", - "unicode-width", -] - [[package]] name = "console" version = "0.14.1" @@ -92,42 +37,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "criterion" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" -dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools 0.10.0", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" -dependencies = [ - "cast", - "itertools 0.9.0", -] - [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -173,28 +82,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - [[package]] name = "ctor" version = "0.1.20" @@ -239,12 +126,6 @@ dependencies = [ "syn", ] -[[package]] -name = "half" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" - [[package]] name = "hermit-abi" version = "0.1.18" @@ -321,39 +202,6 @@ dependencies = [ "syn", ] -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "js-sys" -version = "0.3.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -381,15 +229,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "matrixmultiply" version = "0.2.4" @@ -408,12 +247,6 @@ dependencies = [ "rawpointer", ] -[[package]] -name = "memchr" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" - [[package]] name = "memoffset" version = "0.6.3" @@ -529,12 +362,6 @@ dependencies = [ "pyo3", ] -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - [[package]] name = "parking_lot" version = "0.11.1" @@ -579,43 +406,6 @@ dependencies = [ "proc-macro-hack", ] -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "plotters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" - -[[package]] -name = "plotters-svg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" -dependencies = [ - "plotters-backend", -] - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -784,15 +574,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-automata" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" -dependencies = [ - "byteorder", -] - [[package]] name = "regex-syntax" version = "0.6.25" @@ -803,7 +584,6 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" name = "rust-dtw" version = "0.1.13" dependencies = [ - "criterion", "indicatif", "ndarray 0.14.0", "ndarray-rand", @@ -813,92 +593,12 @@ dependencies = [ "rand", ] -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" - -[[package]] -name = "serde_cbor" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.126" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "smallvec" version = "1.6.1" @@ -926,37 +626,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - [[package]] name = "unicode-xid" version = "0.2.2" @@ -969,87 +638,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" -[[package]] -name = "wasm-bindgen" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" - -[[package]] -name = "web-sys" -version = "0.3.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -1066,15 +660,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 0d1d717..78d7910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,3 @@ indicatif = { version = "0.16.2", features = ["rayon"] } version = "0.13.2" features = ["extension-module"] -[dev-dependencies] -criterion = "0.3.4" - -[[bench]] -name = "my_benchmark" -harness = false From f6409b6d8eb93f741018dcd3f0a57cc3a63aac76 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Mon, 19 Jul 2021 21:56:54 +0100 Subject: [PATCH 17/19] Finalizing example --- .../classification/ABIDE_classification.ipynb | 346 ++++-------------- 1 file changed, 73 insertions(+), 273 deletions(-) diff --git a/examples/classification/ABIDE_classification.ipynb b/examples/classification/ABIDE_classification.ipynb index 160f630..cb0275f 100644 --- a/examples/classification/ABIDE_classification.ipynb +++ b/examples/classification/ABIDE_classification.ipynb @@ -2,12 +2,21 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": 38, "id": "spare-number", "metadata": { "scrolled": false }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/fleetwood/miniconda3/lib/python3.6/site-packages/numpy/lib/npyio.py:2349: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", + " output = genfromtxt(fname, **kwargs)\n" + ] + } + ], "source": [ "#Modified version of the following script from nilearn: \n", "#https://nilearn.github.io/auto_examples/03_connectivity/plot_group_level_connectivity.html\n", @@ -19,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 39, "id": "frank-glenn", "metadata": { "scrolled": false @@ -31,7 +40,7 @@ "dict_keys(['description', 'phenotypic', 'func_preproc'])" ] }, - "execution_count": 11, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -42,19 +51,10 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 40, "id": "difficult-multiple", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/fleetwood/miniconda3/lib/python3.6/site-packages/numpy/lib/npyio.py:2349: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.\n", - " output = genfromtxt(fname, **kwargs)\n" - ] - } - ], + "outputs": [], "source": [ "from nilearn import input_data\n", "\n", @@ -66,14 +66,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 41, "id": "cardiac-canadian", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ff312cd1ea494c188b7c4b2a694c82ab", + "model_id": "a60c254988fb400fa8c08d47ff36a3ca", "version_major": 2, "version_minor": 0 }, @@ -105,243 +105,57 @@ }, { "cell_type": "code", - "execution_count": 38, - "id": "varied-federation", + "execution_count": 42, + "id": "substantial-meditation", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(196, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(206, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(78, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(176, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(146, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n", - "(296, 39)\n" - ] - } - ], + "outputs": [], "source": [ - "for elem in pooled_subjects:\n", - " print(elem.shape)" + "n_regions = pooled_subjects[0].shape[1]" ] }, { "cell_type": "code", "execution_count": 43, - "id": "occupied-photographer", + "id": "cardiovascular-equivalent", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "200\n" - ] - } - ], + "outputs": [], "source": [ - "print(len(groups))" + "def sym_matrix_to_vec(symmetric):\n", + " tril_mask = np.tril(np.ones(symmetric.shape[-2:]), k=-1).astype(np.bool)\n", + " return symmetric[..., tril_mask]" ] }, { "cell_type": "code", "execution_count": 44, + "id": "natural-editing", + "metadata": {}, + "outputs": [], + "source": [ + "def compute_dtw(subjects, n_regions):\n", + " dtw_output = []\n", + " for subj in subjects:\n", + " dtw_output.append(\n", + " rust_dtw.dtw_connectome(\n", + " connectome=subj,\n", + " window=100, \n", + " distance_mode=\"euclidean\")\n", + " )\n", + " connectomes = []\n", + " #Post processing them as per paper recommendations\n", + " for vec in dtw_output:\n", + " sym = np.zeros((n_regions, n_regions))\n", + " sym[i_lower] = vec\n", + " sym += sym.T\n", + " sym *= -1\n", + " StandardScaler().fit_transform(sym)\n", + " connectomes.append(sym_matrix_to_vec(sym))\n", + " return connectomes" + ] + }, + { + "cell_type": "code", + "execution_count": 45, "id": "structured-defensive", "metadata": { "scrolled": false @@ -358,10 +172,8 @@ "name": "stderr", "output_type": "stream", "text": [ - "/home/fleetwood/miniconda3/lib/python3.6/site-packages/sklearn/svm/_base.py:983: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.\n", - " \"the number of iterations.\", ConvergenceWarning)\n", - "/home/fleetwood/miniconda3/lib/python3.6/site-packages/sklearn/svm/_base.py:983: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.\n", - " \"the number of iterations.\", ConvergenceWarning)\n" + "/home/fleetwood/miniconda3/lib/python3.6/site-packages/numpy/core/_asarray.py:83: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray\n", + " return array(a, dtype, copy=False, order=order)\n" ] }, { @@ -377,10 +189,13 @@ "source": [ "from sklearn.svm import LinearSVC\n", "from sklearn.model_selection import StratifiedShuffleSplit\n", + "from sklearn.preprocessing import StandardScaler\n", "from sklearn.metrics import accuracy_score\n", "from nilearn.connectome import ConnectivityMeasure\n", + "import matplotlib.pyplot as plt\n", "import rust_dtw\n", "import numpy as np\n", + "import copy\n", "\n", "kinds = ['dtw', 'correlation', 'partial correlation', 'tangent']\n", "# kinds = ['correlation']\n", @@ -393,25 +208,9 @@ " print('PROCESSING: ', kind)\n", " scores[kind] = []\n", " for train, test in cv.split(pooled_subjects, classes):\n", - " if kind == 'dtw':\n", - "# Having to do it this way because there are different time series configurations in the provided\n", - "# data. Otherwise rust_dtw.dtw_connectomes would be easier\n", - " connectomes = []\n", - " for subj in pooled_subjects[train]:\n", - " connectomes.append(\n", - " rust_dtw.dtw_connectome(\n", - " connectome=subj,\n", - " window=100, \n", - " distance_mode=\"euclidean\")\n", - " )\n", - " test_connectomes = []\n", - " for subj in pooled_subjects[test]:\n", - " test_connectomes.append(\n", - " rust_dtw.dtw_connectome(\n", - " connectome=subj,\n", - " window=100, \n", - " distance_mode=\"euclidean\")\n", - " )\n", + " if kind == 'dtw': \n", + " connectomes = compute_dtw(pooled_subjects[train], n_regions)\n", + " test_connectomes = compute_dtw(pooled_subjects[test], n_regions)\n", " else:\n", " connectivity = ConnectivityMeasure(kind=kind, vectorize=True)\n", " connectomes = connectivity.fit_transform(pooled_subjects[train])\n", @@ -427,7 +226,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 52, "id": "optical-satisfaction", "metadata": {}, "outputs": [ @@ -440,33 +239,34 @@ }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsgAAAI4CAYAAAB3OR9vAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAAsTAAALEwEAmpwYAAAvWUlEQVR4nO3de5QnZX0n/veHi4MIggsqKCwYuURFnBBIFty47WX3qGhgI0GjqLiuGM26IZHsT7Oa48YbiZplowFFNouiriC6LmG9RMUWw2UCg8NllCEYEAgiF21Qucjl+f3RNbFshpmm6O9UT/N6nTNn+lvf6qr3t6kzvOeZp56q1loAAIBZm40dAAAAFhMFGQAAehRkAADoUZABAKBHQQYAgJ4txg6w2Gy//fZtjz32GDsGi8BPf/rTPOpRjxo7BouE64E+1wNJkjVrcu+992bzpz517CQMtHLlyptba4+du11BnuPxj398LrzwwrFjsAhMT09nampq7BgsEq4H+lwPJEmmpjIzM5Pt9YZNVlV9b13bTbEAAIAeI8gAAEN85CNZs2JFfn3sHCw4BRkAYIi9984d3//+2CmYAFMsAACG+Ju/yQ7nnjt2CiZAQQYAGOIDH8iup502dgomQEEGAIAeBRkAAHoUZAAA6FGQAQCgxzJvAABDnHJKvnPeeTlw7BwsOCPIAABD7Lpr7nrc48ZOwQQoyAAAQ5x6ah571lljp2ACFGQAgCFOOCFPPOOMsVMwAQoyAAD0KMgAANCjIAMAQI+CDAAAPdZBBgAY4vTTs/qcc/LMsXOw4IwgAwAMseOOuXu77cZOwQQoyAAAQ5x8cnb60pfGTsEEKMgAAEMoyEuWggwAAD3VWhs7w6KybOc9286vPm7sGACwqFx97MFjR1h8pqYyMzOT7VetGjsJA1XVytba/nO3G0EGAIAeBRkAAHqsgwwAMMQXvpBLzj47zxo7BwvOCDIAwBBbb537ttpq7BRMgIIMADDE8cfnCZ///NgpmABTLAAAhjjttDxuZmbsFEyAEWQAAOhRkAEAoEdBBgCAHgUZAAB63KQHADDE9HRWTU9nauwcLDgjyAAA0KMgAwAM8f73Z9dTTx07BRNgigUAwBBnnpkdrIO8JBlBBgCAHgUZAAB6FGQAAOhRkAEAhnjkI3PvsmVjp2AC3KQHADDEF7+YS62DvCQZQQYAgB4FGQBgiHe+M7t9/ONjp2ACTLEAABjia1/LY6yDvCQZQQYAgB4FGQAAehRkAADoMQcZAGCIHXbI3ffdN3YKJkBBBgAY4rOfzWrrIC9JplgAAECPEWQAgCHe+tY86ZprkqmpsZOwwCY2glxV21fVGyd1/Aejqo6uqq3HzgEALCHnnZftVq8eOwUTMMkpFtsnWRQFOcnRSRRkAAA2aJJTLI5N8uSqWpXk60n2TfKYJFsmeVtr7f9W1e5Jvpjk75IclOSfkhzSWrujqg5I8j+T3JfkK0le0Frbp6o27449lWRZkr9qrX2kqqaSvCPJzUn2SbIyyRFJ3pTkCUm+XlU3t9aePcHPDMCDdMOn3jJ2BOZh6vz3jR1h0Tlu1arcc889OWYRTbGYnp4eO8KSMMmC/JYk+7TWllfVFkm2bq3dVlU7Jjm/qs7o9tszye+01l5XVacleUmSTyT5X0le11o7r6qO7R33tUluba0dUFXLkpxTVX/bvfcrSZ6W5Pok5yR5ZmvtL6vqD5M8u7V287qCVtVRSY5KkkfstMcC/ggAYGmY8Ujl+7nnnnvSWltUPxsFeWFsrJv0Ksl7qupZmR0RfmKSx3fvXdVaW9V9vTLJ7lW1fZJtW2vndds/leRF3df/Lsm+VXVY93q7zJbsnyX5+9badUnSjVzvntnR6fVqrZ2Y5MQkWbbznm3QJwRgkJ1efuyGd2J0q449eOwIi88RR+QHP/hBVn3lK2MnYYFtrIL8iiSPTfKrrbW7q+rqJFt1793V2+/eJI/cwLEqyZtaa1/+hY2zUyzmHssqHQDAZHziE/nO9PQ/j/ixdEzyJr0fJ9m2+3q7JDd25fjZSXZb3ze21maS/Liqfr3b9LLe219O8oaq2jJJqmqvqnrUg8gCAAAPaGIjrK21W6rqnKq6LMkFSX65qi5NcmGSy+dxiNcm+WhV3ZfkG0lu7baflNmpExdVVSW5KcmhGzjWiUm+VFXXu0kPAFgQRx+dPa67zjrIS1C1tjin3FbVNq21n3RfvyXJzq2135/0eZftvGfb+dXHTfo0ALBJudoc5PubmsrMzEy2X7Vq7CQMVFUrW2v7z92+mOfoHlxVb81sxu8lOXLcOAAAPBws2oLcWjs1yalj5wAA4OFlkjfpAQDAJkdBBgAYYq+9cvsuu4ydgglYtFMsAAAWtRNPzBXT03nC2DlYcEaQAQCgxwgyAMAQRx2Vva6/3jrIS5CCDAAwxBVXZOuZmbFTMAGmWAAAQI+CDAAAPQoyAAD0KMgAAEMsX56f7LHH2CmYADfpAQAMcdxxuXJ6Oh4VsvQYQQYAgB4jyAAAQxxxRJ7ygx9YB3kJUpABAIa47rossw7ykmSKBQAA9CjIAADQoyADAECPOcgAAEMceGBuveaabD92DhacggwAMMR735urpqez29g5WHCmWAAAQI8RZACAIV7ykjztppuSs88eOwkLzAgyAMAQt9ySLW+7bewUTICCDAAAPQoyAAD0KMgAANDjJr05dn/0Zllz7MFjx2ARmJ6eztTU1NgxWCRcD/S5HkiSPPe5+dFVV1kHeQlSkAEAhnj72/O96ek8aewcLDhTLAAAoMcIMgDAEC94QZ7+wx8mK1aMnYQFZgQZAGCIO+7I5nfdNXYKJkBBBgCAHgUZAAB6FGQAAOhxkx4AwBAvelFu+e53rYO8BCnIAABDHHNMrp2ezpPHzsGCM8UCAAB6jCADAAwxNZXlMzPJqlVjJ2GBGUEGAIAeBRkAAHoUZAAA6FGQAQCgx016AABDHH54brziCusgL0FGkAEAhnjjG3P9oYeOnYIJUJABAIa4/fZsduedY6dgAkyxAAAY4oUvzL4zM8nznz92EhaYEWQAAOhRkAEAoEdBBgCAHgUZAAB63KQHADDEkUfmhssvtw7yEmQEGQBgiCOPzA1WsFiSFGQAgCFuvjlb3nrr2CmYAFMsAACGOOywPG1mJjnkkLGTsMCMIAMAQI+CDAAAPQoyAAD0KMgAANDjJj0AgCHe8Ib80+rV1kFeghRkAIAhXvrS3DQ9PXYKJsAUCwCAIa69NstuvHHsFEyAEWQAgCFe+co8ZWYmOfzwsZOwwIwgAwBAj4IMAAA9CjIAAPQoyAAA0OMmPQCAId785lx76aXWQV6CFGQAgCFe/OLcsu22Y6dgAkyxAAAYYs2aPPKaa8ZOwQQYQQYAGOL1r8/eMzPJq141dhIWmBFkAADoUZABAKBHQQYAgB4FGQAAetykBwAwxNvelu9dfLF1kJcgBRkAYIjnPS8/2kKVWopMsQAAGGLVqmxz5ZVjp2ACFGQAgCGOPjp7fOhDY6dgAhRkAADoUZABAKBHQQYAgB4FGQAAeqxNAgAwxHvek3+86KLsN3YOFpyCDAAwxEEH5baf/WzsFEyAKRYAAEOce24efdllY6dgAhRkAIAh/viP80snnTR2CiZAQQYAgB4FGQAAehRkAADoUZABAKDHMm8AAEMcd1yuvPDC7D92DhacggwAMMTy5fnJzMzYKZgAUywAAIb46lfzmJUrx07BBFRrbewMi8qynfdsO7/6uLFjALCJu/rYg8eOwKRNTWVmZibbr1o1dhIGqqqVrbX7zZIxggwAAD0KMgAA9CjIAADQoyADAECPZd4AAIb4yEeyZsWK/PrYOVhwCjIAwBB77507vv/9sVMwAaZYAAAM8Td/kx3OPXfsFEyAggwAMMQHPpBdTztt7BRMgIIMAAA9CjIAAPQoyAAA0KMgAwBAj2XeAACGOOWUfOe883Lg2DlYcEaQAQCG2HXX3PW4x42dgglQkAEAhjj11Dz2rLPGTsEEKMgAAEOccEKeeMYZY6dgAhRkAADoUZABAKBHQQYAgB4FGQAAeqyDDAAwxOmnZ/U55+SZY+dgwRlBBgAYYscdc/d2242dgglQkAEAhjj55Oz0pS+NnYIJUJABAIZQkJesTbIgV9WhVfXU3us/rarnbeB7Tq6qwyafDgCATdkmV5Craoskhyb554LcWvuT1tpXRwsFAMCSMUpBrqrdq+ryqvpkVX2nqk6vqq2r6k+q6oKquqyqTqyq6vafrqrjqurCJP9fkt9M8r6qWlVVT+6PDj/QMQAAYD7GHEHeO8nxrbWnJLktyRuTfKi1dkBrbZ8kj0zyot7+j2it7d9ae3eSM5L8UWtteWvtu3OOu75jAADAeo1ZkK9trZ3Tff2JJP86ybOrakVVXZrkOUme1tv/1Hked33HAABYGF/4Qi459tixUzABYz4opK3j9fFJ9m+tXVtV70iyVe/9n27ogFW11QaOAQCwMLbeOvdtpWYsRWOOIP/Lqjqw+/rlSf6u+/rmqtomyfpWnPhxkm3XsX3tVTqfYwAADHf88XnC5z8/dgomYMwR5DVJfq+q/jrJt5OckOQxSS5LckOSC9bzvZ9O8tGq+s/pleDW2kxVfXSexwB4WLrhU28ZO8LDwtT57xs7wpI3PT09boDTTsvjZmbGzcBEjFmQ72mtHTFn29u6X7+gtTY15/U56S3zluTI3nsPdIwj525bq6qOSnJUkjxipz02GBwANmRGcZq4sQvy8pmZ3HvvvaPnYOGNWZAXjdbaiUlOTJJlO+85d240wJKy08vdVLQxrDr24LEjMGnbb5+ZmZlMTU2NnYQFNkpBbq1dnWSfMc4NAADrsyiepFdV76iqYzawz4N+vDQAADxYExtBrqotWmv3PNDrAQ5NcmZmb+hLa+1PHlpCAICHYHo6q6anMzV2DhbcvEaQq+pVVXVJVV1cVad0j4o+q9v2tar6l91+J1fVh6tqRZI/X8frJ1fVl6pqZVV9s6p+eR3nel33qOiLq+qz3SOoD8r6Hy/93Kr6VlVdWlV/XVXLuu1XV9V/q6qLuvfudz4AAOjbYEGuqqdldlWI57TWnpHk95N8MMnHWmv7Jvlkkr/sfcsuSQ5qrf3hOl6fmORNrbVfTXJMZh/qMdfnukdFPyPJd5K8trV2bh7g8dLdw0FOTvLS1trTMzsq/obe8W5ure2X2WXk1juNAwBg3t7//ux66nwf9MumZD5TLJ6T5DOttZuTpLX2w+4BH7/VvX9Kkj/v7f+Z1tq9c193D+44KMlnqmrte8vWcb59qupdSbZPsk2SL28g395JrmqtXdG9/liS30tyXPf6c93vK3uZAQAemjPPzA6W81uSJjEHee4jode+3izJTGtt+Qa+/+Qkh7bWLq6qI5OHPLXnru73e2NZOwAANmA+c5DPSvLbVbVDklTVv0hybpKXde+/Isk3N3SQ1tptSa6qqt/ujlNV9Yx17Lptku9X1Zbdsdd6oMdLr0mye1WtfcLHK5N8Y4OfCgAA1mGDBbm1tjrJu5N8o6ouTvIXSd6U5DVVdUlmC+nvz/N8r0jy2u44q5Mcso593p5kRZJzklze2/7pJH/U3Yz35F6+O5O8JrNTNy5Ncl+SD88zDwAA/IJqzYPj+pbtvGfb+dXHjR0DgE3c1Z6kt/S94AW55Yc/zA4rVoydhIGqamVrbf+5283JBQAY4otfzKXWQV6SFsWT9AAAYLFQkAEAhnjnO7Pbxz8+dgomwBQLAIAhvva1PMY6yEuSEWQAAOhRkAEAoEdBBgCAHnOQAQCG2GGH3H3ffWOnYAIUZACAIT772ay2DvKSZIoFAAD0GEEGABjirW/Nk665JpmaGjsJC0xBBgAY4rzzsp11kJckUywAAKBHQQYAgB4FGQAAesxBBgAYYpddcteWW46dgglQkAEAhvjEJ/Kd6ek8fuwcLDhTLAAAoMcIMgDAEEcfnT2uu846yEuQggwAMMSqVdnGOshLkikWAADQYwR5jt0fvVnWHHvw2DFYBKanpzPln83ouB7ocz3A0mYEGQAAehRkAIAh9tort++yy9gpmABTLAAAhjjxxFwxPZ0njJ2DBWcEGQAAeowgAwAMcdRR2ev6662DvAQpyAAAQ1xxRba2DvKSZIoFAAD0KMgAANCjIAMAQI+CDAAwxPLl+ckee4ydgglwkx4AwBDHHZcrp6fjUSFLjxFkAADoMYIMADDEEUfkKT/4gXWQlyAFGQBgiOuuyzLrIC9JplgAAECPggwAAD0KMgAA9JiDDAAwxIEH5tZrrsn2Y+dgwSnIAABDvPe9uWp6OruNnYMFZ4oFAAD0GEEGABjiJS/J0266KTn77LGTsMCMIAMADHHLLdnyttvGTsEEKMgAANCjIAMAQI+CDAAAPW7SAwAY4rnPzY+uuso6yEuQggwAMMTb357vTU/nSWPnYMGZYgEAAD1GkAEAhnjBC/L0H/4wWbFi7CQsMCPIAABD3HFHNr/rrrFTMAEKMgAA9CjIAADQoyADAECPm/QAAIZ40Ytyy3e/ax3kJUhBBgAY4phjcu30dJ48dg4WnCkWAADQYwQZAGCIqaksn5lJVq0aOwkLzAgyAAD0KMgAANCjIAMAQI+CDAAAPW7SAwAY4vDDc+MVV1gHeQkyggwAMMQb35jrDz107BRMgIIMADDE7bdnszvvHDsFE2CKBQDAEC98YfadmUme//yxk7DAjCADAECPggwAAD0KMgAA9CjIAADQ4yY9AIAhjjwyN1x+uXWQlyAjyAAAQxx5ZG6wgsWSpCADAAxx883Z8tZbx07BBJhiAQAwxGGH5WkzM8khh4ydhAVmBBkAAHoUZAAA6FGQAQCgR0EGAIAeN+kBAAzxhjfkn1avtg7yEqQgAwAM8dKX5qbp6bFTMAGmWAAADHHttVl2441jp2ACjCADAAzxylfmKTMzyeGHj52EBWYEGQAAehRkAADoUZABAKBHQQYAgB436QEADPHmN+faSy+1DvISVK21sTMsKst23rPt/Orjxo4BALn62IPHjsAGTE9PZ2pqauwYDFRVK1tr+8/dbooFAMAQa9bkkddcM3YKJsAUCwCAIV7/+uw9M5O86lVjJ2GBGUEGAIAeBRkAAHoUZAAA6FGQAQCgx016AABDvO1t+d7FF1sHeQlSkAEAhnje8/KjLVSppcgUCwCAIVatyjZXXjl2CiZAQQYAGOLoo7PHhz40dgomQEEGAIAeBRkAAHoUZAAA6FGQAQCgx9okAABDvOc9+ceLLsp+Y+dgwSnIAABDHHRQbvvZz8ZOwQSYYgEAMMS55+bRl102dgomQEEGABjij/84v3TSSWOnYAIUZAAA6FGQAQCgR0EGAIAeBRkAAHos8wYAMMRxx+XKCy/M/mPnYMEpyAAAQyxfnp/MzIydggkwxQIAYIivfjWPWbly7BRMgIIMADDEu96V3U45ZewUTICCDAAAPQoyAAD0KMgAANCzKApyVb2jqo7ZwD6HVtVTe6//tKqeN/l0AAA8nExsmbeq2qK1ds8DvR7g0CRnJvl2krTW/uShJQQAeAg+8pGsWbEivz52DhbcvApyVb0qyTFJWpJLkrw9yV8n2THJTUle01q7pqpOTnJnkl9Jck5V/Ys5r/8qyV8leWyS25O8rrV2+ZxzvS7JUUkekeTKJK9MsjzJbyb5N1X1tiQv6TKc2Vo7vaqem+T93ee5IMkbWmt3VdXVST6W5MVJtkzy23PPBzCGGz71lrEjsAmYOv99Y0dgA2ZmZrL9X//1qBmmp6dHPf9StMGCXFVPS/K2JAe11m7uSu/HknystfaxqvoPSf4ysyO8SbJLt++9XWHuv/5akt9trf1DVf16kuOTPGfOKT/XWvtod+53JXlta+2DVXVGukLcvbc231ZJTk7y3NbaFVX18SRvSHJcd7ybW2v7VdUbM1vy/+M6PuNRmS3lecROe2zoRwIAG8WMh1Asas+69dbcd999+buRcyjIC28+I8jPSfKZ1trNSdJa+2FVHZjkt7r3T0ny5739P9Nau3fu66raJslBST6zttwmWbaO8+3TFePtk2yT5MsbyLd3kqtaa1d0rz+W5Pfy84L8ue73lb3Mv6C1dmKSE5Nk2c57tg2cD+Ah2+nlx44dgU3AqmMPHjsC6zM1NTuCvGrV2ElYYJOYg/zTB3i9WZKZ1tryDXz/yUkOba1dXFVHJpl6iHnu6n6/Nx6tDQDABsxnFYuzkvx2Ve2QJN0Ui3OTvKx7/xVJvrmhg7TWbktyVVX9dnecqqpnrGPXbZN8v6q27I691o+79+Zak2T3qlo7N+KVSb6xwU8FAADrsMGC3FpbneTdSb5RVRcn+Yskb0rymqq6JLOF9Pfneb5XJHltd5zVSQ5Zxz5vT7IiyTlJ+jfUfTrJH1XVt6rqyb18dyZ5TWanblya5L4kH55nHgAA+AXVmim3fct23rPt/Orjxo4BALnaHOTFzRzkTV5VrWyt7T93uzm5AABDnHJKvnPeeTlw7BwsuEXxJD0AgE3Orrvmrsc9buwUTICCDAAwxKmn5rFnnTV2CiZAQQYAGOKEE/LEM84YOwUToCADAECPggwAAD0KMgAA9CjIAADQYx1kAIAhTj89q885J88cOwcLzggyAMAQO+6Yu7fbbuwUTICCDAAwxMknZ6cvfWnsFEyAggwAMISCvGQpyAAA0KMgAwBAj4IMAAA9CjIAAPRYBxkAYIgvfCGXnH12njV2DhacEWQAgCG23jr3bbXV2CmYAAUZAGCI44/PEz7/+bFTMAGmWAAADHHaaXnczMzYKZgAI8gAANCjIAMAQI+CDAAAPQoyAAD0uEkPAGCI6emsmp7O1Ng5WHBGkAEAoEdBBgAY4v3vz66nnjp2CibAFIs5dn/0Zllz7MFjx2ARmJ6eztTU1NgxWCRcD/S5HkiSnHlmdrAO8pJkBBkAAHoUZAAA6FGQAQCgR0EGABjikY/MvcuWjZ2CCXCTHgDAEF/8Yi61DvKSZAQZAAB6FGQAgCHe+c7s9vGPj52CCTDFAgBgiK99LY+xDvKSZAQZAAB6FGQAAOhRkAEAoMccZACAIXbYIXffd9/YKZgABRkAYIjPfjarrYO8JJliAQAAPUaQAQCGeOtb86RrrkmmpsZOwgJTkAEAhjjvvGxnHeQlyRQLAADoUZABAKBHQQYAgB5zkAEAhthll9y15ZZjp2ACFGQAgCE+8Yl8Z3o6jx87BwvOFAsAAOgxggwAMMTRR2eP666zDvISpCADAAyxalW2sQ7ykmSKBQAA9CjIAADQoyADAECPggwAMMRee+X2XXYZOwUT4CY9AIAhTjwxV0xP5wlj52DBGUEGAIAeI8gAAEMcdVT2uv566yAvQQoyAMAQV1yRra2DvCSZYgEAAD0KMgAA9CjIAADQoyADAAyxfHl+ssceY6dgAtykBwAwxHHH5crp6XhUyNJjBBkAAHqMIAMADHHEEXnKD35gHeQlSEEGABjiuuuyzDrIS5IpFgAA0KMgAwBAj4IMAAA95iADAAxx4IG59Zprsv3YOVhwCjIAwBDvfW+ump7ObmPnYMGZYgEAAD1GkAEAhnjJS/K0m25Kzj577CQsMCPIAABD3HJLtrzttrFTMAEKMgAA9CjIAADQoyADAECPm/QAAIZ47nPzo6uusg7yEqQgAwAM8fa353vT03nS2DlYcKZYAABAjxFkAIAhXvCCPP2HP0xWrBg7CQvMCDIAwBB33JHN77pr7BRMgIIMAAA9CjIAAPQoyAAA0OMmPQCAIV70otzy3e9aB3kJUpABAIY45phcOz2dJ4+dgwVnigUAAPQYQQYAGGJqKstnZpJVq8ZOwgIzggwAAD0KMgAA9FRrbewMi8qynfdsO7/6uLFjAMCicvWxB48dYfGZmsrMzEy2N8Vik1VVK1tr+8/dbgQZAAB63KQHADDE4YfnxiuusA7yEmQEGQBgiDe+MdcfeujYKZgABRkAYIjbb89md945dgomwBQLAIAhXvjC7Dszkzz/+WMnYYEZQQYAgB4FGQAAehRkAADoUZABAKDHTXoAAEMceWRuuPxy6yAvQUaQAQCGOPLI3GAFiyVJQQYAGOLmm7PlrbeOnYIJMMUCAGCIww7L02ZmkkMOGTsJC8wIMgAA9CjIAADQoyADAECPggwAAD1u0gMAGOINb8g/rV5tHeQlSEEGABjipS/NTdPTY6dgAkyxAAAY4tprs+zGG8dOwQQYQQYAGOKVr8xTZmaSww8fOwkLzAgyAAD0KMgAANCjIAMAQI+CDAAAPW7SAwAY4s1vzrWXXmod5CVIQQYAGOLFL84t2247dgomwBQLAIAh1qzJI6+5ZuwUTIARZACAIV7/+uw9M5O86lVjJ2GBGUEGAICeTXoEuarekeQnSW5O8rettevHTQQAwKZuqYwgH5nkCWOHAABg07fJjSBX1X9N8uokNya5NsnKJPsn+WRV3ZHkTUn+oLX2W1V1SJJPJ9kus38Z+HZr7ZfGSQ4weTd86i1jR2CJmjr/fWNHWHSOW7Uq99xzT46Zmho7yiZtenp67Aj3s0kV5Kr61SQvS7I8s9kvymxBvjDJMa21C6tqiyQf677lN5JcluSAbv8VD3Dco5IclSSP2GmPCX4CANg0zczMjB1h0Tlhhx1y3333+dk8RAryQ/cbSf5Pa+32JKmqM+bu0Fq7p6q+W1VPSfJrSf4iybOSbJ7km+s6aGvtxCQnJsmynfdsE8oOMHE7vfzYsSOwRK069uCxIyxK09PT+agR5CVnqcxBnuvsJC9IcneSryb5192vdRZkAIAHbdWqbHPllWOnYAI2tYJ8dpJDq+qRVbVtkhd323+cpP8om28mOTrJea21m5LskGTvzE63AAB46I4+Ont86ENjp2ACNqkpFq21i6rq1CQXZ/YmvQu6t05O8uHuJr0DMzvX+PGZLdRJckmSnVprpk8AALBem1RBTpLW2ruTvHsdb312zutlve85aqKhAABYMja1KRYAADBRCjIAAPRsclMsAAAWhfe8J/940UXZb+wcLDgFGQBgiIMOym0/+9nYKZgAUywAAIY499w8+jIryC5FCjIAwBB//Mf5pZNOGjsFE6AgAwBAj4IMAAA9CjIAAPQoyAAA0GOZNwCAIY47LldeeGH2HzsHC05BBgAYYvny/GRmZuwUTIApFgAAQ3z1q3nMypVjp2ACFGQAgCHe9a7sdsopY6dgAhRkAADoUZABAKBHQQYAgB4FGQAAeizzBgAwxEc+kjUrVuTXx87BglOQAQCG2Hvv3PH974+dggkwxQIAYIi/+ZvscO65Y6dgAhRkAIAhPvCB7HraaWOnYAIUZAAA6FGQAQCgR0EGAIAeBRkAAHos8wYAMMQpp+Q7552XA8fOwYJTkOfY/dGbZc2xB48dg0Vgeno6U1NTY8dgkXA90Od6IEmy666567vfHTsFE2CKBQDAEKeemseeddbYKZgABRkAYIgTTsgTzzhj7BRMgIIMAAA9CjIAAPQoyAAA0KMgAwBAj2XeAACGOP30rD7nnDxz7BwsOCPIAABD7Lhj7t5uu7FTMAEKMgDAECefnJ2+9KWxUzABCjIAwBAK8pKlIAMAQI+CDAAAPQoyAAD0KMgAANBjHWQAgCG+8IVccvbZedbYOVhwRpABAIbYeuvct9VWY6dgAhRkAIAhjj8+T/j858dOwQSYYgEAMMRpp+VxMzNjp2ACjCADAECPggwAAD0KMgAA9CjIAADQU621sTMsKlX14yRrxs7BorBjkpvHDsGi4Xqgz/XAWq6FTdturbXHzt1oFYv7W9Na23/sEIyvqi50LbCW64E+1wNruRaWJlMsAACgR0EGAIAeBfn+Thw7AIuGa4E+1wN9rgfWci0sQW7SAwCAHiPIAADQoyADAEDPw7YgV9Xzq2pNVV1ZVW9Zx/vLqurU7v0VVbX7CDHZCOZxLfxhVX27qi6pqq9V1W5j5GTj2ND10NvvJVXVqsryTkvUfK6Fqjq8+/NhdVV9amNnZOOZx/8r/mVVfb2qvtX9/+KFY+RkYTws5yBX1eZJrkjyb5Ncl+SCJL/TWvt2b583Jtm3tfa7VfWyJP++tfbSUQIzMfO8Fp6dZEVr7faqekOSKdfC0jSf66Hbb9sk/y/JI5L8p9bahRs7K5M1zz8b9kxyWpLntNZ+VFWPa63dOEpgJmqe18OJSb7VWjuhqp6a5Auttd3HyMtD93AdQf61JFe21v6xtfazJJ9OcsicfQ5J8rHu69OTPLeqaiNmZOPY4LXQWvt6a+327uX5SXbZyBnZeObzZ0OSvDPJnyW5c2OGY6Oaz7XwuiR/1Vr7UZIox0vafK6HluTR3dfbJbl+I+ZjgT1cC/ITk1zbe31dt22d+7TW7klya5IdNko6Nqb5XAt9r03yxYkmYkwbvB6qar8ku7bW/t/GDMZGN58/G/ZKsldVnVNV51fV8zdaOja2+VwP70hyRFVdl+QLSd60caIxCR41DfNUVUck2T/Jvxk7C+Ooqs2S/EWSI0eOwuKwRZI9k0xl9l+Wzq6qp7fWZsYMxWh+J8nJrbUPVNWBSU6pqn1aa/eNHYwH7+E6gvxPSXbtvd6l27bOfapqi8z+c8ktGyUdG9N8roVU1fOS/Nckv9lau2sjZWPj29D1sG2SfZJMV9XVSf5VkjPcqLckzefPhuuSnNFau7u1dlVm56juuZHysXHN53p4bWbnpKe1dl6SrZLsuFHSseAergX5giR7VtWTquoRSV6W5Iw5+5yR5NXd14clOas9HO9oXPo2eC1U1a8k+Uhmy7E5hkvbeq+H1tqtrbUdW2u7dzffnJ/Z68JNekvPfP4/8fnMjh6nqnbM7JSLf9yIGdl45nM9XJPkuUlSVU/JbEG+aaOmZME8LAtyN6f4PyX5cpLvJDmttba6qv60qn6z2+1/Jtmhqq5M8odJHnC5JzZd87wW3pdkmySfqapVVTX3D0WWiHleDzwMzPNa+HKSW6rq20m+nuSPWmv+pXEJmuf18OYkr6uqi5P87yRHGljbdD0sl3kDAIAH8rAcQQYAgAeiIAMAQI+CDAAAPQoyAAD0KMgAANCjIANsQFXtVFWfrqrvVtXKqvpCVe1VVbtX1WULeJ4/7R5Kk6r6japa3S0t+MSqOn3gMY+sqif0Xp9UVU9dqMwAS5Fl3gDWo6oqyblJPtZa+3C37RlJHp3k2iRnttb2mcB5P5zk71prn3iIx5lOcsym/DCTqtq8tXbv2DmAhw8jyADr9+wkd68tx0nSWru4tfbN/k7daPI3q+qi7tdB3fadq+rsbiT4sm5kePOqOrl7fWlV/UG378lVdVhV/cckhyd5Z1V9sj9S3X3v+7vvvaSq3tRt/5OquqDbfmLNOizJ/kk+2Z3/kVU1vfbR2FX1O935L6uqP+t9lp9U1bur6uKqOr+qHj/3h1JVv1ZV51XVt6rq3KraewP5Duj2u7iq/r6qtu1Gtz/UO+aZVTXVy/CB7qELB67r83X77VFVX+2Oe1FVPbmqPl5Vh/aO+8mqOmTgf3/gYUhBBli/fZKsnMd+Nyb5t621/ZK8NMlfdttfnuTLrbXlSZ6RZFWS5Ume2Frbp7X29CT/q3+g1tpJmX2M7R+11l4x5zxHJdk9yfLW2r5JPtlt/1Br7YBuNPuRSV7UWjs9yYVJXtFaW95au2PtQbppF3+W5DldngN6pfJRSc5vrT0jydlJXreOz3t5kt9orf1Kkj9J8p4Hylezj+Y9Ncnvd8d8XpI77n/IX/CoJCtaa89orf3duj5ft98nk/xVd9yDknw/s09CPbL7nNt12//fBs4H8M8UZICFsWWSj1bVpUk+k2TtPN8Lkrymqt6R5OmttR8n+cckv1RVH6yq5ye57UGc53lJPtI9+jattR92259dVSu68z8nydM2cJwDkky31m7qjvXJJM/q3vtZkjO7r1dmtvDOtV1mH79+WZL/3jvfuvLtneT7rbULum23rX1/Pe5N8tne6/t9vqraNrN/0fg/3XHvbK3d3lr7RpI9q+qxSX4nyWfncT6Af6YgA6zf6iS/Oo/9/iDJDzI7Srx/kkckSWvt7MwWz39KcnJVvaq19qNuv+kkv5vkpIcSsKq2SnJ8ksO6EemPJtnqIRzy7vbzG1TuTbLFOvZ5Z5KvdyO6Lx54vnvyi/8f6h/jzrXzjgd+vo8nOSLJa5L89YBswMOYggywfmclWVZVR63dUFX7VtVvzNlvu8yOkt6X5JVJNu/23S3JD1prH81sEd6vqnZMsllr7bNJ3pZkvweR5ytJXl9VW3TH/xf5eVm8uaq2SXJYb/8fJ9l2Hcf5+yT/pqp2rKrNMzvS+o0HkWO7zJb+pJvOsJ58a5LsXFUHdNu27d6/OsnyqtqsqnZN8msPcK51fr5uNP66tVNDqmpZVW3d7XtykqO7/b79ID4XgIIMsD7dSOq/T/K8ml3mbXWS9ya5Yc6uxyd5dXdT2S8n+Wm3fSrJxVX1rczOTf4fSZ6YZLqqViX5RJK3PohIJyW5Jskl3ble3lqbyeyo6mVJvpzZaR1rnZzkw2tv0ut9ru8neUuSrye5OMnK1tr/fRA5/jzJe7vP1R9hXle+n2X2s3+w2/aVzJbec5JcleTbmZ2zfdG6TrSBz/fKJP+5qi7J7GojO3Xf84Mk38mc+d0A82GZNwCWnG4k+dIk+7XWbh07D7BpMYIMwJJSsw9b+U6SDyrHwBBGkAEAoMcIMgAA9CjIAHN0T5z7Rre6w9BjnNw9ye4Bt1fVSVX11Pt/98Y396l2I+ZY589toY5ZVZ+uqj0X8vjA0qMgA9zff0jyubXr8K61dumyhdJa+4+WINvoTkjyX8YOASxuCjLA/b0iyf9NkqqaqqpvVtUZSb5dVZtX1fuq6oKquqSqXt/tV1X1oapaU1VfTfK4DZ2kqqarav/u659U1bur6uKqOr+qHt9tf2xVfbY73wVV9cyH+uGq6oCqOrc71993T6RLkidU1Zeq6h+q6s97+59QVRdW1eqq+m+97VdX1X+rqouq6tKq+uVu+zZV9b+6bZdU1Uu67f+uqs7r9v9Mt6bx+nL+ajeSv7KqvlxVO1fVL1fV3/f22b17ut4691/HYb+Z2SX7FvQvO8DSoiAD9FTVI5L8Umvt6t7m/ZL8fmttrySvTXJra+2AzD6u+XVV9aTMrpW8d2YfMf2qJAc9yFM/Ksn5rbVnJDk7yeu67f8jyX/vzveSrOOpe1W1d7fO8bp+bb+Oz3dq93mekdlHQ9/Rvb08s+sVPz3JS7uHdyTJf22t7Z9k38w+XGTf3iFvbq3tl9mR2WO6bW/vfkZPb63tm+Ssmn04ytuSPK/b/8Ikf/hAP4yq2jLJBzP79LxfzezT8N7dWrs8ySO6n3m6vKc+0P5zj9s9yOXKzD7JEGCd/A0a4BftmGRmzra/b61d1X3975Ls25snu12SPTP7OOn/3U3LuL6qznqQ5/1ZkjO7r1cm+bfd189L8tSqWrvfo6tqm9baT9ZuaK2tyWy5nY+9M/vEvwu6770tSbrjf23tsmhV9e0kuyW5NsnhNfskwS2S7JzZvwRc0h3vc73Mv9XL/LJevh9V1Yu67zunO9cjkpy3gZz7JPlKt//mSb7fvXdaZovxsd3vL93A/nPdmOQJXWaA+1GQAX7RHfn5o43X+mnv60ryptbal/s7VNULH+J5724/X3fz3vz8z+fNkvyr1tqdD/SNVbV3ZkeF12WqexLdfNzV+/reJFt0I7XHJDmgK7on5xd/Pnf191/PsSvJV1prvzPPLJVkdWvtwHW8d2qSz1TV5zL7sMN/qKqnr2f/ubbKz0fNAe7HFAuAntbaj5JsXlVzS/JaX07yhu6f9FNVe1XVozI7LeKl3RzlnZM8e4Ei/W2SN619UVXL15F5TWtt+QP8mpmz+5okO1fVAd3xtt3AfNxHZ/YvCLd286JfMI/MX0nye73Mj0lyfpJnVtUe3bZHVdVe6znGmiSPraoDu/23rKqndZ/3u5kt5G/Pz/9i8ID7r8NemX1sNcA6KcgA9/e3Sf71A7x3UpJvJ7moqi5L8pHMjpz+nyT/0L338ax/+sCD8Z+T7N/d7PbtJL/7UA7WWvtZZqckfLCqLs5smX2gvwyktXZxkm8luTzJp5KcM4/TvCvJY6rqsu4cz26t3ZTkyCT/u6ouyezP55c3kPOwJH/WHWNVfnFe96lJjsjsdIv57J8k6Ur+Ha21G+bxOYCHKU/SA5ijqvZL8gettVeOnYWFVVV/kOS21tr/HDsLsHgZQQaYo7V2UZKv10N4UAiL1kySj40dAljcjCADAECPEWQAAOhRkAEAoEdBBgCAHgUZAAB6FGQAAOj5/wFI5fIk3CBlIwAAAABJRU5ErkJggg==\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABBIAAAKqCAYAAACHEX3kAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAAsTAAALEwEAmpwYAABdcElEQVR4nO3de9zX8/0/8EdHpKNTTnPuulBSskiJ5Tg0aeaUGG1FG5vZHMbX135mczZkTkOEmK2IxczaLCwp5BQidJCcKik6fn5/uF3X1+WqvKsrV7jfbze327xfr8/r/Xy/e62bz+Pzer/edUqlUikAAAAABdSt7QIAAACArw5BAgAAAFCYIAEAAAAoTJAAAAAAFCZIAAAAAAqrX9sF8OXq0aNHpkyZkkaNGmXzzTev7XIAAABYzbz55puZO3duNt1009xzzz3V2gUJ3zBTpkzJ7NmzM3v27EyfPr22ywEAAGA1NWXKlCUeFyR8wzRq1CizZ89OkyZNst1229V2ObBKzZ49O0nSpEmTWq4EVj3znW8S851vEvOd2jB+/PjMnj07jRo1WmK7IOEbZvPNN8/06dOz3XbbZdCgQbVdDqxSY8eOTZJ06NChliuBVc9855vEfOebxHynNvTu3TujR49e6uPwNlsEAAAAChMkAAAAAIUJEgAAAIDCBAkAAABAYYIEAAAAoDBBAgAAAFCYIAEAAAAoTJAAAAAAFCZIAAAAAAoTJAAAAACFCRIAAACAwgQJAAAAQGGCBAAAAKAwQQIAAABQmCABAAAAKEyQAAAAABQmSAAAAAAKEyQAAAAAhQkSAAAAgMIECQAAAEBhggQAAACgMEECAAAAUJggAQAAAChMkAAAAAAUVr+2C6B2jJr4frY442+1XQZ8Oe421/kGMd/5JjHfV4k3LjiwtksAVnNWJAAAAACFCRIAAACAwgQJAAAAQGGCBAAAAKAwQQIAAABQmCABAAAAKEyQAAAAABQmSAAAAAAKEyQAAAAAhQkSAAAAgMIECQAAAEBhggQAAACgMEECAAAAUJggAQAAAChMkAAAAAAUJkgAAAAAChMkAAAAAIUJEgAAAIDCBAkAAABAYYIEAAAAoDBBAgAAAFCYIAEAAAAoTJAAAAAAFCZIAAAAAAoTJAAAAACFCRIAAACAwgQJAAAAQGGCBAAAAKAwQQIAAABQmCABAAAAKEyQAAAAABQmSAAAAAAKW22DhOHDh+fhhx+u7TJq3bhx4zJw4MDaLgMAAACSrMZBwpVXXilISPLXv/41t956a22XAQAAAElW0yBh1qxZeeONN2q7jNXCuHHjarsEAAAAqLTaBQlnnHFGOnbsmFKplKFDh6a8vDy9e/dOknz88ccZMGBAunfvnh133DFt2rRJt27dcs455+S9996rMs6UKVNSXl6eww47LPPnz8/ll1+evfbaK23atEmXLl1y9tln58MPP6x2/vvvvz89e/bMjjvumE6dOuW0007Lu+++mwsvvDDl5eUZPHhwlf6ffPJJrr766nTv3j1t27ZN+/bt07NnzwwcODALFy6s0veqq66qHGPChAnp379/dt1117Rp0yYHHXRQ7r777sq+Q4YMSXl5eV566aVMnTo15eXlKS8vr6nbDAAAACukfm0X8HkHHHBA1lhjjdx5551p06ZNDjjggGy00UZZvHhxfvzjH+fJJ59M27Zt06dPn5RKpfznP//JXXfdlccffzz33HNPGjduXG3Mk046KW+99VYOOeSQzJ07N/fff3/uvvvuvP/++7nmmmsq+/3lL3/JWWedlTXXXDM9evRIy5Yt85///CdHHnlkdtlll2rjfvLJJzn66KPz3HPPZccdd8xxxx2X+fPn59///nd+//vf5/HHH8+1116bunWr5jWTJk3KZZddlq5du+bYY4/NG2+8kWHDhuXss89O8+bNs88++2SHHXbIaaedlosuuijNmjVLv379av5mAwAAwHJa7YKErl27VgYJrVq1Sp8+fZIko0aNypNPPpntttsugwcPTv36n5Z+0kkn5Qc/+EGef/753HvvvenVq1eV8Z5//vl06tQpf/3rX9OwYcMkybHHHptu3brlX//6V2bOnJnmzZtnwYIFufTSS5Mk1157bTp16pQk6d+/f04//fTce++91Wr94x//mOeeey6HHXZY/t//+3+pU6dOkuSUU07Jj370ozzyyCMZOnRovv/971f53C233JLf/e536dGjR+WxbbbZJpdcckmGDh2affbZJ61atUqrVq1y0UUXpXHjxpX3YUmGDBmSoUOHFrq/48ePL9QPAFi9zXz09sx6bPAXd4TlVOfC2q6Ar7Mf//jHfiT9Cpg9e/Yy21e7IGFpWrVqlZtvvjnNmjWrDBGSpG7duvnOd76T559/Pi+//HK1zy1a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27dfan0vvfRSjjzyyCxYsCAHHnhgttxyy7z33nu59957c+6552by5Mk57bTTVtn9AQAAgBX1tQwShg0blgsvvDA9evSoPLbbbrvl5z//eS6++OLsvffemTx5coYNG5b1118/d999d9Zee+3KvieddFIeeuihDBo0qNoX+lmzZqVu3bq5+uqrK4+1bt0606dPz2OPPZbdd989PXv2XGZ9N998c+bOnZvTTjstffr0qTx+6KGHpkePHrn11ltz4oknpkmTJit5JwAAAKBmfS2DhM0226xKiJAk+++/f9Zff/288cYbmTx5cpo3b55bbrkl9evXrxIiJEm3bt3y0EMP5eWXX6429oIFC74wKPgiffr0yX777ZeddtqpyvFtt902G2+8caZOnZrXXnst7dq1W6nzAAAAQE37WgYJO+64Y7VjderUyZZbbpl33303EydOzB577JFdd901SVIqlTJr1qzMmTOnyv4E8+fPX+L422+//UrVV1ZWlrKysspzzJgxIwsWLEiSNG3aNFOnTs28efNW6hwAAACwKnwtg4R11113icebNm2aJPnwww+TJE8++WQGDBiQsWPHVn6RL6JZs2YrVd8nn3ySAQMG5L777svbb7+9UmMBAADAl+lrGSTUrbvkl1EsXrw4SbLGGmtk1KhROf7447No0aIcdNBB2XXXXdO0adPUq1cvL774YpU9ED6vXr16K1Vfv379MmrUqHzrW9/KySefnM033zxrrrlmkuTiiy/OG2+8sVLjAwAAwKrytQwSZsyYscTjs2fPTvLpioVrrrkmixYtys9+9rP079+/Sr+lPdJQE8aNG5dRo0Zl/fXXz5///Oess846VdovvfTSVXZuAAAAWFlL/un+K+7ZZ5+tdmzx4sWZOHFikmSTTTbJlClTkny6seLnjRw5cpXVVnHeHXfcsVqIMHnyZKsRAAAAWK19LYOE1157LQ8++GCVY8OHD8/777+f8vLybLjhhmnZsmWSZMKECVX6DRs2LI899liST1/1WNQaa6yRZOmrISpUnHfixImVj1okyQcffJDTTz+9cn+Hin0cAAAAYHXytXy0Yf/998/ZZ5+dBx54IK1atcr06dMzdOjQ1K1bN7/85S+TJIccckhGjRqVc889N88//3yaNm2ap556KuPHj891112Xww8/PK+88kp+97vfZf/996/2qsbP22abbZIkN9xwQ6ZOnZqWLVumX79+1frtuOOO2WqrrTJx4sQce+yx6dSpUz744IMMHz48Bx98cHbYYYcMHDgwAwYMyOuvv56+ffvW/A0CAACAFfS1XJHwrW99K7fcckvmzJmTgQMHZtiwYWndunWuu+66dO3aNUnSo0ePnHvuudlwww1z55135s4778w666yTu+66KzvssEN+8YtfpFmzZhkyZEgmTZr0hefcc88984Mf/CDz58/PkCFD8uabby6xX4MGDXLDDTdk3333zWuvvZYbbrghY8eOzc9//vOcfvrpOfbYY9O2bdtMnDgx999/f43eFwAAAFhZdUqlUqm2i6gpV111VQYMGJAf//jHlSsPqKp3794ZPXp0Fq+3dRbs/pPaLgcA4EvxxgUH1nYJsELGjh2bJOnQoUMtV8I3ScX3xo4dO2bQoEHV2r+WjzZ80wwZMiRDhw4t1Hf8+PGruBoAWH3NfPT2zHpscG2XQS2oc2FtV0Bt+vGPf7zEx46/SioCBfgyVLzxcGkECV8DU6dOzejRo2u7DAAAAL4BBAlfA5tsskk6duxYqO/48eO/MF0CAICvk4033vgr+2iARxuoDU2aNFlmuyDha6Bnz57p2bNnob4Vz7oAwDdR8y690rxLr9oug1pgjwSAmvO1ChJOOumknHTSSbVdBgAAAHxtfS1f/wgAAACsGoIEAAAAoLBvTJDQu3fvlJeXZ8qUKTU+9lVXXZXy8vIMGTKkxscGAACA1cnXao+EL8Odd96ZLbfcMrvsskvlsc6dO6dRo0bZYYcdarEyAAAAWPW+MSsSasKiRYty4YUXVnvrwU477ZQ+ffqkVatWtVQZAAAAfDkECcvhlVdeydy5c2u7DAAAAKg1hYOEhQsX5uabb87BBx+cdu3apX379vnhD3+YMWPGVOk3f/783HDDDTnkkEPSvn37tG3bNvvtt18uuOCCfPDBB1X6DhkyJOXl5bnkkkvyj3/8I/vuu2/l4wFTpkxJeXl5DjvssLz66qs5/PDD07Zt2yrnmzZtWs4555x069Ytbdq0yS677JLjjjsuI0aMKHwDnnrqqfTv3z+dO3dO69ats9NOO+Woo47K8OHDq/Tr3bt3evTokSQZMGBAysvLc8YZZyRZ+h4Js2fPzuWXX54DDjggO+64Y9q1a5fu3btnwIABmTNnTpW+FWMMHjw4EyZMSP/+/bPrrrumTZs2Oeigg3L33XcXviYAAABYVQrtkbB48eKccMIJGTlyZHbZZZf07ds3M2fOzL333ptevXrlsssuy4EHHpgFCxakT58+GT16dMrKynLYYYdlzTXXzLPPPpubb745//jHP3LXXXdlvfXWqzL+O++8k//5n/9Jjx490rhx42rnP/XUU1NWVpZu3bqlZcuWSZLXXnstvXr1ysyZM7P33nunZ8+eee+99zJ8+PCceOKJOfXUU9O3b99lXtfIkSNzwgknpGHDhunevXs23njjvPXWW7nnnntyyimnZMaMGenVq1eS5Mgjj8x6662X4cOHp3PnzuncufMyH2WYPXt2jjjiiLz66qtp165devfunVKplCeffDJXXXVVRowYkTvuuCNrrrlmlc9NmjQpl112Wbp27Zpjjz02b7zxRoYNG5azzz47zZs3zz777FPkjwwAAABWiUJBwh133JGRI0emR48eufDCCyuP9+rVK927d88555yTvfbaK4MHD87o0aPTqVOn/OlPf0r9+v83/HnnnZfbbrstl19+ec4///wq4z/44IO59NJLl/gl+aWXXsphhx2Ws88+u8rxs846KzNmzMjFF1+c733ve5XH+/fvn+9973u54oorsu+++2aLLbZY6nVdd911WbhwYS699NLsv//+lcf33HPPnHjiibn66qtz1FFHpU6dOjnggAPyySefZPjw4Wnfvn369OmzzHt2xRVX5NVXX80hhxySCy64oPJ4qVRK//79M2LEiNx0003p379/lc/dcsst+d3vfle5+iFJttlmm1xyySUZOnSoIAEAAIBaVejRhopl9Z//8rz55pvntNNOy/HHH59Zs2blnnvuSZL89Kc/rRIiJEm/fv2SJMOHD8+iRYuqtDVq1CjdunVb4rnnzZuXQw89tMqxCRMm5Omnn07r1q2rhAhJssEGG+SYY47JwoULc//99y/zuk477bRcffXV1c69xx57pH79+nn//ffz3nvvLXOMJSmVShk2bFiS5OSTT67SVqdOncp7saT6ysrKqoQISdKlS5ckyRtvvLHctQAAAEBN+sIVCfPnz88rr7ySBg0aZJtttqnWfvTRR1f2mzBhQurVq5e2bdtW67fBBhtko402yrRp0zJ58uQqKwXKy8tTr169JZ6/Xr16KS8vr3LsmWeeSZJsttlmmTJlSrXPbLzxxkmSF154YZnX9tk6P/7448ycObMy5GjSpElmzJiRefPmLXOMJZk0aVJmzZqVDTfcsLKWz9p+++1Tr169vP7665k3b17WWGONyrbWrVtX61/xuMcnn3yy3LUAAABATfrCIGHGjBlZvHhxmjVrlrp1l76AoeJLePPmzdOwYcMl9llnnXUybdq0zJgxo0qQ0KxZs6WO27Rp09SpU6fKsYpNGx944IE88MADS/3s+++/v9S25NNr+8Mf/pCHHnqo2kaQK6NirHXXXXeJ7Q0bNkyTJk0yc+bMzJo1KxtssEFlW4sWLar1//z1AwAAQG35wiCh4kvs/PnzC/UrlUpL7bN48eIkqRZILG01wtLaKs6155575gc/+MFSP9ukSZOltn3yySfp3bt3JkyYkPLy8vTp0ycbbbRR5eqAM888Mx9++OFSP78sK3MvAAAAYHX2hUFC8+bNU79+/cyZMyfz589f6mqDZs2apX79+vnoo4+W2q/il/p11llnpYqueOvDWmutlb333nuFxhgxYkQmTJiQsrKy/PnPf67y9oTFixdn4cKFK1xfxUqEpa2ImD9/fubMmZP69eunadOmK3weAAAA+LJ94c/hDRs2TFlZWZJk1KhR1dqvv/769O/fPxMnTkx5eXkWLVpUuYfBZ7311luZPn16mjdvnk033XSlit5xxx2TJGPGjMmCBQuqtX/44YeZMWPGMseo2FuhU6dO1V7BOGbMmMydO3eF69t0003TvHnzTJ8+PVOnTq3W/uyzz2bRokUpLy9fajADAAAAq6NC6+or3iIwcODAKm9ceOutt3L99ddn1KhR2WyzzSrfrnDNNddU+0X/6quvTpL07NlzpZ/533rrrdO+ffu8++67ueWWW6q0LVy4MGeddVZ22223/Pe//13qGBX7Erz66qtVjk+ePDnnn39+mjdvniRVHm+oeOzhi0KKOnXqVN6LAQMGVGlbtGhRrrnmmiTJ97///WWOAwAAAKubL3y0IUmOOuqoPPzww3nsscdy+OGHZ88998zs2bMzbNiwfPTRR7n00kvTqFGjHH744ZX9Dj300HTu3Dl169bN2LFjM3bs2Gy77bb56U9/WiOFn3feeendu3cuvvjijB49Ou3bt89HH32UESNGZOLEidlnn32yyy67LPXze+65Z1q0aJHHHnss/fv3zw477JBp06blb3/7W0455ZSMGjUq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m2++Of/4xz9y1113Zb311qsy/jvvvJP/+Z//SY8ePdK4ceNq5z/11FNTVlaWbt26pWXLlkmS1157Lb169crMmTOz9957p2fPnnnvvfcyfPjwnHjiiTn11FPTt2/fZV7XyJEjc8IJJ6Rhw4bp3r17Nt5447z11lu55557csopp2TGjBnp1atXkuTII4/Meuutl+HDh6dz587p3LnzMh9lmD17do444oi8+uqradeuXXr37p1SqZQnn3wyV111VUaMGJE77rgja665ZpXPTZo0KZdddlm6du2aY489Nm+88UaGDRuWs88+O82bN88+++xT5I8MAAAAVolCQcIdd9yRkSNHpkePHrnwwgsrj/fq1Svdu3fPOeeck7322iuDBw/O6NGj06lTp/zpT39K/fr/N/x5552X2267LZdffnnOP//8KuM/+OCDufTSS5f4Jfmll17KYYcdlrPPPrvK8bPOOiszZszIxRdfnO9973uVx/v375/vfe97ueKKK7Lvvvtmiy22WOp1XXfddVm4cGEuvfTS7L///pXH99xzz5x44om5+uqrc9RRR6VOnTo54IAD8sknn2T48OFp3759+vTps8x7dsUVV+TVV1/NIYcckgsuuKDyeKlUSv/+/TNixIjcdNNN6d+/f5XP3XLLLfnd735XufohSbbZZptccsklGTp06BLv0ZAhQzJ06NBl1lNh/PjxhfoBQBEzH709sx4bXNtlwGqhzoVf3Ae+SX784x+nX79+tV0GK2D27NnLbC/0aEPFsvrPf3nefPPNc9ppp+X444/PrFmzcs899yRJfvrTn1YJEZJUTqDhw4dn0aJFVdoaNWqUbt26LfHc8+bNy6GHHlrl2IQJE/L000+ndevWVUKEJNlggw1yzDHHZOHChbn//vuXeV2nnXZarr766mrn3mOPPVK/fv28//77ee+995Y5xpKUSqUMGzYsSXLyySdXaatTp07lvVhSfWVlZVVChCTp0qVLkuSNN95Y4vmmTp2a0aNHF/rniyYEAAAALMsXrkiYP39+XnnllTRo0CDbbLNNtfajjz66st+ECRNSr169tG3btlq/DTbYIBtttFGmTZuWyZMnV1kpUF5ennr16i3x/PXq1Ut5eXmVY88880ySZLPNNsuUKVOqfWbjjTdOkrzwwgvLvLbP1vnxxx9n5syZlSFHkyZNMmPGjMybN2+Z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9svDDz+cBQsWpEGDBkmSF154IR999FH23XffavUMGTIkQ4cOLVT7+PHjl+9iAWA1NfPR2zPrscG1XQZ87dS5sLYrgK+vH//4x+nXr19tl/GFZs+evcx2QcIK+Oc//5kkOfbYY6sc32STTdKzZ8/ccssty/x8w4YN8+1vfzuPPPJI3n333ay//vpJPg0SmjdvngMPPDD3339/nnvuuey0006VbUmy++67Vxtv6tSpGT169EpfFwAAAHwRQcJy+uSTTzJ58uQ0aNAg22yzTbX2HXfcsdA4u+22Wx555JE8+eSTOeCAA5Iko0aNSvv27bPzzjunbt26efLJJyuDhNGjR6du3brp1KlTtbE22WSTdOzYsdB5x48f/4XpEgAAADVv4403TocOHWq7jC/UpEmTZbYLEpbTrFmzUiqVsvbaa6du3epbTDRv3rzQOJ07d06SjBkzJgcccEDefvvtvPnmmzn88MPTtGnTlJWV5cknn0y/fv2ycOHCjB07Nm3atEmLFi2qjdWzZ8/07Nmz0Hl79+5t9QIAXwvNu/RK8y69arsM+Np544IDa7sEPmPs2LFJ8pX48sk3h80WV1CdOnWWeHzx4sWFPt+qVatssMEGlZsqjho1KkkqVxbsvPPOeeqpp7Jo0aK88MILmTNnTrp06VIDlQMAAMCKEyQsp4olHh999FFKpVK19vfff7/wWJ07d86ECRMya9asPPHEE2nSpEm23377JJ8GCXPmzMmLL75YuT+CIAEAAIDaJkhYTo0aNcqGG26YBQsW5PXXX6/W/swzzxQea7fddkupVMqYMWMyatSodOjQIfXq1UvyaZCQfPqKyDFjxqRJkyaF918AAACAVUWQsAL22GOPJMntt99e5fjkyZMzbNiwav0bNmyYWbNmZdGiRVWOd+7cOXXq1Mnw4cPz1ltvVdkwcf31188WW2yR0aNHZ9y4cenUqVPq17elBQAAALXLN9MV8OMf/zjDhw/PbbfdlmnTpmWHHXbIW2+9lYceeig9evSoFjC0atUqL7zwQvr27ZvNNtssRx11VFq1apV111035eXl+fvf/54k1d680KFDhwwbNiwLFizwWAMAAACrBSsSVsC3vvWt3Hbbbdljjz3yxBNP5Nprr81zzz2XM888M0cddVS1/meffXa22mqrPPHEE3n44YezcOHCyrbddtstCxYsqLI/QoVvf/vbWbBgQRL7IwAAALB6qFNa0o6BfG1VvP5x8XpbZ8HuP6ntcgAAWM14/ePqxesfqQ0V3xs7duyYQYMGVWu3IgEAAAAoTJAAAAAAFCZIAAAAAAoTJAAAAACFCRIAAACAwgQJAAAAQGGCBAAAAKAwQQIAAABQmCABAAAAKEyQAAAAABQmSAAAAAAKEyQAAAAAhQkSAAAAgMIECQAAAEBhggQAAACgMEECAAAAUJggAQAAAChMkAAAAAAUJkgAAAAAChMkAAAAAIUJEgAAAIDCBAkAAABAYYIEAAAAoDBBAgAAAFCYIAEAAAAoTJAAAAAAFCZIAAAAAAoTJAAAAACFCRIAAACAwgQJAAAAQGH1a7sAaseuW62bQRccWNtlwCo1duzYJEmHDh1quRJY9cx3vknMd4DaZUUCAAAAUJggAQAAAChMkAAAAAAUJkgAAAAAChMkAAAAAIUJEgAAAIDCBAkAAABAYYIEAAAAoDBBAgAAAFCYIAEAAAAoTJAAAAAAFCZIAAAAAAoTJAAAAACFCRIAAACAwgQJAAAAQGGCBAAAAKAwQQIAAABQmCABAAAAKEyQAAAAABQmSAAAAAAKEyQAAAAAhQkSAAAAgMIECQAAAEBhggQAAACgMEECAAAAUFj92i6AL9ebb76ZJBk/fnx69+5dy9XAqjV79uwkSZMmTWq5Elj1zHe+Scx3vknMd2rD+PHjk/zf98fPEyR8w8yaNSvJp38hjR49uparAQAAYHU1d+7cJR4XJHzD1K376dMsDRo0SPv27Wu5Gli1xo8fn9mzZ6dJkybZbrvtarscWKXMd75JzHe+Scx3asObb76ZuXPnZtNNN11iuyDhG6ZNmzYZPXp02rdvn0GDBtV2ObBK9e7dO6NHj852221nvvO1Z77zTWK+801ivrM6stkiAAAAUJggAQAAAChMkAAAAAAUJkgAAAAAChMkAAAAAIUJEgAAAIDCBAkAAABAYYIEAAAAoDBBAgAAAFBY/dougC/XIYccko4dO2aTTTap7VJglTPf+SYx3/kmMd/5JjHfWR3VKZVKpdouAgAAAPhq8GgDAAAAUJggAQAAAChMkAAAAAAUZrPFr4kHH3wwt99+e1588cXMnz8/m2++eQ488MAcd9xxWXPNNQuN8cILL+S6667LmDFj8uGHH2b99dfP7rvvnp/85Cdp2bLlKr4CKK4m5vujjz6aW265Jc8++2w++uijNGvWLO3bt0+fPn2y0047reIrgGJqYq5/3sknn5y///3v6dixYwYNGlTDFcOKq4n5vnDhwtx6660ZOnRo3nzzzay11lopKyvLcccdl27duq3iK4DiVna+l0ql3HPPPRkyZEheeumlzJ07N02bNs0OO+yQww8/PHvttdeXcBV8k9ls8WvgqquuyoABA7LRRhvlu9/9bho3bpzHH388Y8aMSceOHXPzzTenfv1lZ0YjR45M//79U69evXzve9/LxhtvnAkTJmT48OFZb731cuedd9opltVCTcz3P/3pT7n44ovTqFGjHHDAAdloo43y8ssv5x//+Efq1KmTq666KnvvvfeXdEWwZDUx1z/vnnvuyemnn54kggRWKzUx3xcsWJATTjghjz76aDp37pydd94577//fu67777MmjUr5513Xg477LAv6Ypg6Wpivp9++um55557st5662X//ffPBhtskGnTpmXYsGGZM2dOfvrTn+akk076kq6Ib6QSX2njx48vbbvttqVu3bqVZs6cWaXtV7/6VamsrKx04403LnOMefPmlbp06VJq3bp16dlnn63S9uc//7lUVlZW6tevX43XDsurJub7a6+9Vtpuu+1KO++8c2nixIlV2v7yl7+UysrKSnvttVeN1w7Loybm+ue99dZbpQ4dOpQOOeSQUllZWenoo4+uyZJhhdXUfL/mmmtKZWVlpRtuuKHK8YkTJ5Z22mmn0lFHHVVatGhRjdYOy6sm5vvTTz9dKisrK3Xu3Ln0wQcfVGl77bXXSq1bty5tv/32pffff7/G64cK9kj4irvrrruyePHi9OnTJ82aNavSVpFCDh48eJlj/POf/8w777yTvffeOzvssEOVtu9///vZZJNN8u9//ztvvfVWzRYPy6km5vtTTz2Vxo0b5+CDD86WW25Zpa1Hjx5ZY401Mnny5Lzzzjs1Wzwsh5qY659VKpVyxhlnZNGiRZUrEmB1URPzfeHChRk4cGBatWqVPn36VGnbcsstM3bs2Nx+++2pW9d/+lK7amK+v/7660mSDh06pEWLFlXattpqq2y55ZZZuHBhpkyZUoOVQ1X+Nv2KGzVqVJKkc+fO1dq+9a1vZZNNNsmkSZOWGQIsa4y6deumY8eOKZVKGT16dA1VDSumJub7oYcemtGjR+fss8+u1lavXr2stdZaSZLFixfXUNWw/Gpirn/WLbfcklGjRuX000/3mBqrnZqY72PHjs2MGTOy3377pU6dOkmSGTNmZMaMGaumaFhBNTHfy8rKknwaKJQ+95T6vHnz8s4772SNNdbIFltsUXOFw+cIEr7CFixYkDfffDP16tXLpptuusQ+m2++eZJkwoQJSx3n1VdfTZJsttlmKzwGrGo1Nd+X5ZlnnsnMmTOzxRZbZMMNN1zhWmFl1PRcf+2113LZZZdl9913zxFHHFGjtcLKqqn5Pn78+CTJNttsk0GDBmXPPffMrrvuml133TXdunXLn//855ovHpZTTc331q1b56ijjsrLL7+cU045JU8//XQmTZqUMWPG5Oc//3lmzpyZX/ziF2natOkquQ5IvLXhK+2jjz7KokWL0qRJk9SrV2+JfSqWTH344YdLHaeibWl/2RQZA1a1mprvyxr/f/7nf5Ikp5566ooXCiupJuf6ggUL8qtf/Sprrrlmzj///BqvFVZWTc33adOmJfl0SfiECRPSq1evbLnllnnttddy00035X/+53/y3nvvpX///jV/EVBQTf79/r//+79p3bp1zj///DzwwAOVx9dbb71cffXVNo1mlRMkfIV98sknSZIGDRostU/Dhg2r9F2Sjz/+eJnjVIxR0Q9qQ03N9yV5//33c8IJJ+SVV17Jj370o+y7774rXiispJqc61dffXVeeOGFXHrppV7jy2qppub7nDlzkiQvvfRShgwZUuXX3j333DOHHXZY/vjHP+b73/++/y9Qa2ry7/fbbrstv//977P55pvnqKOOyrrrrptp06blrrvuyimnnJLzzjsvPXr0qLHa4fMECV9hFe+YXbBgwVL7zJs3r0rfJal4Jnxp41SMUdEPakNNzffPe/XVV9OvX79MmTIlffv2zS9+8YuVKxRWUk3N9XHjxuX666/P/vvvn4MOOqhmi4QaUlPzveLX3UMOOaTakvG2bdumc+fOefTRR/Of//wnP/jBD1a2bFghNTXfx44dm9/+9rcpKyvL3XffnTXWWKOy7fDDD88BBxyQs88+OzvuuGO1jaWhptgj4SusSZMmqV+/fubMmZP58+cvsU/FJkOf39H1s5o3b54kmTlz5gqPAataTc33z/rPf/6Tww8/PNOnT895552XU089tXKTLqgtNTHXP/7445x22mnZYIMN8pvf/GaV1Qorq6b/W2bdddddYnurVq2S/N8jEFAbamq+33XXXSmVSjnyyCOrhAhJsvbaa6dHjx5ZsGBBHnrooZorHj5HkPAVVr9+/Wy11VZZvHhx3nzzzSX2mThxYpJku+22W+o4FTu/VvRd2hjbbrvtypQLK6Wm5nuFRx55JP3790+9evVy00035bDDDqvRemFF1cRcf+655/LGG29k2rRp2WWXXVJeXl75z1577ZUkGT16dMrLy9OtW7dVcyFQQE393b711lsnWXpQULFM/PNfuuDLVFPz/d13303y6X4IS1IRQkydOnVlyoVl8mjDV1znzp3zyiuvZOTIkZVpe4UXX3wx7777brbffvulJvQVY9x2220ZOXJkevXqVaVt/vz5+e9//5sGDRpk1113XSXXAEXVxHxPkqeffjonn3xymjZtmltvvTXbbLPNqiwbltvKzvUNN9wwxx9//BLbPvroo/z5z3/OhhtumAMOOKDae8zhy1YTf7d36tQpdevWzaOPPppFixZV28juxRdfTJKUl5fX/AXAcqiJ+V4RILzxxhtLbJ80aVKSZP3116+ZomEJrEj4ijviiCPSoEGDDBw4MO+//37l8UWLFuWyyy5LkhxzzDGVx99666289tprmTt3buWxrl27ZvPNN88jjzySJ598ssr4N910Uz744IN0794966yzziq+Gli2mpjvc+fOzamnnppFixblT3/6kxCB1dLKzvXNNtssp59++hL/6devX5U+J5xwwpd4ZVBdTfzdvv766+e73/1uJk2alBtvvLHK+A8++GDGjRuX9ddfP7vtttsqvhpYtpqY7xUryW6//fYqYyTJ9OnTc++99yaJNzewStUplUql2i6ClTNw4MD8/ve/zwYbbJDu3bunUaNG+ec//5kXX3wx++23X6644orK57579+6d0aNH54YbbkjXrl0rxxg7dmz69OmTxYsX5+CDD87GG2+cZ599NiNGjMiWW26ZO+64Q5DAamFl5/sNN9yQSy65JNttt126d+++1PN07dq12i8F8GWqib/bl2TKlCnZa6+90rFjxwwaNOjLuBT4QjUx3997770ceeSRmTRpUvbZZ5+0adMmEydOzH333Zf69evnmmuuSZcuXWrrEqHSys73UqmUX/ziFxk+fHhatGiR7t27Z/3118/bb7+dv/3tb5k5c2ZOOOGEnHLKKbV5mXzNebTha+CHP/xhvvWtb+Xmm2/OnXfemYULF2bLLbfMr3/96/Tq1avQ5nEdOnTIXXfdlauvvjoPP/xwZs+enZYtW+aHP/xhTjzxxMpNjKC2rex8f+2115Ik48ePz/jx45far0WLFoIEalVN/N0OXxU1Md/XW2+9/PWvf638b5l///vfady4cfbZZ5+ceOKJhfbPgS/Dys73OnXq5LLLLssee+yRIUOG5N57782cOXPSpEmT7LDDDjnqqKPsf8MqZ0UCAAAAUJg9EgAAAIDCBAkAAABAYYIEAAAAoDBBAgAAAFCYIAEAAAAoTJAAAAAAFCZIAAAAAAoTJAAAAACFCRIAYDk9/fTTOffcc3PggQfm29/+dtq2bZu99torvXv3zi233JIZM2ZU+8yUKVNSXl6e8vLyWqh4+ZxxxhkpLy/PVVddVeX4nDlzcuaZZ2bXXXfN9ttvnx49eiRJrrrqqpSXl+eMM86ohWqX7Kt0vwHgq6Z+bRcAAF8Vc+bMya9//es8+OCDSZLy8vJ85zvfydprr53p06dn1KhRGT16dK644opceOGF2WeffWq54hXTuXPnNGnSJDvuuGOV4zfddFOGDBmSRo0a5Xvf+1422GCDJMmOO+6YY445Jm3btq2NctOlS5d06dIlF1xwQeWxxo0b55hjjqmVegDg665OqVQq1XYRALC6mz9/fnr16pVnn30222yzTX7/+99X++I8c+bMDBgwIIMGDUqdOnVy+eWX57vf/W6ST38h32uvvZIkL7/88pdef0340Y9+lJEjR+bUU09N3759a7ucJMnbb7+dPfbYI4ccckiVIAEAWHU82gAABVxxxRV59tlns+mmm2bQoEFL/PW9efPmOfvss3PsscemVCrl/PPPz0cffVQL1a4a8+bNS5Kst956tVzJ/3nuuedquwQA+MaxIgEAvsCsWbOy5557Zu7cufnjH/9YubJgaebMmZM//elP2X///Suf0V/WioT3338/N910U/7zn/9k8uTJWbBgQVq0aJGddtopP/rRj5YYWowfPz5/+tOfMnbs2Lz33ntZY4010rJly+y555459thj07Jlyyr9n3jiidx6660ZN25cZsyYkcaNG2fDDTfMPvvsk2OOOSZNmzat7HvGGWdk6NCh+elPf5qTTjopV111VQYMGFCthk022SQjRoyobF/SqoC33347119/fUaOHJm33347a665Znbcccf0798/O+20U7Uxn3zyydx222155pln8v7776devXrZZJNN0q1bt/Tr1y9NmjSp7Luk/Q8qalrW/Z4/f37uvvvu3HfffXnttdfy8ccfp3nz5tlxxx1z9NFHp1OnTlX6DxkyJGeeeWb23HPP/PGPf8yf/vSnDB06NNOmTUv9+vXTrl27/OxnP1uuRzvmz5+fO++8Mw888EBeffXVzJkzJ40bN055eXmOPPLIHHDAAUv83PLez6L9n3jiiRxzzDGV9+/zltberVu3TJ06NX/5y1/y+OOPZ9CgQZk5c2aef/75yj6TJ0/OjTfemFGjRmXatGlZtGhR1l9//XTs2DEnnHBCttxyyyVe6yOPPJI77rgjzz33XD788MOsv/762XfffdOvX7+ss846Wbx4cb7zne/k7bffzmWXXZYDDzxwieOcddZZ+ctf/pIf/OAH+e1vf7vkPxAAlos9EgDgCzzyyCOZO3duNthgg3znO9/5wv5rr712fvaznxUae/r06fnBD36Q6dOnZ5NNNsl3v/vdNGjQIC+99FL+/ve/55///Geuu+66dOnSpfIzY8aMyXHHHZcFCxbk29/+drp27ZoFCxbk6aefzo033pjhw4fnjjvuyMYbb5wk+dvf/pZTTz019evXzy677JJNN900H3/8cUaPHp2rrroqDz30UO644440btx4iTVW7IHw97//PdOnT0/nzp2z9dZbp1mzZsu8tmeeeSZ9+/bNrFmzsssuu2S33XbL22+/nZEjR2bkyJH57W9/mx/84AeV/YcMGZJf//rXSZJvf/vb2XPPPTNnzpyMGjUqN9xwQ/71r3/lz3/+c9Zee+0kyTHHHJNx48Zl3Lhx2XrrrdO5c+cvrGnevHn50Y9+lNGjR6dJkybZeeeds9566+XNN9/MiBEj8vDDDy/z0Y1TTz01o0aNyh577JF27dpl9OjRefTRR/PUU0/lvvvuy6abbrrM8yfJokWL0q9fvzz++ONp0qRJdt999zRr1ixvvfVWHn300YwePToTJkyoNoeW934ub/+V8a9//Ss333xz9tlnnzRo0KDy+EsvvZSjjz46s2fPzjbbbJPu3bunVCrl2WefzT333JOHHnoogwcPzrbbbltlvEsvvTTXX399mjRpkq5du2bttdfOM888k4EDB+Zvf/tb/vKXv2TDDTfMIYcckmuuuSZDhgxZYpCwYMGCPPzww0mSnj171si1ApCkBAAs029+85tSWVlZ6ec///kKjzF58uRSWVlZqaysrMrxiy66qFRWVlb6/ve/X/rkk0+qtF1++eWlsrKy0kEHHVTl+PHHH18qKysrDR48uMrxxYsXl84777xSWVlZ6Xe/+13l8f32269UVlZW+s9//lOl//z580s/+clPSmVlZaVbbrml8vjpp59eKisrK1155ZVV+h999NGlsrKy0l//+tcqx6+88spSWVlZ6fTTT688Nm/evNJ3vvOdUllZWemuu+6q0v+xxx4rbbvttqXWrVuX3nrrrVKpVCotWrSo1Llz51JZWVnppptuqtJ/7ty5lddw4403fuG5S6Wl3+9LL720VFZWVtpvv/1K7733XpW2Rx55pFReXl7adtttS+PHj688/te//rVUVlZWateuXemQQw4pzZo1q0pt3bt3L5WVlZWuuOKKUhH//ve/S2VlZaX27duXJk+eXKXt8ccfL5WVlZW233770jvvvFN5fHnv5/L2HzVqVKmsrKz0ne98Z4k1L6294hydOnUqPfvss9U+97Of/axUVlZWOvHEE0uLFi2qPL548eLSaaedViorKyv169evymceffTRUllZWWm33XarrK9U+nSOnHHGGaWysrJSnz59SqVSqTRp0qTKP7Np06ZVO/+//vWvUllZWWnfffdd4nUBsGLskQAAX2D69OlJUujX5uXVtWvXnHPOOTnjjDOyxhprVGk74ogjkiSvvPJKPvzww8rjU6ZMSZK0b9++Sv86derk5z//ee68884qv6gvrX+DBg3ym9/8JnfffXcOPvjgmruofPoL9dSpU7PlllvmsMMOq9K222675YADDsi2226bF198McmnS/1/9atf5fTTT8+hhx5apf9aa62V733ve0k+XY2xohYsWJC77rorSfKrX/0q6667bpX2rl27Zq+99srixYtz9913V/v83Llz87//+79VHgNZa621st9++yUpvonmpptumvPOOy+/+c1vqs2pTp06ZbPNNsvChQszbty4yuPLez+Xt//KKi8vzw477FDt+MEHH5yzzjorJ598curW/b//7KxTp07laojP/5kOGjQoSXLsscdmo402qjxet27dnHTSSdl6660zb968fPzxx/nWt76VXXbZJYsXL87QoUOrnX/48OFJrEYAqGkebQCALzB37twkn35prGm77LJLdtlllyW2fXafg9mzZ1d+gd1yyy3zxhtv5Le//W3OO++8bLHFFpX9GjduXC0w2HLLLfPKK6/krLPOyq9//esq46677rrVvlDXhCeeeCJJsvPOOy+x/dJLL63y72uuueYyw4yKmmfPnr3CNU2YMCEzZ85MvXr1svvuuy+xz2677ZaHH344Tz/9dLW2hg0bLnEfhIrXYBatbeutt87WW2+91PaWLVtm0qRJVcZb3vu5vP1X1tLm8LIeBVrSn2mpVKqsvUOHDtU+s/HGG1eGAxUOPfTQjBo1KkOHDs2JJ55YeXz+/Pn55z//mbp166ZHjx6FrwWALyZIAIAvULF3wKp6A8Po0aNzxx135IUXXsgHH3ywxPOUPrM38q9//eu88MILGT16dPbbb7+Ul5enU6dO2X333bPLLrtUeUY9Sc4///wcd9xxefDBB/PQQw9lhx12qOy/0047VfmluKZMnTo1Sapt+rgs8+fPz+DBg/PQQw9l8uTJ+eCDD7JgwYIaq6liZcb666+fhg0bLrHPJptskiSZNm1atbaWLVumTp061Y7Xq1cvSdU/oy8yadKkDBw4ME8++WTeeeedzJo1q9rnP/vvy3s/V+T+r4xlhVEPP/xw/vKXv+SVV17JjBkzKoO5Jflse9Ha99133zRt2jRvvvlmxowZUxmePPLII/noo4/SpUuXL+0+AHxTCBIA4AtUbFo4ceLEGh/7tttuy29/+9uUSqW0bNkyXbt2TYsWLSq/nN56663VPrPZZptl2LBhlRvPvfzyy3n55ZczcODArLPOOunfv3969+5d2b9t27a5//77c+ONN+bvf/975QaF1157bTbZZJP88pe/XOpbAlZUxasi69cv9p8a8+fPz7HHHpunnnoqderUSdu2bbPbbrtVvqXhtddey2OPPbZSNX388cdJPl39sDQVj5d88skn1dqKXssXeeaZZ3L88cdnzpw5WXvttdOxY8dssMEGleeu2NTys5b3fi5v/5XVqFGjJR6/8MILc9NNNyX5dN7utddeadasWerWrZuPPvooQ4YMqdJ//vz5lf/784HY0qyxxhrp3r17br/99vz1r3+tDBIqVi58//vfX+7rAWDZBAkA8AXat2+fgQMHZsyYMfnkk0+W+UW0wvz585f6q3eFDz74IBdccEFKpVJOOumk/OQnP6nyi/fixYuXGCQkSYsWLXLKKafklFNOyZtvvplHH300DzzwQJ588sn89re/zaJFi/LDH/6wsv9GG22Us88+O2effXZeeeWVjBw5Mvfff39efPHFnHLKKWnYsGH23nvvYjekgIrHQD67t8OyDB48OE899VSaNGmS2267rdou/n/5y19WOkio+LJbESgsSUVbxZshVoVzzz03c+bMyR577JErrrii2iMzL774YrUgYXnv5/L2/yILFy5c7s+89NJLlSHC73//+2r7FEyePLlakPDZezFr1qzCKwkOPfTQ3H777fn73/+ec889N4sWLcq///3vNG3atEbnNQCfstkiAHyBrl27pmnTppk9e3a1Lz5LsmjRohx22GE59dRT88477yy139NPP50FCxakSZMmOfHEE6stm588eXKh+jbffPP06tUrt912W/7f//t/SZLbb799qf3LysrSp0+fDB06NP369Uvy6cqImvStb30rSap9IV6aJ598Msmnm/N9PkRIit+LIjW9++67S1xx8NnzrIqNNZNPH48ZP358kuTkk09e4r4bS7rW5b2fy9u/4vGWRYsWLbG94lGJ5TF69Ogkn863JW12uKTrbNasWeUrPIvWniTbb799WrdunTlz5uSRRx7JP//5z8ydOzcHHnjgFwZ6ACw/QQIAfIFGjRrl+OOPT5Jcfvnlef3115fZ/7LLLsv48eMzduzYpS75Tv7vS1vjxo0rH2X4rMGDB1f+74rn5WfNmpUHHngg//nPf5Y45kEHHZTk/76EvfPOO7n33nvz1FNPFepfUyo23xs9evQS9zk45ZRTsv322+ePf/xjkv/7xfuzb0So8PHHH+fee+9NsvR9CIrsT9CqVau0aNEiixcvXur9GzlyZJKkY8eOXzjeivjsL/sVX5g/a8SIEZV/Fp+9puW9n8vbv2IFxowZM5a4+uDRRx8tdoGfUTG/l/Rnmix5fif/d++XtAJl9uzZadu2bVq3bp233nqrSlvFIwz3339/7rvvviTe1gCwqggSAKCAvn37Ztddd82HH36Yo48+Og8//HC1Ph988EHOOeec/OlPf0qjRo1y0UUXVW7UuCQVO/e//fbblb9SVxg8eHBGjhyZddZZp7JPkrz33ns55ZRTcvrpp+e1116rNmbFc+Hbbbddkk9fSXjaaaflzDPPXOLqiM/3ryldu3bN5ptvnnfffTdXXXVVlbZnnnkmDz/8cOrWrZsDDzwwyf/di5EjR1b5VXzWrFn5+c9/XrkJYsV9qFDxBfjzXyqXpF69epV7R1x66aX54IMPqrQ/+OCDGTlyZNZcc81qr0ysKc2bN6/cmPBf//pXlbYxY8bk//2//5c2bdokqbrh4/Lez+Xtv8UWW6RBgwaZN29e5ZfwCsOHD1+hx0q22mqrJNUf1Vi4cGGuuOKKvPvuu5UrIT57rUcffXSS5K677qryespSqZQrr7wy8+bNy0477VS5d0mF7t27Z4011sgjjzySxx57LNtss80S37IBwMqzRwIAFFCvXr1cf/31OffcczN06ND85Cc/yaabbpp27dqladOmmT59ev773/9m7ty52WijjXL11VendevWyxxz6623zne+853861//Sq9evbLXXnulYcOGeeqpp/Lee+/l5ptvzpVXXplHHnkkZ599dnbdddf85je/Sb9+/XLttdfm4IMPTseOHbPppptm8eLFee211/L000+nUaNGOf3005Mku+++e7p375777rsv++67b3bddde0bNkyCxYsyPjx4/Piiy9mnXXWycknn1yj96tBgwa55JJLcvzxx+e6667L448/nu233z7vvPNOZVhw5plnZvPNN0/y6ZfHO+64I88991wOPvjg7LTTTpk5c2Yee+yxtGnTJhdffHH23nvvTJ48OSeccEL233//9OjRo/JL9+jRo3PEEUdUnndp+vbtm7Fjx+axxx7Ld7/73XTs2DFNmzatvHf169fPeeedt8oebUiSH//4x7ngggvy+9//Po8//nhatmyZV199NU8//XR+85vfZNasWXn++edz880355133skPf/jDbLXVVst1P5f3/jdq1CiHHnpoBg8enF//+td58MEHs9FGG+WNN97ImDFjcu655+ass85aruvs0qVLtttuu4wfPz6HHHJI9thjj8rXO5ZKpdx2223p169fXn311fzkJz/JHnvskZ///OfZddddc8IJJ+Taa6/NEUcckT322CMtWrTIM888k5dffjnrrbdezjvvvGrna9q0afbdd9/KIOSQQw5ZyT8pAJZGkAAABa2xxhr5/e9/n169emXo0KEZNWpUHnnkkXz88cdp2rRp2rVrl3322Sc9e/YstCFjklx00UW55JJLMmLEiDzwwANZf/31s+uuu6Zfv37ZYost8otf/CLTpk3L66+/XrmPwCmnnJI2bdrkr3/9a1588cWMGTMmpVIpG2+8cQ477LAcf/zx2WKLLSrPcfHFF6dTp06577778vzzz+fRRx9N/fr1s+mmm+a4447L8ccfnw022KDG71fbtm0zbNiwXHvttXn00UczZMiQrLXWWtl1113zox/9KJ06darsu9FGG+XGG2/MH/7whzz33HOZOnVqvvWtb+WEE07Isccem4YNG+aXv/xlrr/++owaNSo77LBDkk+XwZ9wwgmVv15vtNFGqVev3lJfG9mgQYNcd911+fOf/5z77rsvjz32WObPn59111033bt3z/HHH5/tt9++xu/FZx177LFZtGhR7r777jz66KNp2rRptt9++9x4443p1KlTZs+enVGjRuXJJ5/MiBEjctRRRy33/VyR/meddVZatGhReV/WXnvttGnTJgMHDlyhYKVevXq57rrrctFFF+XRRx/N/fffn5YtW2avvfZK3759s8EGG+Tss8/OOeeckwkTJlSuvkk+nePt2rXLbbfdltGjR2fOnDlZf/31c9RRR+XEE09c6nw99NBDc99996VevXo5+OCDl7tmAIqpU1qelx4DAMBq6pFHHknfvn2z33775corr6ztcgC+tuyRAADA18I111yT5NNVHwCsOoIEAAC+8q688so8/fTT6dSpUzp06FDb5QB8rXm0AQCAr6Snn346999/f8aNG5fnnnsu66yzTu6+++5VulkmAFYkAADwFTV16tTcfvvtee211/Kd73wnd955pxAB4EtgRQIAAABQmBUJAAAAQGGCBAAAAKAwQQIAAABQmCABAAAAKEyQAAAAABQmSAAAAAAK+//osHOLIzChCQAAAABJRU5ErkJggg==\n", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", + "import seaborn\n", + "plt.style.use('seaborn-white')\n", + "seaborn.set_context('poster')\n", "mean_scores = [np.mean(scores[kind]) for kind in kinds]\n", "print(list(zip(mean_scores, kinds) ))\n", "scores_std = [np.std(scores[kind]) for kind in kinds]\n", "\n", - "plt.figure(figsize=(10, 8))\n", + "plt.figure(figsize=(15, 10))\n", "positions = np.arange(len(kinds)) * .1 + .1\n", "plt.barh(positions, mean_scores, align='center', height=.05, xerr=scores_std)\n", "yticks = [k.replace(' ', '\\n') for k in kinds]\n", "plt.yticks(positions, yticks)\n", "plt.gca().grid(True)\n", "plt.gca().set_axisbelow(True)\n", - "plt.gca().axvline(.8, color='red', linestyle='--')\n", - "plt.xlabel('Classification accuracy\\n(red line = chance level)')\n", - "plt.tight_layout()" + "plt.xlabel('Classification accuracy')\n", + "plt.tight_layout()\n", + "plt.savefig('accuracy.png', bbox_inches=\"tight\", dpi=300)" ] }, { From e8c86b77e425049d16eeb4d8781448c6b6373a15 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood Date: Tue, 20 Jul 2021 20:28:56 +0100 Subject: [PATCH 18/19] 0.14.0 version bump --- Cargo.toml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 78d7910..2dad7fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rust-dtw" -version = "0.1.13" +version = "0.1.14" authors = ["Christopher Fleetwood"] edition = "2018" description = "A rust implementation of dynamic time warping with python bindings!" diff --git a/pyproject.toml b/pyproject.toml index f67419e..1b1a924 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ build-backend = "maturin" [tool.poetry] name = "rust-dtw" -version = "0.1.13" +version = "0.1.14" description = "A rust implementation of dynamic time warping with python bindings!" license = "MIT" readme = "README.md" From 37ab548c34e041d969083ac1f1ae84d3d941c014 Mon Sep 17 00:00:00 2001 From: Christopher Fleetwood <45471420+FL33TW00D@users.noreply.github.com> Date: Tue, 20 Jul 2021 20:36:34 +0100 Subject: [PATCH 19/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb6a588..93ac327 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ poetry run pytest

-The above shows the performance of the rustdtw implementation vs the DTAIDistance OpenMP Python version (more benchmarks vs C implementation coming soon). +The above shows the performance of the rustdtw implementation vs the DTAIDistance OpenMP Python version, showing a ~10x speed improvement. ## ⚠️ License