From 230c6ff61780fb4c7555913ad6f54ab1c0b44fcd Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 12 Feb 2025 14:38:24 +0100 Subject: [PATCH] docs: refactoring the qmatchatea intro notebook --- examples/qmatchatea_intro/qibojit_errs.npy | Bin 232 -> 0 bytes examples/qmatchatea_intro/qibojit_times.npy | Bin 232 -> 0 bytes examples/qmatchatea_intro/qmatcha_errs.npy | Bin 520 -> 0 bytes examples/qmatchatea_intro/qmatcha_times.npy | Bin 520 -> 0 bytes .../qmatchatea_introduction.ipynb | 550 +++++------------- 5 files changed, 146 insertions(+), 404 deletions(-) delete mode 100644 examples/qmatchatea_intro/qibojit_errs.npy delete mode 100644 examples/qmatchatea_intro/qibojit_times.npy delete mode 100644 examples/qmatchatea_intro/qmatcha_errs.npy delete mode 100644 examples/qmatchatea_intro/qmatcha_times.npy diff --git a/examples/qmatchatea_intro/qibojit_errs.npy b/examples/qmatchatea_intro/qibojit_errs.npy deleted file mode 100644 index 50a5db285ebf8b1d89d20523c5c6f1427bee0abe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$-#yXlh3bhL411<&-Xn1A^rWbsM&bXPjIBrY|g7Vh;cl%Pu_t diff --git a/examples/qmatchatea_intro/qibojit_times.npy b/examples/qmatchatea_intro/qibojit_times.npy deleted file mode 100644 index 6b80df624287941403287edb3212edfb8ee14967..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 232 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$-#yXlh3bhL411<&-P%*a$(+LNJ|CF&|8CI5Y`NJNQA& z^QfN=<{M-|^j%m6G50_hM87~5L|>Z62CzDTY>2)at0sW?8MU*)w8pf#U|Pg;8JK>O zu?S4Bh~5IG=j?~@J61!?JHfjNEZ%Z?C75QoupUgaxUB`#B40Lx=@Z%8!Su965OdyT rZUysCv2O&^0S6)KYEG{P^BbC$gK3{>tH5+g_Y#P@jgW9~0EPzuFdTIH diff --git a/examples/qmatchatea_intro/qmatcha_times.npy b/examples/qmatchatea_intro/qmatcha_times.npy deleted file mode 100644 index a1540fd8b363be39ec2c88e0a070036d964e4c3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I#ymO7d`3bhL411<(2xDv3;9z^d5TnDC48EppBHr-pn^fSr*V0z=pgJ61k zD^a@ut;{YUy1Sp4np+hG2tUH3t>1GmOQFn_^nFzwKP z<}p~^5}T)B@y3j2Ao{>Ah8G~(;lA8UFrDlC8Z18{_6=CxR^=UtZeY0zrXMgmeE{(l z4hw$-(GF`Tg6RbQ^`Aie1&K30gJ}WVFCh9s$zupTP5CQ`uTVJS8;EB3;`|*%C)|Gm zrWdqM{{dEiuK5>;cF61e4W`Sw!1Mz(-oGGz!>$WpnxUlnADExz^B+Vr#PBgV0I3fz W_k!sIJ|c`@{^v$8eL(3yqXPgFvyq1Y diff --git a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb index 645ab1c..6a338e2 100644 --- a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb +++ b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "6722d94e-e311-48f9-b6df-c6d829bf67fb", "metadata": {}, "outputs": [], @@ -36,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "64162116-1555-4a68-811c-01593739d622", "metadata": {}, "outputs": [], @@ -61,7 +61,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "4a22a172-f50d-411d-afa3-fa61937c7b3a", "metadata": {}, "outputs": [], @@ -80,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "76f23c57-6d08-496b-9a27-52fb63bbfcb1", "metadata": {}, "outputs": [ @@ -102,7 +102,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "07b2c097-cea2-42ec-8f1d-b4bbb5b71d98", "metadata": {}, "outputs": [], @@ -120,76 +120,18 @@ "source": [ "#### Setting up the tensor network simulator\n", "\n", - "Depending on the simulator, various parameters can be set. In the case of Quantum Matcha, we direclty exploit the `qmatchatea` interface and we construct a `QCConvergenceParameters` object (for more info [this is the link to the qmatchatea code](https://baltig.infn.it/quantum_matcha_tea/py_api_quantum_matcha_tea/-/blob/master/qmatchatea/utils/utils.py?ref_type=heads#L540))." + "Depending on the simulator, various parameters can be set. One can customize the tensor network execution via the `backend.configure_tn_simulation` function, whose face depends on the specific backend provider." ] }, { "cell_type": "code", - "execution_count": 10, - "id": "34452bfd-2287-4b38-8099-e072239eab74", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mSignature:\u001b[0m\n", - "\u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfigure_tn_simulation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mansatz\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'MPS'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mmax_bond_dimension\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mcut_ratio\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mfloat\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1e-09\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mtrunc_tracking_mode\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'C'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0msvd_control\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'A'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mini_bond_dimension\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m\n", - "Configure TN simulation given Quantum Matcha Tea interface.\n", - "\n", - "Args:\n", - " ansatz (str): tensor network ansatz. It can be tree tensor network \"TTN\"\n", - " or Matrix Product States \"MPS\" (default).\n", - " max_bond_dimension : int, optional Maximum bond dimension of the problem. Default to 10.\n", - " cut_ratio : float, optional\n", - " Cut ratio for singular values. If :math:`\\lambda_n/\\lambda_1 <` cut_ratio then\n", - " :math:`\\lambda_n` is neglected. Default to 1e-9.\n", - " trunc_tracking_mode : str, optional\n", - " Modus for storing truncation, 'M' for maximum, 'C' for\n", - " cumulated (default).\n", - " svd_ctrl : character, optional\n", - " Control for the SVD algorithm. Available:\n", - " - \"A\" : automatic. Some heuristic is run to choose the best mode for the algorithm.\n", - " - \"V\" : gesvd. Safe but slow method.\n", - " - \"D\" : gesdd. Fast iterative method. It might fail. Resort to gesvd if it fails\n", - " - \"E\" : eigenvalue decomposition method. Faster on GPU. Available only when\n", - " contracting the singular value to left or right\n", - " - \"X\" : sparse eigenvalue decomposition method. Used when you reach the maximum\n", - " bond dimension.\n", - " - \"R\" : random svd method. Used when you reach the maximum bond dimension.\n", - " Default to 'A'.\n", - " ini_bond_dimension: int, optional\n", - " Initial bond dimension of the simulation. It is used if the initial state is random.\n", - " Default to 1.\n", - "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", - "\u001b[0;31mType:\u001b[0m method\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# And then call the dedicate configuration function\n", - "# which is especially written to match the QuantumMatchaTea requirements\n", - "qmatcha_backend.configure_tn_simulation?" - ] - }, - { - "cell_type": "code", - "execution_count": 13, + "execution_count": 6, "id": "2ee03e94-d794-4a51-9e76-01e8d8a259ba", "metadata": {}, "outputs": [], "source": [ - "# Let's set a simple customization\n", + "# Customization of the tensor network simulation in the case of qmatchatea\n", + "# Here we use only some of the possible arguments\n", "qmatcha_backend.configure_tn_simulation(\n", " ansatz=\"MPS\",\n", " max_bond_dimension=10,\n", @@ -205,77 +147,14 @@ "#### Executing through the backend\n", "\n", "The `backend.execute_circuit` method can be used then. We can simulate results in three ways:\n", - "1. reconstruction of the final state (statevector like, only if `nqubits < 20` due to Quantum Matcha Tea setup);\n", + "1. reconstruction of the final state (statevector like, only if `nqubits < 20` due to Quantum Matcha Tea setup) only if `return_array` is set to `True`;\n", "2. computation of the relevant probabilities of the final state. There are three way of doing so, but we will see it directly into the docstrings;\n", "3. reconstruction of the relevant state's frequencies (only if `nshots` is not `None`)." ] }, { "cell_type": "code", - "execution_count": 14, - "id": "221ef886-5578-4200-a019-dcafa51aada3", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\u001b[0;31mSignature:\u001b[0m\n", - "\u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute_circuit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mprob_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mreturn_array\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mprob_kwargs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m\n", - "Execute a Qibo quantum circuit using tensor network simulation.\n", - "\n", - "This method returns a ``TensorNetworkResult`` object, which provides:\n", - " - Reconstruction of the system state (if the system size is < 20).\n", - " - Frequencies (if the number of shots is specified).\n", - " - Probabilities computed using various methods.\n", - "\n", - "The following probability computation methods are available, as implemented\n", - "in Quantum Matcha Tea:\n", - " - **\"E\" (Even):** Probabilities are computed by evenly descending the probability tree,\n", - " pruning branches (states) with probabilities below a threshold.\n", - " - **\"G\" (Greedy):** Probabilities are computed by following the most probable states\n", - " in descending order until reaching a given coverage (sum of probabilities).\n", - " - **\"U\" (Unbiased):** An optimal probability measure that is unbiased and designed\n", - " for best performance. See https://arxiv.org/abs/2401.10330 for details.\n", - "\n", - "Args:\n", - " circuit: A Qibo circuit to execute.\n", - " initial_state: The initial state of the system (default is the vacuum state\n", - " for tensor network simulations).\n", - " nshots: The number of shots for shot-noise simulation (optional).\n", - " prob_type: The probability computation method. Must be one of:\n", - " - \"E\" (Even)\n", - " - \"G\" (Greedy)\n", - " - \"U\" (Unbiased) [default].\n", - " prob_kwargs: Additional parameters required for probability computation:\n", - " - For \"U\", requires ``num_samples``.\n", - " - For \"E\" and \"G\", requires ``prob_threshold``.\n", - "\n", - "Returns:\n", - " TensorNetworkResult: An object with methods to reconstruct the state,\n", - " compute probabilities, and generate frequencies.\n", - "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", - "\u001b[0;31mType:\u001b[0m method\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "qmatcha_backend.execute_circuit?" - ] - }, - { - "cell_type": "code", - "execution_count": 17, + "execution_count": 7, "id": "35a244c3-adba-4b8b-b28c-0ab592b0f7cf", "metadata": {}, "outputs": [ @@ -285,43 +164,36 @@ "{'nqubits': 4,\n", " 'backend': QMatchaTeaBackend(),\n", " 'measures': None,\n", - " 'measured_probabilities': {'U': {'0000': (0.0, 0.009075021799813076),\n", - " '0001': (0.009075021799813076, 0.013505513590974613),\n", - " '0010': (0.01350551359097461, 0.03969337383808732),\n", - " '0011': (0.03969337383808732, 0.3501239760030636),\n", - " '0100': (0.3501239760030635, 0.3525850615313512),\n", - " '0101': (0.3525850615313512, 0.37831917247243874),\n", - " '0110': (0.37831917247243874, 0.5888353440545037),\n", - " '0111': (0.5888353440545037, 0.5934703289984129),\n", - " '1000': (0.5934703289984128, 0.6896713158046865),\n", - " '1001': (0.6896713158046865, 0.7110042815132214),\n", - " '1010': (0.7110042815132213, 0.7242989335460854),\n", - " '1011': (0.7242989335460854, 0.7640693817193864),\n", - " '1100': (0.7640693817193864, 0.7641799598480784),\n", - " '1101': (0.7641799598480784, 0.9530705920006985),\n", - " '1110': (0.9530705920006985, 0.989871146703016),\n", - " '1111': (0.989871146703016, 1.0000000000000002)},\n", + " 'measured_probabilities': {'U': {'0000': (0.0, 0.08390937969317301),\n", + " '0001': (0.08390937969317301, 0.08858639088838134),\n", + " '0010': (0.08858639088838131, 0.1832549957082757),\n", + " '0011': (0.1832549957082757, 0.25896776804349736),\n", + " '0100': (0.2589677680434974, 0.33039716334036867),\n", + " '0101': (0.33039716334036867, 0.386620221067355),\n", + " '0110': (0.3866202210673549, 0.4380808691410473),\n", + " '0111': (0.4380808691410473, 0.47837271988834),\n", + " '1000': (0.47837271988834, 0.5916815553716759),\n", + " '1001': (0.5916815553716759, 0.5972581739037379),\n", + " '1010': (0.5972581739037378, 0.6359857590550054),\n", + " '1011': (0.6359857590550054, 0.6894851559808782),\n", + " '1100': (0.6894851559808783, 0.7030911408535467),\n", + " '1101': (0.7030911408535467, 0.8264027395524797),\n", + " '1110': (0.8264027395524797, 0.8981519382820797),\n", + " '1111': (0.8981519382820797, 0.9999999999999998)},\n", " 'E': [None],\n", " 'G': [None]},\n", " 'prob_type': 'U',\n", - " 'statevector': array([-0.0381929 -0.08727155j, -0.25811667+0.17197899j,\n", - " -0.04960402-0.00072563j, 0.00323629-0.01000523j,\n", - " 0.0625107 +0.14926578j, -0.11530027-0.00070762j,\n", - " -0.35885958-0.28589503j, -0.08233943+0.17326504j,\n", - " 0.06345512-0.02009824j, 0.03609859+0.14152688j,\n", - " -0.11045046+0.11633919j, -0.27587224+0.33583499j,\n", - " 0.46347251+0.30923103j, 0.02322259-0.19806857j,\n", - " -0.00893755+0.06749152j, -0.07071717-0.0716096j ])}" + " 'statevector': None}" ] }, - "execution_count": 17, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Simple execution (defaults)\n", - "outcome = qmatcha_backend.execute_circuit(circuit=circuit, return_array=True)\n", + "outcome = qmatcha_backend.execute_circuit(circuit=circuit)\n", "\n", "# Print outcome\n", "vars(outcome)" @@ -329,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 8, "id": "60501c3d-2a44-421f-b434-4a508714b132", "metadata": {}, "outputs": [ @@ -341,24 +213,22 @@ " 'measures': None,\n", " 'measured_probabilities': {'U': [None],\n", " 'E': [None],\n", - " 'G': {'0010': 0.02618786024711271,\n", - " '0011': 0.3104306021649763,\n", - " '1100': 0.00011057812869196484,\n", - " '1101': 0.18889063215262014,\n", - " '0110': 0.21051617158206493,\n", - " '0111': 0.0046349849439092155}},\n", + " 'G': {'1110': 0.07174919872960005,\n", + " '1111': 0.10184806171792007,\n", + " '0010': 0.09466860481989439,\n", + " '0011': 0.07571277233522165}},\n", " 'prob_type': 'G',\n", - " 'statevector': array([-0.0381929 -0.08727155j, -0.25811667+0.17197899j,\n", - " -0.04960402-0.00072563j, 0.00323629-0.01000523j,\n", - " 0.0625107 +0.14926578j, -0.11530027-0.00070762j,\n", - " -0.35885958-0.28589503j, -0.08233943+0.17326504j,\n", - " 0.06345512-0.02009824j, 0.03609859+0.14152688j,\n", - " -0.11045046+0.11633919j, -0.27587224+0.33583499j,\n", - " 0.46347251+0.30923103j, 0.02322259-0.19806857j,\n", - " -0.00893755+0.06749152j, -0.07071717-0.0716096j ])}" + " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", + " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", + " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", + " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", + " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", + " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", + " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", + " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" ] }, - "execution_count": 19, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -366,10 +236,11 @@ "source": [ "# Execution with a specific probability type\n", "# We use here \"E\", which is cutting some of the components if under a threshold\n", + "# We also retrieve the statevector\n", "outcome = qmatcha_backend.execute_circuit(\n", " circuit=circuit,\n", " prob_type=\"G\",\n", - " prob_threshold=0.7,\n", + " prob_threshold=0.3,\n", " return_array=True,\n", ")\n", "\n", @@ -389,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 9, "id": "c0443efc-21ef-4ed5-9cf4-785d204a1881", "metadata": {}, "outputs": [ @@ -398,16 +269,15 @@ "output_type": "stream", "text": [ "Probabilities:\n", - " [2.61878602e-02 3.10430602e-01 1.10578129e-04 1.88890632e-01\n", - " 2.10516172e-01 4.63498494e-03]\n", + " [0.0717492 0.10184806 0.0946686 0.07571277]\n", "\n", "State:\n", - " [-0.0381929 -0.08727155j -0.25811667+0.17197899j -0.04960402-0.00072563j\n", - " 0.00323629-0.01000523j 0.0625107 +0.14926578j -0.11530027-0.00070762j\n", - " -0.35885958-0.28589503j -0.08233943+0.17326504j 0.06345512-0.02009824j\n", - " 0.03609859+0.14152688j -0.11045046+0.11633919j -0.27587224+0.33583499j\n", - " 0.46347251+0.30923103j 0.02322259-0.19806857j -0.00893755+0.06749152j\n", - " -0.07071717-0.0716096j ]\n", + " [ 0.08809627-0.27595005j 0.24859731-0.22695421j 0.18807826+0.18988408j\n", + " 0.09444097+0.06846085j 0.00470148+0.30764671j 0.17371395-0.09247188j\n", + " -0.18900305+0.12545316j -0.17359753+0.20399288j -0.0517478 +0.04471215j\n", + " -0.0411739 -0.06230031j 0.22377064+0.07842041j -0.21784975-0.27541439j\n", + " -0.27208941+0.04098933j -0.22748127+0.04185292j 0.17105258-0.10503745j\n", + " -0.01729753-0.31866731j]\n", "\n" ] } @@ -427,16 +297,6 @@ "But frequencies cannot be accessed, since no shots have been set." ] }, - { - "cell_type": "code", - "execution_count": 21, - "id": "8a868f3e-8383-47ee-a93a-27c5f85e48c5", - "metadata": {}, - "outputs": [], - "source": [ - "# print(f\"Frequencies:\\n {outcome.frequencies()}\\n\")" - ] - }, { "cell_type": "markdown", "id": "8e9413c7-602a-44ed-a50c-1c3dd4dd7494", @@ -449,7 +309,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 10, "id": "68122cd3-662f-4fd1-bb9c-d33b6f5448dd", "metadata": {}, "outputs": [ @@ -458,33 +318,47 @@ "text/plain": [ "{'nqubits': 4,\n", " 'backend': QMatchaTeaBackend(),\n", - " 'measures': {'0000': 12,\n", - " '0001': 5,\n", - " '0010': 20,\n", - " '0011': 307,\n", - " '0100': 5,\n", - " '0101': 35,\n", - " '0110': 207,\n", - " '0111': 4,\n", - " '1000': 107,\n", - " '1001': 19,\n", - " '1010': 11,\n", - " '1011': 41,\n", - " '1100': 1,\n", - " '1101': 199,\n", - " '1110': 37,\n", - " '1111': 14},\n", + " 'measures': {'0000': 92,\n", + " '0001': 7,\n", + " '0010': 85,\n", + " '0011': 79,\n", + " '0100': 81,\n", + " '0101': 55,\n", + " '0110': 47,\n", + " '0111': 39,\n", + " '1000': 117,\n", + " '1001': 7,\n", + " '1010': 38,\n", + " '1011': 53,\n", + " '1100': 22,\n", + " '1101': 129,\n", + " '1110': 74,\n", + " '1111': 99},\n", " 'measured_probabilities': {'U': [None],\n", - " 'E': {'0011': 0.3104306021649763,\n", - " '0110': 0.21051617158206493,\n", - " '1000': 0.09620098680627374,\n", - " '1101': 0.18889063215262014},\n", + " 'E': {'0000': 0.08390937969317301,\n", + " '0010': 0.09466860481989439,\n", + " '0011': 0.07571277233522165,\n", + " '0100': 0.07142939529687124,\n", + " '0101': 0.05622305772698632,\n", + " '0110': 0.05146064807369245,\n", + " '1000': 0.11330883548333581,\n", + " '1011': 0.053499396925872765,\n", + " '1101': 0.12331159869893296,\n", + " '1110': 0.07174919872960005,\n", + " '1111': 0.10184806171792007},\n", " 'G': [None]},\n", " 'prob_type': 'E',\n", - " 'statevector': None}" + " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", + " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", + " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", + " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", + " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", + " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", + " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", + " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" ] }, - "execution_count": 22, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -497,6 +371,7 @@ " nshots=1024,\n", " prob_type=\"E\",\n", " prob_threshold=0.05,\n", + " return_array=True\n", ")\n", "\n", "# Print outcome\n", @@ -505,7 +380,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 11, "id": "ef0e9591-ccca-4cdd-a81b-2bfb3caaf3d0", "metadata": {}, "outputs": [ @@ -514,140 +389,28 @@ "output_type": "stream", "text": [ "Frequencies:\n", - " {'0000': 12, '0001': 5, '0010': 20, '0011': 307, '0100': 5, '0101': 35, '0110': 207, '0111': 4, '1000': 107, '1001': 19, '1010': 11, '1011': 41, '1100': 1, '1101': 199, '1110': 37, '1111': 14}\n", + " {'0000': 92, '0001': 7, '0010': 85, '0011': 79, '0100': 81, '0101': 55, '0110': 47, '0111': 39, '1000': 117, '1001': 7, '1010': 38, '1011': 53, '1100': 22, '1101': 129, '1110': 74, '1111': 99}\n", + "\n", + "Probabilities:\n", + " [0.08390938 0.0946686 0.07571277 0.0714294 0.05622306 0.05146065\n", + " 0.11330884 0.0534994 0.1233116 0.0717492 0.10184806]\n", + "\n", + "State:\n", + " [ 0.08809627-0.27595005j 0.24859731-0.22695421j 0.18807826+0.18988408j\n", + " 0.09444097+0.06846085j 0.00470148+0.30764671j 0.17371395-0.09247188j\n", + " -0.18900305+0.12545316j -0.17359753+0.20399288j -0.0517478 +0.04471215j\n", + " -0.0411739 -0.06230031j 0.22377064+0.07842041j -0.21784975-0.27541439j\n", + " -0.27208941+0.04098933j -0.22748127+0.04185292j 0.17105258-0.10503745j\n", + " -0.01729753-0.31866731j]\n", "\n" ] } ], "source": [ - "# And then finally access frequencies!\n", - "print(f\"Frequencies:\\n {outcome.frequencies()}\\n\")" - ] - }, - { - "cell_type": "markdown", - "id": "7fe725ff-533e-4648-9f8d-9cb6c0826354", - "metadata": {}, - "source": [ - "#### Testing the performances" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "9e9632d5-28dd-4846-8457-c579d0cb9453", - "metadata": {}, - "outputs": [], - "source": [ - "qmatcha_range = range(2, 100, 2)\n", - "qibojit_threshold = 28\n", - "qibojit_range = range(2, qibojit_threshold, 2)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4e6b1126-32ab-418d-b760-44ff62048f70", - "metadata": {}, - "outputs": [], - "source": [ - "qmatcha_times, qmatcha_errs = [], []\n", - "qibojit_times, qibojit_errs = [], []\n", - "\n", - "qibojit_backend = construct_backend(\"qibojit\", platform=\"numba\")\n", - "qibo.set_threads(2)\n", - "\n", - "\n", - "for q in qmatcha_range:\n", - " print(f\"Executing for {q} qubits\")\n", - " # Build the circuit\n", - " circuit = build_circuit(q, 1)\n", - "\n", - " reasonable_exp = min(13, int(q / 2))\n", - " \n", - " qmatcha_backend.configure_tn_simulation(\n", - " convergence_params=QCConvergenceParameters(max_bond_dimension=2**reasonable_exp),\n", - " ansatz=\"MPS\"\n", - " )\n", - "\n", - " tmp_qmatcha_times, tmp_qibojit_times = [], [] \n", - " for i in range(20):\n", - " # Set random parameters\n", - " circuit.set_parameters(parameters=np.random.uniform(-np.pi, np.pi, len(circuit.get_parameters())))\n", - " \n", - " # Measure QMatcha execution time\n", - " print(f\"1. ---- QMatcha\") if i == 0 else None\n", - " start_time = time.time()\n", - " outcome = qmatcha_backend.execute_circuit(circuit=circuit)\n", - " tmp_qmatcha_times.append(time.time() - start_time)\n", - "\n", - " if q < qibojit_threshold:\n", - " \n", - " if i == 0:\n", - " outcome = qibojit_backend.execute_circuit(circuit=circuit)\n", - " \n", - " # Measure Qibojit execution time\n", - " print(f\"2. ---- Qibojit\") if i == 0 else None\n", - " start_time = time.time()\n", - " outcome = qibojit_backend.execute_circuit(circuit=circuit)\n", - " tmp_qibojit_times.append(time.time() - start_time)\n", - " \n", - " qmatcha_times.append(np.median(tmp_qmatcha_times))\n", - " qmatcha_errs.append(stats.median_abs_deviation(tmp_qmatcha_times))\n", - " \n", - " if q < qibojit_threshold:\n", - " qibojit_times.append(np.median(tmp_qibojit_times))\n", - " qibojit_errs.append(stats.median_abs_deviation(tmp_qibojit_times))" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "5620f476-1bc7-4cf8-9868-b127c715b6ec", - "metadata": {}, - "outputs": [], - "source": [ - "# Saving data\n", - "# np.save(arr=qmatcha_times, file=\"qmatcha_times\")\n", - "# np.save(arr=qibojit_times, file=\"qibojit_times\")\n", - "# np.save(arr=qmatcha_errs, file=\"qmatcha_errs\")\n", - "# np.save(arr=qibojit_errs, file=\"qibojit_errs\")\n", - "\n", - "qmatcha_times = np.load(\"qmatcha_times.npy\")\n", - "qmatcha_errs = np.load(\"qmatcha_errs.npy\")\n", - "qibojit_times = np.load(\"qibojit_times.npy\")\n", - "qibojit_errs = np.load(\"qibojit_errs.npy\")" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "32d605e9-672a-40b6-8a1a-eb17bbc3c45e", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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2meffRZt2rTBc889Z9T2W23duhUlJSXV5phU5/z58wCAli1b1uo4dS05ORmOjo6GYdsnn3wS165dw44dOyod1gbkG+fNmzfx5JNPQqFQYOnSpRgzZgwuXLgAe3t7k4/1v//9D1qtFmPGjDG7fSkpKejfvz8UCgWioqLg6emJrVu34rHHHkN2dnaFD9W33noLSqUS8+bNQ1ZWFpYuXYqJEyfizz//NPsxyystLUVERAT69euH5cuXY+fOnXj77bfRvn17zJw5E56envjggw8wc+ZMPPDAA4bn1q1bNwDAiRMncPvtt6N169ZYsGABnJyc8M0332D06NH47rvv8MADD1T5+P/5z3/wxhtvGP4+3bp1w9tvv4309HQsWLAAK1euxGuvvYbXX38d//nPf3DvvfdWeTxLX88nTpzAoEGD4Orqivnz58Pe3h4ffvghhgwZgt9++w39+vUz2n/WrFnw9PTEokWLkJubC0BOhUyZMgURERH497//jby8PHzwwQcYOHAgYmNjzU44feuttzBnzhxoNBqz9rfEF198AQBmByS1lZycDABGXxJ27twJV1dXQwB85swZODk5YdKkSXjnnXcMz/vYsWMoKSlB7969jY6pVqvRvXt3xMbGVvv4Nnk/qrexmGZs+/btQqVSCZVKJQYMGCDmz58vfv75Z1FUVFRh38DAQKPpB/1wWkREhNDpdIbtAwYMEAqFQjz11FOGbSUlJaJNmzbijjvuMGzTD6FrtVpx5coVw/Y///xTABDPPvusYdutUzYXL14UKpVKvP7660ZtPHbsmLCzs6uw/VamhinNPW5sbKwAIDZu3Fjl41gyZbNy5UoBQHz++eeGbUVFRWLAgAHC2dnZMMQqhPxbmDNl8+yzzwoAIjY21qw26P+mO3fuFKmpqSIxMVF8/fXXomXLlkZ/p7qasimvqimbypw9e1ZoNBoxadIks46jf721bNlSpKenG7b/97//FQDE//73vyofz8PDQ3Tv3r3C9uzsbJGammr4KT+M/dhjjwlfX19x48YNo/uMHz9euLm5Gaar9FMewcHBorCw0LDfqlWrBABx7NixKttW2d9jypQpAoD417/+ZbRvjx49RK9evQzXq5qyueuuu0RoaKgoKCgwbNPpdCI8PNxoesDUlE1gYKDhvq+88ooIDw8Xqamp4uzZs6J3797i+eefF0IIUVhYaNbrxdLX8+jRo4VarRbnz583bLt27ZpwcXERgwcPNmzT99/AgQMN00lCCHHz5k3h7u4uZsyYYXTc5ORk4ebmVmG7/vgTJkwwXF+8eLH4/fffDdcLCwvFyJEjzWq/EPLvWFXflJSUCG9vb9G3b1+zj1mepdPXaWlpwsvLSwwaNMhoe7du3YSjo6NwdHQUs2fPFt99952YPXu2ACDGjx9v2G/jxo0CgNizZ0+FYz/00EPCx8fHcN3c96P6wCmbejB8+HDs27cP999/P44ePYqlS5ciIiICrVu3NntY9rHHHjMaFu/Xrx+EEHjssccM21QqFXr37m00xKc3evRotG7d2nC9b9++6NevH7Zs2WLyMb///nvodDo8/PDDuHHjhuHHx8cHHTt2xO7du81qe02Pq0/A+/nnn5GXl1ejx7rVli1b4OPjg0ceecSwzd7e3pB9/ttvv1l8zOzsbAAwa3i2vGHDhsHT0xP+/v4YP348nJ2dsWnTJqO/ky3l5eXhoYceglarxVtvvWXRfceNGwcPDw/D9UGDBgFApa/N8rKzs+Hs7Fxh+6RJk+Dp6Wn4eeGFFwAAQgh89913GDVqFIQQRq+niIgIZGVl4ciRI0bHmjZtGtRqtcVtq8pTTz1ldH3QoEFmHS89PR2//PILHn74Ydy8edPQ9rS0NERERODs2bO4evVqtcdxcHAw/B4TEwNPT0907NgRGo3GMEJU/jlXxZLXc2lpKbZv347Ro0ejXbt2hu2+vr6YMGEC/vjjD8Px9GbMmAGVSmW4vmPHDmRmZuKRRx4x+vupVCr069ev0vcZX19fw4gFALzyyisYOHCg4bparTZrespcu3btQkpKSr2Mjuh0OkycOBGZmZkVVmHm5OQgLy8PkydPxrvvvosxY8bg3XffxZNPPomvv/4aZ8+eBSBHggHj14WeRqMx3F5eQ3g/4pRNPenTpw++//57FBUV4ejRo9i0aRPeeecdjB07FnFxcQgJCany/gEBAUbX9R/W/v7+FbZnZGRUuH/Hjh0rbOvUqVOVc4lnz56FEKLS+wKocui9KuYeNygoCHPnzsWKFSvwxRdfYNCgQbj//vvx6KOPmr1a4FaXLl1Cx44dK6xkCg4ONtxuKf0crT7HwlzR0dHo1KkT7Ozs4O3tjdtuu83iFVbWqhtTWlpqyPLfunUr/Pz8LLr/ra9XfXBS2WuzPBcXlwrLEgG5AiAqKgqADPD1UlNTkZmZiY8++ggfffRRpce8fv16nbTNFI1GU2E6z8PDw6zjnTt3DkIIvPzyy3j55Zcr3ef69etVfijodDrodDrDa6dbt2546aWX8PLLL6NDhw6Gv51+v+pY8npOTU1FXl4ebrvttgq3BQcHQ6fTITExEV26dDFsDwoKMtpP/yGqz1kw1R5b+uKLL6BSqTBu3Dij7fn5+YZ8Nj0fH59aPdbs2bOxbds2fPbZZwgLCzO6TZ8LVv4LFQBMmDABH374Ifbt24eOHTsa9qusFk1BQUGlOWV18X5UWwxI6plarUafPn3Qp08fdOrUCdOmTcPGjRurXVpa/htFddtFuaTW2tDpdFAoFNi6dWulj1PZN9m6Pu7bb7+NqVOn4r///S+2b9+Op59+Gm+++Sb2799vkyWHldEnXx47dgzdu3c3+359+/atMMdbnn4+uLJvM4AcwbDGXDkgv8X+9NNP+OKLL0x+UFTF1Ou1utdm586dcfToURQXFxsFvPp8i1vpP2AfffRRTJkypdJ9br1vTdtmiqnjmUPf/nnz5iEiIqLSfTp06FDlMTp16oR9+/bh9ttvByCDoYceegidO3dGr169MG7cONxzzz3Yt29fpYHDrWr6ejbXrR+G+j5Yv359pR/mdna2/ZjKz8/Hpk2bMGzYsAr5dhs2bDBaDg/U7v13yZIleP/99/HWW29VmsPj5+eHEydOVGiHfnGEPgj29fUFACQlJVU4RlJSUqVfMKp7P6oPDEhsSP/Hr+xFU9f030LKO3PmTJXJYu3bt4cQAkFBQejUqZPFj2nq27ulxw0NDUVoaCheeuklxMTE4Pbbb8eaNWvw2muvVfk4lQkMDMRff/1l9I0SkKsc9LdbasSIEVCpVPj8889rndh6a1sBWZtGP61Q3pkzZ2qcJF2V559/HmvXrsXKlSsrfBPTs9bIzH333Yf9+/dj06ZNePjhh6vd39PTEy4uLigtLcWwYcOs0qa6YKq/9NMc9vb2NW7/hAkTsGjRIuzYscNoe2hoKP75z39i9uzZOHbsGJYsWWLy71meJa9nT09PODo64vTp0xVuO3XqFJRKZYVR3Fvpk9C9vLwa5N/wxx9/xM2bNyudromIiKjQ7zWlr+vzzDPPGKYkb9WrVy/s2LEDV69eNQour127BqAs8b5r166ws7PDoUOHjP6PioqKEBcXZ9b/li0wh6Qe7N69u9KoWZ+/Yc63ltr64YcfjOaiDxw4gD///BMjRowweZ8xY8ZApVJhyZIlFdovhEBaWlqVj+nk5AQAFSq1mnvc7OxslJSUGN0eGhoKpVJpNBTp5ORkdjXYkSNHIjk52agKYklJCd577z04OzvjjjvuMOs45fn7+2PGjBnYvn17pZV3dTod3n77bbOLNOn16tULXl5e+L//+78KQ6/6v2dVf7+aWLZsGZYvX45//vOfmDNnjsn9TP1ta2vmzJnw9vbGs88+W2kBt1tfLyqVCg8++CC+++67Spexp6am1mn7akq/AubW/vLy8sKQIUPw4YcfVvrFxJz2T5o0CRkZGZg9ezYWLFhgVPTqlVdewZEjR7BgwQKkpqaaFTBb8npWqVS4++678d///tdoWXxKSgq+/PJLDBw4sNopl4iICLi6uuKNN96otKKsrf+GX375JRwdHStd7eTr64thw4YZ/dTEhg0b8PTTT2PixIlYsWKFyf30gcR//vMfo+3/93//Bzs7O0PRPTc3NwwbNgyff/650dTb+vXrkZOTg4ceeqhG7bQ2jpDUg9mzZyMvLw8PPPAAOnfujKKiIsTExGDDhg2GUszW1qFDBwwcOBAzZ85EYWEhVq5ciZYtW2L+/Pkm79O+fXu89tprWLhwIS5evIjRo0fDxcUFCQkJ2LRpE5544gnMmzfP5P179eoFAHjxxRcxfvx42NvbY9SoUWYf95dffkFUVBQeeughdOrUCSUlJVi/fr3hQ6j84+zcuRMrVqyAn58fgoKCKiw11HviiSfw4YcfYurUqTh8+DDatm2Lb7/9Fnv37sXKlSstTkzVe/vtt3H+/Hk8/fTT+P7773HffffBw8MDly9fxsaNG3Hq1CmMHz/eomOq1WosX74cU6ZMQZ8+fTBu3Di0bNkSsbGx+OSTT9CtWzfDUueqXLp0ybA8V1/yWz+6FBgYaPiQ2rRpE+bPn4+OHTsiODgYn3/+udFxhg8fbhgq1v9tn376aUREREClUln8/CrTokULbNq0CaNGjUJYWBjGjx+PPn36wN7eHomJidi4cSMA4zyQt956C7t370a/fv0wY8YMhISEID09HUeOHMHOnTuRnp5e63bVllarRUhICDZs2IBOnTqhRYsW6Nq1K7p27Yro6GgMHDgQoaGhmDFjBtq1a4eUlBTs27cPV65cwdGjR6s8tr29Pb777jsMHToUhw4dwrPPPovevXtDoVDg8OHDWLlyJa5evYrdu3ebnfdlyev5tddew44dOzBw4EDMmjULdnZ2+PDDD1FYWIilS5dW+1iurq744IMPMGnSJPTs2RPjx4+Hp6cnLl++jM2bN+P222/H6tWrzWp3XUtPT8fWrVvx4IMPWjxFnZWVZQjo9u7dCwBYvXo13N3d4e7ubsiJOnDgACZPnoyWLVvirrvuMkrWBYDw8HDDSFqPHj0wffp0fPLJJygpKcEdd9yBX3/9FRs3bsTChQuNpmJef/11hIeH44477sATTzyBK1eu4O2338bdd9+Ne+65p8Z9YlX1tp6nGdu6dauYPn266Ny5s3B2dhZqtVp06NBBzJ49u0KlVlPLfm9d+qlfontrZc0pU6YIJycnw/XylTPffvtt4e/vLxwcHMSgQYPE0aNHKz3mrb777jsxcOBA4eTkJJycnETnzp1FZGSkOH36dLXP/dVXXxWtW7cWSqWywrK36o574cIFMX36dNG+fXuh0WhEixYtxNChQ8XOnTuNHuPUqVNi8ODBQqvVCgDVLgFOSUkR06ZNE61atRJqtVqEhoaKtWvXVtjP3GW/eiUlJeL//u//xKBBg4Sbm5uwt7cXgYGBYtq0aUZLKC2t+Ll161YxdOhQ4erqKuzt7UVQUJCYO3dupZV/K6NfLlrZT/kl4vq/v6mf8stNS0pKxOzZs4Wnp6dQKBSG101VlVphQaXSpKQk8fzzz4uQkBCh1WqFg4ODaNeunZg8eXKlSxlTUlJEZGSk8Pf3F/b29sLHx0fcdddd4qOPPqrQD7cuI9e3ubLXQHmmlv2W/3/Tq+x/KSYmRvTq1Uuo1eoKfXH+/HkxefJk4ePjI+zt7UXr1q3FfffdJ7799tsK7a+sUqsQcpnshAkThL29veFvZm9vLyZMmCCSkpKqfG6VMff1LIQQR44cEREREcLZ2Vk4OjqKoUOHipiYGKN9qnvd7969W0RERAg3Nzeh0WhE+/btxdSpU8WhQ4csbrulTC37XbNmjQAgfvzxR4uPqX9dVfZT/rH0/WLq59bXZVFRkVi8eLEIDAwU9vb2okOHDpVW1BVCiN9//12Eh4cLjUYjPD09RWRkpFFpg/KPX5MKxHVNIUQdZUBSg3Tx4kUEBQVh2bJlVY5mEFHTkJWVZciJ6ty5c41XpBHVN07ZEBE1IW5ubianLIkaMia1EhERkc0xICEiIiKbYw4JERER2RxHSIiIiMjmmkVA8sADD8DDwwNjx461dVOIiIioEs1iyubXX3/FzZs38emnn+Lbb7+16L46nQ7Xrl2Di4uL1cplExERNUVCCNy8eRN+fn7VnqyvWSz7HTJkCH799dca3ffatWvVnouBiIiITEtMTKz2hKgNPiDZs2cPli1bhsOHDyMpKQmbNm3C6NGjjfaJjo7GsmXLkJycjLCwMLz33nvo27dvnTy+vpR4YmKiWafBLi4uxvbt23H33XebXaaZzMf+tS72r3Wxf62L/WtdNenf7Oxs+Pv7m3VajgYfkOTm5iIsLAzTp0/HmDFjKty+YcMGzJ07F2vWrEG/fv2wcuVKRERE4PTp04ZTMteGfprG1dXV7IDE0dERrq6u/IewAvavdbF/rYv9a13sX+uqTf+ak/LQ4AOSESNGVHlG0xUrVmDGjBmGE9StWbMGmzdvxieffIIFCxZY/HiFhYVGZ1bNzs4GIP8QlZ2J8lb6fczZlyzH/rUu9q91sX+ti/1rXTXpX0v2bfABSVWKiopw+PBhLFy40LBNqVRi2LBh2LdvX42O+eabb2LJkiUVtm/fvt1wCnFz7Nixo0aPT+Zh/1oX+9e62L/Wxf61Lkv6Ny8vz+x9G3VAcuPGDZSWlhpOia7n7e1tOLkUAAwbNgxHjx5Fbm4u2rRpg40bN2LAgAGVHnPhwoWYO3eu4bp+/uvuu+82e8pmx44dGD58OIcMrYD9a13sX+ti/1oX+9e6atK/+lkGczTqgMRcO3fuNHtfBwcHODg4VNhub29v0Qvc0v3JMuxf62L/Whf717rYv9ZlSf9a8ndo1IXRWrVqBZVKhZSUFKPtKSkp8PHxsVGriIiIyFKNOiBRq9Xo1asXdu3aZdim0+mwa9cuk1My5oqOjkZISAj69OlT22YSERFRNRr8lE1OTg7OnTtnuJ6QkIC4uDi0aNECAQEBmDt3LqZMmYLevXujb9++WLlyJXJzcw2rbmoqMjISkZGRyM7OhpubW22fBhEREVWhwQckhw4dwtChQw3X9QmnU6ZMwbp16zBu3DikpqZi0aJFSE5ORvfu3bFt27YKia5ERETUcDX4gGTIkCGo7nQ7UVFRiIqKqtPHjY6ORnR0NEpLS+v0uNTw6XTAiRNARgbg4QF06QJUcwoGIiKqpQYfkNgKp2yap5gYYPVq4ORJoLAQcHAAgoOBqCggPNzWrSMiarr4vY/obzExwLx5wJEjgLs70LatvIyNldtjYmzcQCKiJowBCRHkNM3q1UB6OtChA+DkBKhUgLMz0L69nL6Jjpb7ERFR3WNAQgSZM3LyJODrCygUwJUrQFwccP26vO7jA8THy/2IiKjuMSAxgXVImpeMDJkzotXK63l5QFFR2e1arbw9I8M27SMiauoYkJgQGRmJ+Ph4HDx40NZNoXrg4SETWPPz5fXcXHnp5CQv8/Pl7R4etmkfEVFTx4CECHJpb3AwkJwsR0JKSuRUjVYLCCG3h4TI/YiIqO4xICGCrDMSFSVHQM6ckQGJg4Ocujl/Xm6PjGQ9EiIia+HbqwnMIWl+wsOB5cuB1q1lQFJQAGRmAj17yu2sQ0JEZD0sjGYCC6M1T+HhQEQEYGcnL0eNYqVWIqL6wICE6BYJCYCrK3D//UDXrrZuDRFR88DvfUTlZGcDN27I39u1s21biIiaEwYkROVcuCAvfX0BR0fbtoWIqDlhQEJUzvnz8rJ9e9u2g4iouWFAYgJX2TRP+hESTtcQEdUvBiQmsFJr88QREiIi22BAQvS3ggLg2jX5OwMSIqL6xYCE6G8JCbJMfIsWAEvPEBHVLwYkRH87d05ecnSEiKj+MSAh+ps+oZUBCRFR/WNAQvQ3rrAhIrIdBiQmcNlv81JcDFy6JH/nCAkRUf1jQGICl/02L5cvA6WlgLMz4Olp69YQETU/DEiIYDxdo1DYti1ERM0RAxIicIUNEZGtMSAhAlfYEBHZGgMSavZ0OlkUDeAKGyIiW2FAQs3e1atAYSHg4AC0bm3r1hARNU8MSKjZ00/XBAUBSv5HEBHZBN9+qdnTn+GX0zVERLbDgMQEFkZrPvQBSYcOtm0HEVFzxoDEBBZGax6EYMl4IqKGgAEJNWupqUBODqBSAQEBtm4NEVHzxYCEmjX9dE1gIGBvb9u2EBE1ZwxIqFnjdA0RUcNgZ+sGENkSV9gQUbOn0wEnTgAZGYCHB9Cli01qIDAgoWaNK2yIqFEzJ5ioap+YGGD1auDkybIKkcHBQFQUEB5er0+FAQk1W1lZQHq6PLtvUJCtW0NEdIvqgg1zgomq9gGAefPkG6GvL6DVAvn5QGys3L58eb0GJQxIqNnSj474+QEajW3bQkTNTG2DjZiY6oMJwPQ+zz0HODnJpYatWwPFxUBentzWvr18g4yOBvr3r7fpGwYk1GzpAxKe4ZeI6pS1g42lS4H335e3d+ggh3kBwNlZvqGdPQv8+99AUZE8WZeXF5CZKYOP4mL5mImJ8tLVFcjOlrcDQEiIDEp8fID4ePk8QkPrpdsYkFCzxRU2RFQjtcnJsDTYEEIGFgDQqhVw+TLwzDMyuNBo5BtZaakMNMr/7Nol7+PgABQUlAUc7u7yUqmUzwMA1GqgTRtZ+0Crldu0WiAlRT7HesKAhJotjpAQNTO1TQAFapeTYWpkw8lJBgTnzslgIyVFPmZcnAw8gLJAoqQEuHFDBirOzvIYtwYbKpW8XakE3NxkG729ZcChVsvLvDzg+HFZEbJNm4p9lZ8v7+fhUetuNxcDEmqWcnOBpCT5O0dIiJqB2iaAVje68dxzgItLxWBDo5E5GhcuAM8/L4MNtRpISJDBRWGh/NHp5PXr142DDUBeqtWAnZ0MMjIy5O8tW8r92rWT1+3t5U9BgXwcQLbT2blifyiVsv1ZWbJ9+scC5OMnJwM9e8qArJ4wIDEhOjoa0dHRKC0ttXVTyAoSEuRlq1ZyCpWIGjBzRzaOHUPLEycAf3+ge3fjaZTaJIDeOrrRrp2cJikokNMjLi5yyDUnR45ExMfLbzw6nfHIRkqK6ZENhUKOlAghgwpfX/lcNRoZbOjl5MjH8/OTb2Te3hWDicuXZTAhhBxlad++4j4pKUCfPsDNm7LtPj5lzzk5WT52ZGS91iNhQGJCZGQkIiMjkZ2dDTc3N1s3h+qYPn+E0zVEDUBt62T8vY8qPh5haWlQffONTM6MipKrRFavNp0Aev488O67Mk8jJUUGAvn5MtGzpETuf+YMMGmS3KYfobh1mqSgQLYvK0sGFPr8DEAGFGq1DGLs7ORzdHaW9QbUahl0qNVy6DYjoyzYcHIyPXIxcyYwf77pYKL8FJKpfRYvlvvo+zclRfZvz54yGGEdEiLr0umA338H0tLK3jdsUJSQiIDa18kov4+3N3JVKjg5O5ftM3OmPHbLlvKfPj5e/tN7e8uAIy8P+OkneRwHBzkCcWuwIYTxVIqeUgk4Osr7OTvLtnl4yLbqp0/s7eV+OTnGwYavb82DDX2wsHx59cGEOfv0789KrUT1Tf/et2OH/EKTlgYcPWqTooREVJOcjPIjG6tXyw/y8itScnNlUqebm/zgf/llebtGI49RUCCPc/NmWTv0oxlarQwgcnPlvj4+cuRCqQSuXJGjG61by0BFpTJ+LjdvyscRomwKRs9awUZ4ePXBhDn7KJX1trS3KgxIqNnQv/elpcn3B60W8PS0WVFCoubB1HSMTmd6KiUgQN4nK0uuADl/XuZF6HRypEOhkLkbmzfL+2i1QFwcFGlp8CwpgcLDQ+5TUlIWeOh0MsDJy5PBRFCQ/JAvKpJvCkql6QTQnBz5ZqEf3WjRwvh2S3Iy6jLYAMwLJhpIwFEdBiTULJR/7/PzkyOy+qlcd3ebFCUkavxqs0TWxUVu9/CQH9inTsnjubrKD/j8fBk8pKTIUYuSEnnM4mJ5KYT8XQh53OLispEOpVIGFhqNzPvw95dBR/nAR3+M8+eB3r2rTgA1d3TD3JyMugw2mhAGJNQsnDgh3x98feVobGamcf6IDYoSEtmOtetxVLVE9tAh4IkngM6d5WiDWl024gHIAEChkPvn5ckcDS8vebtSKZNVlUr5j3z9utzX01Puf+wYSoqKYN+jBxR2dnJkw94eeOop4IMPapcAasnoBoONGmFAQs1CRoZ8z9Rqy+oMlZ8CtkFRQiLbsHY9jvJLZG/ckKMTRUXAtWtyCiY3VwYa+iRRIeSoSGGh/JDu2lUeLydHJnip1XJYs3XrsucghDxe//5lIxutWkH064eM69fhpVQaj2xMnChHPuoiAbQR5WQ0NgxIqFnw8JDvLfn58sfdXb4/6d9DbFCUkKj+1UU9jn//G3jvPRlQ+PrKoEGnk3kfAHD6NPDIIzKXQqWSQcit9Tbc3OQ/X6dOMmipbCrFnJyMW0c2vLxkW3JyZPvK19KoqwRQgAGHlTAgoWahSxf5Be/IEfkFDZBL/AGbFSUkqpmaTrcA1dfjWLlSjlQkJclpkCNH5D+Ir6/M4YiPBx56SAYFdnYyWND/Q+krgwJlq02cnWVQov8JCpJBiVIJXLwITJgAfP557XMy9CMb8fFwSkuT9T4qq6XRhBJAmyIGJNQsKJXyy9Ts2XL1nlYr309zcmxWlJCootrmbVS1z4gRcpuPjwwiYmPlB3f5ehxbtshjODjI6/olsvoRDpVKBiFCyNwOe3v5j6RQyOM4OMj2XrsmtwcEVF4KOSdH7jtokKyoWtucjL9HNkrj4nB02zb0u+ceKMtXaqVGgQEJNRvh4cBjjwGvvy7z4y5dsmlRQmpuqiptDtQ+b8PUdEtuLrB/v6wGePOm3KbTyfsCFetxKBRyZEN/AjalUrZXH2xcvixv8/c3vUS2ZcuyJbIuLlWfJ0WprJucjL/3SUtMlPsyGGl0GJBQs6LVAj16yM+Cu++2aVFCakrMHNmotLR5TU5JX1JSturE21tG14sXy+2JiXJbZqb8PTdXjoTk5Mj7qFRlZ3xVqYC2beXy2OJimc9Rvh5HcLDx88zJkc9PH2zUZols+SFJTpMQGJBQM3Phgnz/HDQIGDzY1q2hJsGSkY3KSpuXDzaCgoDDh2WAc9ttcnTh4kVgxgx5u1Ipl83ql4OVP3FbTIz83cFBBiLlE0nVahmkpKbKqZZu3YwDprqux2HJElmivzEgoWZDiLKT6rVrZ9u2UCNjagTE0pENIaDMypIvRjc3ObLx5JOyaJdKJQOBrCz5mOfOycuSEjlNUtkp6e3sys4EW1wst7VsKbd5ecnMbRcXOQJSWlp2jAsX6qceh7mrVojAgISakbS0spN1BgbaujXUYNQ0kXTWLONg49ZVK6dPy1GEa9dksHH8OBRJSWip0xmXNr94sfJgQ6stO/V8aqqcYvHzKxvxKD96kZMjV8YApsuf60/8NncusHVr/dTjADgdQ2ZrFgHJTz/9hOeeew46nQ4vvPACHn/8cVs3iWxAPzri7y/fz4lqlUgaGSl/9/MzXrXSqpU8VmGhDEZuDTYAGWg4OMgg48aNsmDDxQU4flzuq/9wz8mRQYk+b8PLy/RUijnTLRMnyh/W46AGpskHJCUlJZg7dy52794NNzc39OrVCw888ABatmxp66ZRPeN0TTNU1eiHpdMt+g94Bwc5LXLypAwWsrPLzr0CyJUpQNl0ir29DCI8PCA0GqRmZsLLywsKfbChr0SakCDzPPr1K2u/JXkb5k636J8/63FQA9PkA5IDBw6gS5cuaP132eERI0Zg+/bteOSRR2zcMqpvDEiamapGP/r3N10krF074OxZ4KWX5AiHRgNcvSqPkZtbVpujpET+FBSUnbZepZL3d3aWOR2ZmWXBhpubDDD0rJUkykRSaqQafECyZ88eLFu2DIcPH0ZSUhI2bdqE0aNHG+0THR2NZcuWITk5GWFhYXjvvffQt29fAMC1a9cMwQgAtG7dGlevXq3Pp0ANxPnz8pIBSSNQ25O/VTf68cQTwF9/yRUnV6/K24qKZBCh/7l61Xi6pfyqFa1WnoL+8mWZONqjR8UpkitXKgYbpkqb12WSKBNJqZFq8AFJbm4uwsLCMH36dIwZM6bC7Rs2bMDcuXOxZs0a9OvXDytXrkRERAROnz4NLy8vG7SYGiL9iUEBBiQNXm1P/nbr6EdJSdkJ3RQKeZr7+fPLziR7a7AByKkWpVJeOjvLoKNNG3mpL4eekyNzRrRa80c2qiptXpd5G5xuoUaowQckI0aMwIgRI0zevmLFCsyYMQPTpk0DAKxZswabN2/GJ598ggULFsDPz89oROTq1auG0ZPKFBYWorCw0HA9OzsbAFBcXIxi/emxq6Dfx5x9yXI17d/TpwGdTgUvL8DBoRT881SuXl6/f49sKDIzIdzdjT50Ffv2QTl/vvxALv8Bf+QIMHcudEuXAoDpfWbPhu6ee6D64w95zNhYmTQKyCkThUL+6IMTtRpwc4MICChLMrW3l0FOZqYcXbl4UWZClx8BKS2Vq1p69YLuySehXLMGOHUKiuRkCAcHoHt36GbOhOjTR4629OkDrF2LkqNHcXTnTvQaNgx2YWGyjbf2defOxo9TWmq1P0VTw/df66pJ/1qyr0KI8pOaDZtCoTCasikqKoKjoyO+/fZbo2mcKVOmIDMzE//9739RUlKC4OBg/Prrr4ak1piYGJNJrYsXL8aSJUsqbP/yyy/h6OhojadF9eDPP32wY0cgbrstAw89dMbWzWnadDq4XL4MdW4uipyccDMgwBBweJw6hXabN8P5yhWoiotRam+PnDZtcOHee5HRqRN6vfMO3C5cQK6XF9z/TvrJ/HvFiFNyMrIDAqAsKYHrpUso8PCAQqeDqrgYqoICqAoKYFdQAJ29PZTFxSgttzRWp1ajVK1GqYMDSu3s4JCdjTwvL2jT0pDr41NhusUpORmZ7dsj4Z570PXTT2Gfk4MCd3eUOjhAVVgITWYmip2dcXzaNGR07lzlcyZqzvLy8jBhwgRkZWXBtbLzGpXT4EdIqnLjxg2UlpbC29vbaLu3tzdOnToFALCzs8Pbb7+NoUOHQqfTYf78+VWusFm4cCHmzp1ruJ6dnQ1/f3/cfffd1XYmIKPBHTt2YPjw4bC3t6/hMyNTatq/Z88q4eWlwD33tMLIkR2s2MLGrbavX8W+fVC+/74cLSgslKMFnTtDN2sWAEC5aZMc2fD3N4xsuKakwG/TJjnSkJ0NtGkDJ50OitJSQKeDd2qqHCUoKIDzX3/JB3JwgGN6elkRMTc3OdqhUsl9NRqovL1lnoejo3FwkJMDaLXQzJkD5YcfwikzU65u0Y+0pKQA/v7QvvoqfAYMgOL226F8/304nToFRV6efE6DBkE3cyYGDBhQr/1LVWP/WldN+lc/y2CORh2QmOv+++/H/fffb9a+Dg4OcHBwqLDd3t7eohe4pfuTZSzt30uX5GdSx45K8M9SvRq9fmNigAULjBJJFfn5wNGjUL7wgqyxkZkJdOxoXADM0xO4cAHKV1+V1escHOT2oiK5j34KVaUqO/mbk5MMQFq0kJdOTjLwUKvlFEvr1lBcvVr5id2uXwd69oRyyhRZnl2fi5KaKh+7Vy8gMhJKfW7H4MHAwIGG3A7F37kdylqMgPD9wbrYv9ZlSf9a8ndo1AFJq1atoFKpkJKSYrQ9JSUFPj4+NmoVNTRFRfLUHoCsF0W1YGpli05nehlt+/bAsWMyGAkMlH+M8+flSIZ+1LGkpGy0QwgZSOTny6TSTp1koFBQIIMGhcJ0NdKcHLlMd8oU4IMPqq/HwQJgRA1Gow5I1Go1evXqhV27dhlySHQ6HXbt2oUofZGgGoqOjkZ0dDRKmVDW6F2+LD/7XFxkPSuqoapWtri4yO0+PvJ09tnZMgAoLJSBRG6u/LlyRSaNlk9002jk6EZOjpzKSU+XS6FycuTtrq4yCLl6VY5emFuNtH178+pxMNggahAafECSk5ODc/qTTAFISEhAXFwcWrRogYCAAMydOxdTpkxB79690bdvX6xcuRK5ubmGVTc1FRkZicjISGRnZ8PNza22T4NsSF8Q7dbPL7qFTgccO4aWJ07IwKB7d/PqejzzDDBggAwYUlJk9HfrMlr9sK1aLWtxBAbKQMTJSY6C5OTI+8ycKUc2EhKAkBD5OHl5NatGynocRI1Kgw9IDh06hKFDhxqu6xNOp0yZgnXr1mHcuHFITU3FokWLkJycjO7du2Pbtm0VEl2p+dIHJEFBtm1Hg/b36IcqPh5haWlQffONDAhMVTXNy5OjIEVFQHy8zNsoLDQklKJDBxls6M/ZUlRUVogsIKD2IxvmViPl6AdRo9HgA5IhQ4agupXJUVFRtZ6iuRWnbJqO8iMkzZa5VU29vZGrUsHJ2bls9OOBB4A//5QjGadOyZEQna5s9MPBQV5v21aOdHTubDwKoa9a2qePnM6pi5ENjn4QNTkNPiCxFU7ZNA06nRz9B5pwhdbqyqybW9W0TRsgOxvaGzegyMqSwUJioozoCgrKqprqV7q4ucnHc3GR53x58kng88/l/pUFHIsXy/bU1cgGRz+ImhQGJNSkJSXJz1K1Gih3SqOmo7oy61XlfsyeDdx5J/Dbb3KqJSMDisxMOOt0MoBQKOTxiovLzt3i4SF/12rLgp6cHLnfoEEy76S6gIMjG0RUCQYk1KSVzx9pcp951Z1AbulS4P335e3t28vILC1N5n5kZ8vRj0uXykY/lErA3x85paVw8faGQl/X49IlGc1dvSqXKZnK/9AHFtUFHBzZIKJKMCChJk0fkDTq6ZrKpmQA03U/goLkyXueeUYOESkUwOHD8v5AWe6HViuP7eoqAw4vLwgA+devw6VlSxk4WFrXA2DAQUQ1woDEBCa1Ng2NPiAxNSUzYoTc5usrt2dny4TRvDw54lFcLIMFIWSQoj+pnEoF+PnJ/A+Nxnj0w9tb7q9X07oeREQ1wIDEBCa1Nn5CyC/0QCMNSExNyRw8KG/LzpaVS4uLK9b90Gjk6IeDgwxAWrWSv5dX2eiHl5e8X06OLLHOuh5EVE8YkFCTlZEhq5ErFHJFaoNkTin2oCAZcFy/LoOQwkIZMBQXyyenVsvaHi4usvaHk5PcJyNDBiMJCXKf8kyNfsTHwyktTSa5sq4HEdUjBiTUZOmna9q0qfh5XC9qsxzXxQU4elQeIy5OBiaAHAFRKmVyaXq6HDXp1k1OxegJIadievaUlU/nzze79kdpXByObtuGfvfcA2X5Sq1ERFbGgISaLJsWRKvpctwjR4AZM+S2y5fldn3uh729PNGcq6sMOuLjZX5IQoLpYCM83OKqpmmJiXIUhMEIEdUjBiQmMKm18bNZ/ogly3H1K2QKCuRIin5JbmqqDAicneV5ZfRnxdXLyZFBx9y5wNatVQcbzP0gokaAAYkJTGpt/KxaodWc3I927eRyW0CepTYgADh3Tk6hJCfLeaTz5+VISElJWUKqs7MMLMLCZMl1Fxfjx741/2PixOqDDeZ+EFEDx4CEmqTcXFmCA7BCQFJd7sfJkzIwSEmRDSktLQtMSkoqLsctKZG3ubrKpbcuLjIH5IEHZCl2c2p/MNggokaOAQk1SfrREU/PigMMtWJqOubwYXkul5CQslUtCoVcCaOnUMj9S0rkieo8POSKmKIieb1Tp7JiZJaUYiciagIYkFCTZJXpmlunYw4elMGEp6csSpabK4MGIcrOhltYKIONbt1kMJGba7wc18/P+CQ7NSnFTkTUBDAgMYFJrY1brRJaTeWHnDghRyrc3OQDZGfL/W/elJeurnI1TMeOwI0b8tLUeV/MXY4LMP+DiJoFBiQmMKm1cdOPkFi85NdUfkhkJHDsmExAVanKAg17e5ms6u4uf794USaZVpf7YclyXCKiZoABCTU5JSUybgBkkdMKTI2AVJYfkpsL/PEHsGuX3FZSIoMRb285aqHVlh3X0twPLsclIjJgQEJNjn4VrbOzTO8wYmoEZNasstogQUHAoUMyP8TZWSam5uXJ24KDy/YxNR1jSe4Hp2OIiAAwIKEmRqeTgxlpaXK2RIhycUNVBcueeEKe+EajAf76qyw/RKORP97ecqomMrLsRHTM/SAiqjMMSKjJiIkBVr8n8MfuYmRkKXElQYdHJ9ojarYC4f3LrZDp0EGOjqSlyYTU7Gy5vaREBhUKRVm59nbt5HljdDqZHxIYyNwPIiIrYEBCTUJMDDDvyWykX7oJFCigLVWjReYNxG52wbzjLlg+PxXhx47JxNPjx8uqpumro2o0MgfExUWWandyMp5eyc+XgYeHhxz1YO4HEVGdYkBCjZ5OB6xenIr0c7nooEzAEUUoFHYqtNLkQlOUhPNnAhC94DL6Z12C0lFTNodjZyfrgLi5yWBj//6y5JOq8kMATscQEdUxBiQmsA5J43HimA4nD+bCV5GMQkd3lGaroBQ6aEpyoCgthk/JFcSntMIJTReEOlyRQUiLFjIg0cvJkXkiWq15+SFERFSn+O5qQmRkJOLj43Hw4EFbN4WqkXEkAYX5Omi1ArkFKuiKSuBQfBOKokJAAFq7YhQqNMjo1K9s6U35YEQ/AtKnDxAdDfToAWRmypyRzEw5MrJ8OfNDiIisiCMk1Oh5IAMOcER+vgL5RQoooYOLIkeeT0atRr5wgkNeMTxG9Ad2H6i+YFl4OPNDiIjqGQMSajx0OuDYMbQ8cUImnnbvDigU6FJ4BME6D8QWh6EEKsDODo5O9oCDoxz8yHFHT+0JdHm4C3CvGStkmB9CRFTvGJBQw2Cqeqre3wXNVPHxCEtLg+qbb4C2bQEXFygzMhDlpsJzGYGIRwjsFKXQqIqQU6JBcqE7PEQaIvscgDL0Hp6sjoiogWJAQrZnqnpqVJQctShf0MzbG7lKJZwKCmQFNHt7ICwM4XPvxiuffYXHzr6AHJ0zrhe6QYNC9FQdRWTQ/xC+eCoLlhERNWAMSMi2qqqeOm8esHRpWUl3f38oDh9Gq9xcKNzc5L46nVwx88ILaOl1HD1evgaX/BTMdPsSHpoCdOnpAGXULCakEhE1cAxIyPpMTcfoylVPbdcOOHxY7h8WBnh5ARcuAE8+Cdy4IbdnZAC5uVDodHKVTNu2cjTlwgXgxAmcd+oGRU+BPsHJGDzKidMxRESNCAMSsq6qpmNcXOT2li1l0JGbC5SWAkePyvuWlMhgRYiyYmVKJXRKJRRdukCh0cj9U1KAjAycPw9AoUD7gb7AYF+bPm0iIrIMAxITWBitDpiajjlyBHjqKZnHcfGizANRKORZdfUcHABXV1kHRK2Wq2rc3YHYWJQWFkKlryNSrqT7hQtyU/v29fw8iYio1hiQmBAZGYnIyEhkZ2fDzc3N1s1pfG6djjl0CCgqklMx2dmyMmpystxPp5Pl2wsKZHDSo4e8zMmR55Tx8wMSEoBWrSB690bm9evwUiqNSrrfDOiC69flQwcF2fapExGR5Ti5TtZx4oScjvH1lVM1N2/K0YysrLIpGHt7ICRETtncdps8wZ1KJX/0wUaXLsA//ynzQc6fl0GKTicvz583FDS7cFG+lH18ZAxDRESNCwMSso6MDBmI6HTAqVPyUqkE2rQBunaVxcjc3IBJk2RAkpAgg5NevYC8PKNgAwMHytLtPXoAWVlwun5dBjblSrpzuoaIqHHjlA1Zh4eHTDiNj5eBiEolhy58fOT1nByZ+zFokKy4Wl311PBwoH9/lMbF4ei2beh3zz1Qdu9uWEFz/rzcrV07mzxbIiKqJQYkVHuVLeu9dEmumsnPlzkgvXuXLb8tl/thWJZrTvXUvwuapSUmyoTYcrfrAxKOkBARNU4MSKh2KlvW6+AgR0QCA2WeiFIpp2EqO5ldHVRPLSgArl6VvzMgISJqnBiQUM3duqzXwQE4e1ZOu6jVwDPPAHfdBURHVz0dU0sXL8pBlxYt5MpgIiJqfBiQUM1Utqw3N1eunnFykoHH6dPASy8BAwZY9WR2zB8hImr8GJBQzZRf1ltcLJNUS0tloNGpk7yMj5f7hYZa9WR2zB8hImr8uOyXaka/rFerlXMm+mCkc2dZYVWrlbdnZFi9KfolvxwhISJqvBiQUM14eMhpmWvXZOVVQE7XODrK38uVdLemkhK5oAfgCAkRUWPGgIRqpksXoEMHOTwhhKyyeuuy3pAQuZ8VXb4sgxInJ1mVnoiIGicGJCZER0cjJCQEffr0sXVTGialEvD2BuzsZA6JnZ0MRG4p6V6XyauVKZ8/olBY9aGIiMiKmNRqAk+uV42DB+VcSUiIDD6uXJE5I9nZdb6styrMHyEiahoYkJDl8vOB99+Xv0+fDkydatVlvVXhChsioqaBAQlZ7tNPgRs35HlpJk6sVZXV2tDp5Dn5AAYkRESNHXNIyDInTwJbtsjfo6LkShobSUqSZePVaqB1a5s1g4iI6gADEjJfURHw7rsyeXX4cCAszKbN0U/XBAXV2wwRERFZCadsqGrlz+S7dy+QmCjzRKZPt3XLmD9CRNSEMCAh08qfyffmTSA1VRY/e/lleWljXGFDRNR0MCChyunP5JuWJhNY8/JkFdaiIpnU2q1bvSzrNUUIjpAQETUlnHmnisqfybd9e3meGp1OJrD27AlkZgLR0XKbjdy4IQdtVCogMNBmzSAiojrCgKQ50+mAY8eAPXvkpT7A0J/J19sbuH5dLmUBgDZt5JIWH5+yM/naSEKCLMvq7w/Y29usGUREVEc4ZdNclc8PKSyUox/BwXIpb26uzBe5fl2eKEYI+anfqpW8r1YLpKTUy5l8TeF0DRFR08KApDnS54ekpwO+vjLAyM8HDh+WVVdbtpTzIXZ2Mm/E0VGOjOhPFlNPZ/KtyoULsi0MSIiImgYGJM1N+fyQdu1kECKEDEyys2UgkpMDeHrKBNaQECA2tuz++jP59uxp9TP5VkU/ZcMVNkRETQMDkuZGnx/i6yuvFxTIKRv96IebmxwReekl4MMPy06gp9XKlTbJyfV2Jl9TcnPtcOOGfHgGJERETUOzSGp94IEH4OHhgbFjx9q6KbaXkSEDEH0eSEGBHPVwdAQ6dpTVV+3tZfnT5cuBHj3kqpqLF+Vlz55yuw2X/KakOAEA/Pzk0yAiosavWYyQzJkzB9OnT8enn35q66bYnoeHzP9ISwOuXJHbtFqZ0KpSyekafX5IaCjQv7/NzuRrSnKyIwCOjhARNSXNYoRkyJAhcHFxsXUzGoYuXYAOHYBTp8pWzzg4yCkbfX5ISEhZfoj+TL6DB8vLBnDSmORkOULChFYioqbDrBGSH3/80eIDDx8+HFozxtP37NmDZcuW4fDhw0hKSsKmTZswevRoo32io6OxbNkyJCcnIywsDO+99x769u1rcZsIMvBwdZWjIUVFcgrG2bnB5IeYIznZCXZ2DEiIiJoSswKSWwOE6igUCpw9exbtzBhTz83NRVhYGKZPn44xY8ZUuH3Dhg2YO3cu1qxZg379+mHlypWIiIjA6dOn4eXlBQDo3r07SkpKKtx3+/bt8PPzs6jtTd5338n6IqGhMoE1MVGWPdVXYY2MtGl+SHXy8oD0dA28vDhlQ0TUlJidQ5KcnGwIAKpjyfTIiBEjMGLECJO3r1ixAjNmzMC0adMAAGvWrMHmzZvxySefYMGCBQCAuLg4sx+vWYuPB9avl7//85/A8OENLj+kKjodsH07cPOmPXx9Ac7CERE1HWYFJFOmTDFr+kXv0Ucfhaura40bpVdUVITDhw9j4cKFhm1KpRLDhg3Dvn37an38yhQWFqKwsNBwPTs7GwBQXFyM4uLiau+v38ecfetVVhZUb74JlJRA3HEHdEOHynPUdO5ctk9pqfxpgPbtU+D995X4808VkpNdkZkJTJigw6xZOgwYIGzdvCajwb5+mwj2r3Wxf62rJv1ryb4KIUSDeTdXKBRGOSTXrl1D69atERMTgwEDBhj2mz9/Pn777Tf8+eefZh132LBhOHr0KHJzc9GiRQts3LjR6HjlLV68GEuWLKmw/csvv4Sjo6PlT8pWdDq4XL4MdW4uipyc0HrvXrglJCC/VSscnz4dOrXa1i0026lTHli7titycuxRWqpAXp4dXFyKACjg7FyMadOOo3Nn25WxJyKiyuXl5WHChAnIysqqdqCi1st+s7Oz8csvv+C2225DcHBwbQ9nFTt37jR734ULF2Lu3LmG69nZ2fD398fdd99t1qhPcXExduzYgeHDh8PeRmd9U+zbB+X77wOnTkFRWChLvRcVQXTogNL33kObRnR6XJ0O+OorFYQAunWTs075+UUICHCCu7sCFy4AcXED8cwzpQ15tqnRaAiv36aM/Wtd7F/rqkn/6mcZzGFxQPLwww9j8ODBiIqKQn5+Pnr37o2LFy9CCIGvv/4aDz74oKWHNKlVq1ZQqVRISUkx2p6SkgIfH586e5zyHBwc4ODgUGG7vb29RS9wS/evMzExwIIFZeepKSmRlVkLCqC4cQPK69flst9G4tgx4PRpWQRNpwOSkgR0OntotQqoVEr4+soVzGfOKBEaauvWNh02e/02E+xf62L/Wpcl/WvJ38Hi75R79uzBoEGDAACbNm2CEAKZmZl499138dprr1l6uCqp1Wr06tULu3btMmzT6XTYtWuXySmXuhIdHY2QkBD06dPHqo9Tp8qfp6ZDB7lyJiFBLvENCJB1RqKj5X6NRPnCsllZcptCIc/1B8jthYU2PfEwERHVAYsDkqysLLRo0QIAsG3bNjz44INwdHTEvffei7Nnz1rcgJycHMTFxRlWyiQkJCAuLg6XL18GAMydOxcff/wxPv30U5w8eRIzZ85Ebm6uYdWNtURGRiI+Ph4HDx606uPUiE4nhw727JGX+gBDf54ab2+5tHfPHiA1FdBoZCl4Hx8553HihG3bbwF9Ydn8fBmQuLsDrVvnGKZnGsCJh4mIqA5YPGXj7++Pffv2oUWLFti2bRu+/vprAEBGRgY0Go3FDTh06BCGDh1quK7P35gyZQrWrVuHcePGITU1FYsWLUJycjK6d++Obdu2wdvb2+LHahJiYuQoyMmTcmjAwUGWfY+KApKSZHGzlJSyFTMKhawgplSWnb+mEQ0ndOkin96RI2UjJM7ORQAazImHiYioDlgckDzzzDOYOHEinJ2dERgYiCFDhgCQUzmhNZjEHzJkCKpb6BMVFYWoqCiLj93kxMQA8+bJ89DcuCGnYjp3ltv37AFatZKVw+zsACcnmQXaqpW8DjTK4QSlUsZaTz4J3Lwpn5ZaXYqcHDkI1AgKyxIRkRksDkhmzZqFfv364fLlyxg+fDiUf38StGvXrs5zSGwpOjoa0dHRKG0odTnK54e0b1+WXHHhgiwBn5cnhwwCAuQJ8rp0kaMjeo14OCE8HLjzTuDbb2Xgcf26E0pLG0VhWSIiMlONlv326tULvXr1Mtp277331kmDGorIyEhERkYiOzsbbm5utm5OWX6Iry9QUCCHC3Q6mR9ibw+0aSMv588HPvgAOH9e5oxotXJkpJGcp6YyJSVypqlHD2DKlBKcPXsU99zTD927KxvbUyEiIhPMejufO3cucnNzzT7owoULkZ6eXuNGUSX0IyIlJXKdq04nA4ugIKB7d3lil9JSIDAQWL5cfnpnZgIXL8rLnj3l9kY4nHD0KJCbC7RoAdx/P9ClS1pDOfEwERHVEbNGSFatWoWFCxfCycnJrINGR0djxowZhtU4VAc8POTUzMmTMndEnyfSsqX8ZM7JKcsPCQ0F+vdvVOepqcrevfJywIBG+xSIiKgaZgUkQgh06tQJivI5CVWwZDSlobJpDolOZxxMhIQAhw7JqZeCAsDfH+jdu+zTubL8EKUSTaFSWGkpsH+//P32223bFiIish6zApK1a9dafODGvizXZjkkty7rVatlwOHsDLRtK5eWADKJtQnkh1Tn+HGZLuPqKmOtRlTTjYiILGD22X6pHuiX9erLvtvZycAkM1MGJkuWAL16lQUsKSlymqYJLzcpP12jUjEgISJqqmp9cj2qI+WX9bZrBxw4IDM5XV3lj0Yj5y4iI5tUfkhVdDpg3z75exOMtYiIqBwGJA1F+WW9OTnyRwg5AnLbbXJ1jb7se2hok8gPqU58vBwccnaWNd6IiKjpanpfq+tIvZ9cT7+sVwjg7Fl5aWcn66ZrNM3yLHIxMfKyX7+yYrNERNQ0MSAxod5PrufhIZMkTp6UcxV2dnJooBGXfa8NIcryR7i6hoio6avx985z587h/PnzGDx4MLRaLYQQZi8Lpkq0aSODjtxcef6ZXr1kgAI06rLvNXX6tEyn0Wpl3TciImraLB4hSUtLw7Bhw9CpUyeMHDkSSUlJAIDHHnsMzz33XJ03sFkoKABefRXw9gYcHeWKmvx8WYQjJ0eWgW+iy3pN0Y+O9OsnK+ITEVHTZvGn27PPPgs7OztcvnwZjo6Ohu3jxo3Dtm3b6rRxzUJJCfDGGzJvpG1b4OOPZdGzJlL2vSY4XUNE1PxYPGWzfft2/Pzzz2jTpo3R9o4dO+LSpUt11rAmq3wVVnd3YNs2IDZW5oe88grQqRMwalSzWNZryrlzQGqqzOXt2dPWrSEiovpgcUCSm5trNDKil56eDgcHhzppVENgldLxt1ZhzcmR0zLt2wPvvCODEaDJlH2vKf3qmt695ewVERE1fRZ/7R40aBA+++wzw3WFQgGdToelS5di6NChddo4W6rzVTb6KqxHjsiREY1GJrBmZQFpaTJAacYKCuTA0H33AXv2yG2criEiaj4sHiFZunQp7rrrLhw6dAhFRUWYP38+Tpw4gfT0dOzVT/yTsfJVWDt0kAHI1atySW9QkAxMoqNlBdZmNDVTmbw8uaBIo5EjJERE1DxY/OnXtWtXnDlzBgMHDsQ//vEP5ObmYsyYMYiNjUX79u2t0cbGr3wV1uxsICFBbvf1lT8+PmVVWJspnU52TWKivOzRQwYlRETUPNSoDombmxtefPHFum5L06WvwqrVyrwRhQJo2RLw95e3a7XyRHnNqApreTExwKpVQFycHCG5fl3m+N55Z7NZWERE1OzVKCApKCjAX3/9hevXr0N3y+lX77///jppWJPi4SE/YfPzATc3uWqm/Nf/ZlaFtTx9ak1ampytUihk3ZHERLm9Ga12JiJq1iwOSLZt24bJkyfjxo0bFW5TKBR1uyqlqejSRZ6TJjZWrqjRastua4ZVWPXKp9a0by+7QaEAWrQAOnaU9eCYWkNE1DxY/DY/e/ZsPPTQQ0hKSoJOpzP6aUrBSJ2eXE+pBKKi5AjI+fNly32baRVWvfKpNQoFUFwst3t4yOtMrSEiaj4s/gRMSUnB3Llz4e3tbY32NBh1vuw3PFzOP/To0ayrsJZXPrVGp5MxGgC4uMjLZniCYyKiZsviKZuxY8fi119/5YqamggPl/MPzbgKa3nlU2v052VUKMqKoTXj1BoiombH4oBk9erVeOihh/D7778jNDQU9rec+ezpp5+us8Y1Sc28Cmt55VNr3N3lNn1iazNOrSEiapYsDki++uorbN++HRqNBr/++isU+q+2kEmtDEjIXPrUmnnz5AyWEHJbTo5c+ttMU2uIiJoli9/qX3zxRSxZsgRZWVm4ePEiEhISDD8XLlywRhupCdOn1nh5leWRZGU169QaIqJmyeIRkqKiIowbNw5Kfm2lOhIeLn8SEwE/P+D//k8GJHyJERE1Hxa/5U+ZMgUbNmywRluomRICuHZNntrH2xvo2pXBCBFRc2PxCElpaSmWLl2Kn3/+Gd26dauQ1LpixYo6axw1D6mpsgaJQsHz1xARNVcWByTHjh1Djx49AADHjx83uq18gmtjFx0djejo6CZV7K2hunxZjoiMHSsrtxIRUfNjcUCye/dua7SjwYmMjERkZCSys7Ph5uZm6+Y0aYmJ8lJ/rkEiImp+OFNPNseAhIiIzBohGTNmDNatWwdXV1eMGTOmyn2///77OmkYNR8MSIiIyKyAxM3NzZAfwukLqktCMCAhIiIzA5K1a9fiX//6F+bNm4e1a9dau03UjGRkALm5coVN69a2bg0REdmK2TkkS5YsQU5OjjXbQs3QlSvy0scHuGUFORERNSNmByRCCGu2g5qpy5flZUCAbdtBRES2ZdEqm6ZUZ4QaBuaPEBERYGEdkk6dOlUblKSnp9eqQdS86KdsGJAQETVvFgUkS5Ys4SobqlP6KRsGJEREzZtFAcn48ePh5eVlrbZQM3PzJpCZKX9v08amTSEiIhszO4eE+SNU1/TTNa1aAVqtbdtCRES2xVU2JkRHRyMkJAR9+vSxdVOaLCa0EhGRntkBiU6na1bTNZGRkYiPj8fBgwdt3ZQmSx+QcMkvERHx5HpkM/qAhPkjRETEgIRshiMkRESkx4CEbKKgALh+Xf7OHBIiImJAQjahX2Hj5ga4uNi2LUREZHsMSMgmuMKGiIjKY0BCNsGAhIiIymNAQjbBgISIiMpjQEI2wYCEiIjKY0BC9a6kBEhKkr8zICEiIoABCdnAtWuATgc4OgItWti6NURE1BAwIKF6d/myvPT3B3jORiIiAhiQkA3oa5BwuoaIiPQYkFC9Kz9CQkREBDAgIRvgChsiIrpVkw9IEhMTMWTIEISEhKBbt27YuHGjrZvUrOl0wNWr8ncGJEREpGdn6wZYm52dHVauXInu3bsjOTkZvXr1wsiRI+Hk5GTrpjVLKSlAcTGgVgNeXrZuDRERNRRNPiDx9fWFr68vAMDHxwetWrVCeno6AxIb0U/XtGkDKJv8+BwREZnL5h8Je/bswahRo+Dn5weFQoEffvihwj7R0dFo27YtNBoN+vXrhwMHDtTosQ4fPozS0lL4c67AZvQJrW3a2LYdRETUsNg8IMnNzUVYWBiio6MrvX3Dhg2YO3cuXnnlFRw5cgRhYWGIiIjA9evXDft0794dXbt2rfBz7do1wz7p6emYPHkyPvroI6s/JzKNS36JiKgyNp+yGTFiBEaMGGHy9hUrVmDGjBmYNm0aAGDNmjXYvHkzPvnkEyxYsAAAEBcXV+VjFBYWYvTo0ViwYAHCw8Or3bewsNBwPTs7GwBQXFyM4uLiap+Pfh9z9m2OLl5UQqdTwM9Ph+JiYfH92b/Wxf61LvavdbF/rasm/WvJvjYPSKpSVFSEw4cPY+HChYZtSqUSw4YNw759+8w6hhACU6dOxZ133olJkyZVu/+bb76JJUuWVNi+fft2ODo6mt32HTt2mL1vcyEEcPBgbxQVqXDmzFFkZBTU+FjsX+ti/1oX+9e62L/WZUn/5uXlmb1vgw5Ibty4gdLSUnh7extt9/b2xqlTp8w6xt69e7FhwwZ069bNkJ+yfv16hIaGVrr/woULMXfuXMP17Oxs+Pv74+6774arq2u1j1dcXIwdO3Zg+PDhsLe3N6uNzcWNG4C7uwoqFTBx4p2wq8Grj/1rXexf62L/Whf717pq0r/6WQZzNOiApC4MHDgQOp3O7P0dHBzg4OBQYbu9vb1FL3BL928OkpPlyprWrQGttnbpS+xf62L/Whf717rYv9ZlSf9a8neweVJrVVq1agWVSoWUlBSj7SkpKfDx8bHqY0dHRyMkJAR9+vSx6uM0J/olvwEBtm0HERE1PA06IFGr1ejVqxd27dpl2KbT6bBr1y4MGDDAqo8dGRmJ+Ph4HDx40KqP05yUr0FCRERUns2nbHJycnDu3DnD9YSEBMTFxaFFixYICAjA3LlzMWXKFPTu3Rt9+/bFypUrkZuba1h1Q40Hz2FDRESm2DwgOXToEIYOHWq4rk8onTJlCtatW4dx48YhNTUVixYtQnJyMrp3745t27ZVSHSlhkunA06cAA4fBoqKOEJCREQV2TwgGTJkCISouh5FVFQUoqKi6qlFUnR0NKKjo1FaWlqvj9vUxMQAq1cDx48DCQkyqfXFF4E5c4BqSsIQEVEz0qBzSGyJOSS1FxMDzJsHHDkCaDSAVgs4OgJ//SW3x8TYuoVERNRQMCAhq9Dp5MhIejrQoQOgUMgfV1egfXsgIwOIjpb7ERERMSAhqzhxAjh5EvD1lYFIfr7crtXK6z4+QHy83I+IiIgBiQmsQ1I7GRlAYaEMQHQ64Px5IDMT0Nec02rl7RkZNm0mERE1EAxITGAOSe14eMjgIz9fnsPGxQVwd5c/gNzu4CD3IyIiYkBCVtGlCxAcLMvFZ2XJoESjAdRq+XtyMhASIvcjIiJiQEJWoVQCUVFyBOTCBaCkRI6S5OTI6RsPDyAyUu5HRETEjwOymvBwYPlywMlJBiS5uTKPpGdPuZ11SIiISM/mhdEaKhZGqxudOgGdO8vqrAsXAn5+cpqGIyNERFQePxZMYFJr3YiLk8t8e/cGIiKA0FAGI0REVBE/Gsiq4uLkZffutmwFERE1dAxIyGqEYEBCRETmYUBCVnPpklzy6+Ag80iIiIhMYUBCVqMfHenaFbBj+jQREVWBAYkJLB1fe/qApEcPmzaDiIgaAQYkJnCVTe0UFwPHj8vfw8Js2xYiImr4GJCQVZw+LU+e5+YGBAbaujVERNTQMSAhq4iNlZfdu8s6JERERFVhQEJWcfSovGT+CBERmYMBCdW53FzgzBn5O/NHiIjIHAxIqM799Zcsita6NdCqla1bQ0REjQEDEhO47LfmuNyXiIgsxYDEBC77rTl9/gjLxRMRkbkYkFCdSk0Frl6VZ/Tt2tXWrSEiosaCAQnVKf10TadOgJOTTZtCRESNCAMSqlM8uy8REdUEAxKqM0KU5Y9wuS8REVmCAQnVmYsXgawsQKMBOne2dWuIiKgxYUBCdUY/XdO1K2BnZ9OmEBFRI8OAhOoMl/sSEVFNMSAxgYXRLFNcDBw7Jn9n/ggREVmKAYkJLIxmPp0O+PFHIClJJrb6+9u6RURE1NgwIKFaiYkBHn0UmDtXjpDExgKTJsntRERE5mJAQjUWEwPMmwccOSJHSbRawNNTBiXz5jEoISIi8zEgoRrR6YDVq4H0dKBtW5lDolAA3t5A+/ZARgYQHS33IyIiqg4DEqqREyeAkycBX18gO1sGIDdvyuW+CgXg4wPEx8v9iIiIqsOAhGokIwMoLJTTNJmZclv52iNarbw9I8MmzSMiokaGAQnViIcH4OAA5OWVBST29mW35+fL2z08bNI8IiJqZBiQUI106QIEBwOXLgElJUCrVsCAAYBSKZf+JicDISFyPyIiouowIKEaUSqBqCiZL5KXBzg6ygTWnBzg/Hk5MhIZKfcjIiKqDs84QjU2YADQqZPMFVEq5cn1HByAnj1lMBIebusWEhFRY8GAhGrs5EmZNzJwILBggVxl4+Ehp2k4MkJERJZgQEI1pi981q8f0KOHbdtCRESNG7/HmsCT61VNiLKAhFMzRERUWwxITODJ9ap27hyQmgpoNDJnhIiIqDYYkFCN6EdHevcG1GrbtoWIiBo/BiRkMU7XEBFRXWNAQha7dAm4dk2usOnd29atISKipoABCVlMPzrSo4c8Zw0REVFtMSAhi+3bJy85XUNERHWFAQlZ5No1WZFVpZL1R4iIiOoCAxKyiH66pls3wNnZtm0hIqKmgwEJWYSra4iIyBoYkJDZrl8Hzp6VZ/gdMMDWrSEioqaEAQmZTZ/M2qUL4OZm27YQEVHTwoCEzMbpGiIishYGJGSWjAzg5En5O6driIiorjEgIbPs2ydLxt92G9Cqla1bQ0RETY2drRtADZtOB5w4AXz9NZCdDfTvb+sWERFRU9TkA5LMzEwMGzYMJSUlKCkpwZw5czBjxgxbN6tRiIkBVq8Gjh8HEhIApRL48kvAz495JEREVLeafEDi4uKCPXv2wNHREbm5uejatSvGjBmDli1b2rppDVpMDDBvHpCeDmg08pw1arXMI5k3D1i+nEEJERHVnSYfkKhUKjg6OgIACgsLIYSAEMLGrWrYdDo5MpKeDnToUFZ7xNsb8PUFzp8HoqPl9I2SWUhENqHT6VBUVGTrZjQoxcXFsLOzQ0FBAUpLS23dnCansv61t7eHSqWqk+PbPCDZs2cPli1bhsOHDyMpKQmbNm3C6NGjjfaJjo7GsmXLkJycjLCwMLz33nvo27ev2Y+RmZmJO+64A2fPnsWyZcvQilmZVTpxQo6E+PrK4CQrS2738JCBiY8PEB8v9wsNtW1biZqjoqIiJCQkQKfT2bopDYoQAj4+PkhMTIRCobB1c5ocU/3r7u4OHx+fWve5zQOS3NxchIWFYfr06RgzZkyF2zds2IC5c+dizZo16NevH1auXImIiAicPn0aXl5eAIDu3bujpKSkwn23b98OPz8/uLu74+jRo0hJScGYMWMwduxYeHt7W/25NVYZGUBhoZyqOX9eXlepAAcHebtWC6SkyO1EVL+EEEhKSoJKpYK/vz+UHKY00Ol0yMnJgbOzM/vFCm7tXyEE8vLycP36dQCAr69vrY5v84BkxIgRGDFihMnbV6xYgRkzZmDatGkAgDVr1mDz5s345JNPsGDBAgBAXFycWY/l7e2NsLAw/P777xg7dmyl+xQWFqKwsNBwPTs7G4AcqiouLq72MfT7mLNvQ+XsDKjVKpw/r0BWlhwZ6dRJTnPpdEBurswncXYuRX0/zabQvw0Z+9e66qJ/S0pKkJubCz8/P2g0mrpqWpMghEBRUREcHBw4QmIFlfWvg4MDdDodUlNT4eHhUWH6xpLXus0DkqoUFRXh8OHDWLhwoWGbUqnEsGHDsE9fx7waKSkpcHR0hIuLC7KysrBnzx7MnDnT5P5vvvkmlixZUmH79u3bDbko5tixY4fZ+zY0Oh1QWDgQiYmuUKtL4eeXi/z8YuTny1okyclOaN8+E5cuHUFiom3a2Jj7tzFg/1pXbfrXzs4OPj4+KCoqMnxhImM3b960dROatFv7V6fTIT8/H7t27aowW5GXl2f2cRt0QHLjxg2UlpZWmF7x9vbGqVOnzDrGpUuX8MQTTxiSWWfPno3QKhIfFi5ciLlz5xquZ2dnw9/fH3fffTdcXV2rfbzi4mLs2LEDw4cPh729vVltbGh27FDAw0OJ5GQFnJxU8PJSQ6sF8vPlVI2/P/Dqq1oMGDCy3tvWFPq3IWP/Wldd9G9BQQESExPh7OxcuxGSggIoHn4YACC++UbO0TZyQgjcvHkTLi4uHCGxAlP9W1BQAK1Wi8GDB1d4TVoSNDfogKQu9O3b1+wpHUAOPznokyXKsbe3t+gNxNL9G4o//wTWrAFatgRmzwbOnVPg5EkgNVXmkPTqBURGAuHhtp2fbaz921iwf62rNv1bWloKhUIBpVJZuzwJpVJmqQNQKJVNYsmcPslX3z+1cfHiRQQFBSE2Nhbdu3evg9Y1fqb6V6lUQqFQVPq6tuR13qBfga1atYJKpUJKSorR9pSUFPj4+Fj1saOjoxESEoI+ffpY9XFsSacDjh0D9uyRlydOAEuXymmZ4cOBJUuAzz8HPvtMBimffQasX8/6I0RNgk4nyy+npcnqh/W0YicxMRHTp0+Hn58f1Go1AgMDMWfOHKSlpRn2GTJkCBQKBd56660K97/33nuhUCiwePFisx/z119/hUKhQGZmZh08A7KWBh2QqNVq9OrVC7t27TJs0+l02LVrFwZY+QxvkZGRiI+Px8GDB636OLYSEwM8+igweTLw1FPA+PHAyJFAcjLQp48cBVEo5Jem0FBg8GB52QS+RBFRTAwwbRoQFye/jTz+uHxD0J/S20ouXLiA3r174+zZs/jqq69w7tw5rFmzxvCenp6ebtjX398f69atM7r/1atXsWvXrlqv5qCGyeYfLzk5OYiLizNMqyQkJCAuLg6XL18GAMydOxcff/wxPv30U5w8eRIzZ85Ebm6uYdUNWU5fhfXIEcDdXZaCT0uTPwkJMvioozo3RNTQ6N8A4uIAOzu5jt/NDYiNldutGJRERkZCrVZj+/btuOOOOxAQEIARI0Zg586duHr1Kl588UXDvvfddx9u3LiBvXv3GrZ9+umnuPvuuw0lH/TWr1+P3r17w83NDbfddhsmTpxoWIp68eJFDB06FADg4eEBhUKBqVOnApBfcJcuXYoOHTrAwcEBAQEBeP31142OfeHCBQwdOhSOjo4ICwszWlCRlpaGRx55BK1bt4ajoyNCQ0Px1VdfVdsPbdu2xRtvvIHp06fDxcUFAQEB+Oijjwy3VzaiExcXB4VCgYsXLwIA1q1bB3d3d/z000+47bbb4OjoiLFjxyIvLw+ffvop2rZtCw8PDzz99NNGReLatm2LV199FY888gicnJzQunVrREdHG26fPn067rvvPqP2FhcXw8vLC//5z3+qfW61YfOA5NChQ+jRowd69OgBQAYgPXr0wKJFiwAA48aNw/Lly7Fo0SJ0794dcXFx2LZtG+uI1NCtVVg1GuDcOTlN07Il4OICfPxxvY3eElFtCQEUFJj3k5cHrFolv320bVv2zUOrBYKC5PZ335X7mXM8C6pep6en4+eff8asWbOg1WqNbvPx8cHEiROxYcMGQyVttVqNiRMnYu3atYb91q1bh+nTp1c4dnFxMV599VXExsbi888/x6VLlwxBh7+/P7777jsAwOnTp5GUlIRVq1YBkIsY3nrrLbz88suIj4/Hl19+WeGz5cUXX8S8efMQFxeHTp064ZFHHjGsJCkoKECvXr2wefNmHD9+HE888QQmTZqEAwcOVNsfb7/9Nnr37o3Y2FjMmjULM2fOxOnTp83sTSkvLw/vvvsuvv76a2zbtg2//vorHnjgAWzZsgVbtmzB+vXr8eGHH+Lbb781ut+yZcsQFhaG2NhYLFiwAHPmzDGs/Hr88cexbds2JCUlGfb/6aefkJeXh3HjxlnUPkvZPKl1yJAh1ZZyj4qKQlRUVD21SIqOjkZ0dHSTKz9cvgprYaH8MlRSAnh6Ap07A0VFrMJK1KgUFgIPPWTevtnZZSMjmZllZZhjY+VlSQmwdSswYgRgxqpCbNxo9uqcs2fPQgiB4ODgSm8PDg5GRkYGUlNTDdumT5+OQYMGYdWqVTh8+DCysrJw3333Vcgf0QcpOp0OrVq1wsqVK9GvXz9DEa8WLVoAALy8vODu7g5ALl1dtWoVVq9ejSlTpgAA2rdvj4EDBxode968ebj33nsBAEuWLEGXLl1w7tw5dO7cGa1bt8a8efMM+86ePRs///wzvvnmm2qriY8cORKzZs0CALzwwgt45513sHv3btx2221V3q+84uJifPDBB2jfvj0AYOzYsVi/fj1SUlLg7OyMkJAQDB06FLt37zYKJm6//XZDHa9OnTph7969eOeddzB8+HCEh4fjtttuw/r16zF//nwAwNq1a/HQQw/B2dnZqkvNbT5C0lA11RwSfRXWggIZdJSUyFyRjh1lsTOtVt7OKqxETVBxsRz+NDUnq1LJ261YGK+6L6Bqtdrwe1hYGDp27Ihvv/0Wn3zyCSZNmgQ7u4rfow8fPoxRo0ahbdu28Pf3N0zR6Kf+K3Py5EkUFhbirrvuqrI93bp1M/yuz13RTweVlpbi1VdfRWhoKFq0aAFnZ2f8/PPPhsf94osv4OzsbPj5/fffKz2uQqGAj4+P4bjmcnR0NAQjgCyJ0bZtWzg7Oxttu/W4t+ZgDhgwACdPnjRcf/zxxw0jUykpKdi6dWulI1N1zeYjJGQdOp0MODIyZKXVLl1kQqpGI78UpaTIL0mtWwPt25d9ycnPl8t7PTxs234iMpODgxypMMfx4zKB1c1Nfvs4ckRu79FDBiM5OfIN4t13ga5dzXtsM3Xo0AEKhQInT57EAw88UOH2kydPwtPT0zCCoTd9+nRER0cjPj6+0qmQ3NxcREREICIiAuvXr4dWq0V6ejpGjBhR5ckHb502MqX8slV97Q398tdly5Zh1apVWLlyJUJDQ+Hk5IRnnnnG8Lj3338/+vXrZ7h/69atKz2u/tj64+qX1JYP3iqreFrZMao6rrkmT56MBQsWYN++fYiJiUFQUBAGDRpk9XMnMSBpgmJiZJ7IyZNytMPBAQgOBu67D9AvWCoslAXO2rQxlCL4uwor0LOnDGCIqBFQKMwvatazJxASIqdogoLK/vlVKvn79etyn54963xJXcuWLTF8+HC8//77ePbZZ40CguTkZHzxxReIjIyscL8JEyZg3rx5CAsLQ0hISIXbT506hbS0NLz11lto3bo1srOz8eOPPxrtox91KT8F37FjR2i1WuzatQuPP/54jZ7T3r178Y9//AOPPvooABmonDlzxtBOFxcXuLi4WHxcT09PAEBSUhI8/v52aEk9rers37+/wvXyU2ktW7bE6NGjsXbtWuzbt6/eFpFwysaExlqH5NYVNG3byi9Dv/0GREUBp07JL0MdOsigJDcXKC2VX4zOn5cjI5GRXN5L1CQplfKNwMND/sOXlMhvIvX0BrB69WoUFhYiIiICe/bsQWJiIrZt24bhw4ejU6dOhsUM5Xl4eCApKcmo/EN5AQEBUKvVeO+993DhwgVs2bKlwkqZwMBAKBQK/PTTT0hNTUVOTg40Gg1eeOEFzJ8/H5999hnOnz+P/fv3W7SSpGPHjtixYwdiYmJw8uRJPPnkkxXqZtVEhw4d4O/vj8WLF+Ps2bPYvHkz3n777VofV2/v3r1YunQpzpw5g+joaGzcuBFz5swx2ufxxx83rG7V59hYGz92TGiMOSS3rqBxdpZTwYmJ8rKoSE7JfPUV8P77MjDJzAQuXpSXPXsCy5ez8BlRkxYeLv/Ru3eXAUl+vpymqYc3gI4dO+LgwYNo164dHn74YQQGBmLEiBGGxMryuQ/lubu7w8nJqdLbPD09sW7dOmzcuBFdu3bFypUrsXTpUqN9WrdujSVLlmDBggXw9vY2LJJ4+eWX8dxzz2HRokUIDg7GuHHjLMrjeOmll9CzZ09ERERgyJAh8PHxwejRo82+vyn29vb46quvcOrUKXTr1g3//ve/8dprr9X6uHrPPfecYYXra6+9hhUrViAiIsJon2HDhsHX1xcRERHw8/Ors8eukqAqZWVlCQAiKyvLrP2LiorEDz/8IIqKiqzWptJSIf76S4jffpOXpaVy+19/CdG9uxBDhggxbJgQrq5CODgI4eUlhJ+fvK17d7lfVcdpyOqjf5sz9q911UX/5ufni/j4eJGfn1+7xuTmCjF4sBADBghx8KDN3gAWLVoknJ2dxb59+2p9rNLSUpGRkSFKG8ObmY0EBgaKd955p9r9bt68KVxdXcV3331n2Gaqf6t6TVryGcockkbGVH5IVFRZFeisLDkVU1Ag7+PiIhNXVSo5GqJfQaOvwkpEzZBSWba0t2tXm83TLlmyBG3btsX+/fvRt2/fWp+DhmpHp9Phxo0bePvtt+Hu7o7777+/3h6bAUkjos8PSU+XdUS0Whl47N0L/P474OUlgxE7u7IftRro1KksgZ4raIgIgEyE/d//bN0KAGDl7Qbk8uXLCAoKQps2bbBu3bpKl1lbCwMSExpaYbTy+SHt2gEHDsgREicneVtenkxO9faWvwcHGyfecwUNERHpS8+b0rZt22prxVgLx8ZMaGhJreUrrGZny9GOoiIZaGi1QECAHPl4+WX5+5Urch+uoCEiosaAH02NhL7Can4+cPasDETs7ORISLducnmvEEBgoEyU5woaIiJqTDhl00i4u8tgRF9htX17OXWjr2tUvsJqaCjQv3/llVqJiIgaIgYkDdCtZd9DQoB9++QUTWGhrK4aEFC2f2X5IVxBQ0REjQkDkgbm1mW9+vNMOTnJaZkbN2RgkpMjc0fy82UwwvwQIiJqzPjxZYItSsffWvbd31+Okpw7JwOUCROAtWuZH0JEtVdQAIwaJX/0NYuIbIkBiQn1vcrm1rLvajVw5ox8o3BxkeejiYmRuSGffw589hmwZo28XL+ewQgRNR1t27bFypUrDdcVCgV++OGHWh1z8eLF6N69u+H61KlT66TMe31at25dhbMhNyUMSBqI8st68/KAPXuAa9dkAmtIiJyuiY+X++nzQwYPlpecpiEiS+l0ZdWdjx+X1+tDYmIipk+fDj8/P6jVagQGBmLOnDlIS0sz7HPw4EE88cQTdfq48+bNMzpB36pVq7Bu3TrD9SFDhuCZZ56p08cky/CjrIHQL+stKZFn5NXpZKARHAw4Osp8kcLCsrLvREQ1FRMDTJsGxMUBx44Bjz8OPPqo3G5NFy5cQO/evXH27Fl89dVXOHfuHNasWYNdu3ZhwIABSE9PByBPmOfo6Finj+3s7IyWLVsarru5uTXp0YbGiAFJA+HhIZNVT56Uxczs7ORUjYODvL38sl4ioprS56rFxcn3Ga1WTgnHxsrt1gxKIiMjoVarsX37dtxxxx0ICAjAiBEjsHPnTly9ehUvvvgigIpTNgCQlJSEESNGQKvVol27dvj222+Nbj927JjhDLWenp544oknkJOTY7i9qimbqVOn4rfffsOqVaugUCigUChMVjTVT5v8/PPPCA4OhrOzM+655x4kJSUZ9qlstGX06NGYOnWq4Xrbtm3x2muvYfLkyXB2dkZgYCB+/PFHpKam4h//+AecnZ3RrVs3HDp0qEIbfvjhB3Ts2BEajQYRERFITEw03Hb+/Hn84x//gLe3N5ydndGnTx/s3Lmz0ufS0DAgaQCEkG8G+fkyZ6RFC2DIEKBfPzlKol/WGxLCsu9EZEwI+b5hzk9eHrBqlZymadtWnuMKkEFJUJDc/u67cj9zjmdJhfH09HT8/PPPmDVrFrRardFtPj4+mDhxIjZs2GCybPnLL7+MBx98EEePHsXEiRMxfvx4nDx5EgCQm5uLiIgIuLu7Y9euXdiwYQN27tyJqKgos9q2atUqDBgwADNmzEBSUhKSkpLg7+9vcv+8vDwsX74c69evx549e3D58mXMmzfPzJ4o88477+D2229HbGws7r33XkyaNAmTJ0/Go48+iiNHjqB9+/aYPHmyUZ/k5eXh9ddfx2effYa9e/ciMzMT48ePN9yek5ODkSNHYteuXYiNjcU999yDUaNG4fLlyxa3r75x2W89u7XGSKdOwHvvAb/9Jt8grl+X++XlcVkvEVWvsBB46CHz9s3OLhsZycyUJ+ME5BciQE4Zb90KjBhRdiLgqmzcaHzOrKqcPXsWQggEBwdXentwcDAyMjKQmppa6e0PPfQQHn/8cQDAq6++ih07duC9997D+++/jy+//BIFBQX49NNPUVpaCldXV6xevRqjRo3Cv//9b3h7e1fZNjc3N6jVajg6OsLHx6fa51JcXIw1a9agffv2AICoqCj861//qvZ+txo5ciSefPJJAMCiRYvwwQcfoE+fPnjo7z/oCy+8gAEDBiAlJcXQruLiYqxevRr9+vUDAHz66acIDg7GgQMH0LdvX4SFhSEsLMzwGK+++io2bdqEH3/80ewAzVYYkJhgjZPr3VpjxM5OvgF4eACensDixbLeiH6flBQ5TdOzpwxGuJKGiGqjuFh+KdKPjNxKpZJTx8XF1mtDdSduU+uLL91iwIABFa7HxcUBAE6ePImwsDA4OTkhOzsbAHD77bdDp9Ph9OnT1QYkpnTp0gWXLl0CAAwaNAhbt24FADg6OhqCEQDw9fXFdf23SQt069bN8Lu+jaHlKlrqt12/ft0QkNjZ2RmVo+jcuTPc3d1x8uRJ9O3bFzk5OVi8eDE2b96MpKQklJSUID8/nyMkjVlkZCQiIyORnZ0NNze3Wh9PP2+bni5X0igUMnk1O1uOisyYAQwfLvdl2XciMpeDgxypMMfx4zKB1c1NjsAeOSK39+ghg5GcHDlq8u67QNeu5j22uTp06ACFQoGTJ0/igQceqHD7yZMn4enp2aASTbds2YLiv6Oz8tNM9vb2RvspFAqjQEupVFYIvIorifLKH0fx93lAKtums2AJ1Lx587Bjxw4sX74cHTp0gFarxdixY1FUVGT2MWyFH3P14NYaIwBw+rRMXvXwkEXQduwoW3bHZb1EZC6FQk6bmPPTs6fMRbt+Xb6vKBTyR6WSl9evyy9APXuadzz9ubTM0bJlSwwfPhzvv/8+8vPzjW5LTk7GF198YZT0eav9+/dXuK6f/gkODsbRo0eRm5truH3v3r1QKpW47bbbzGqfWq2uMCIeGBiIDh06oEOHDmjdurVZxwHkKqHySa6lpaU4fvy42fevSklJiVGi6+nTp5GZmWnoi71792Lq1Kl44IEHEBoaCh8fH5MJug0NP+rqQfkaI2lp8rw0N27I5bxdusiKrPoaI0RE1qJUAlFR8ovQ+fNyylgIOTJy/rz1c9VWr16NwsJCREREYM+ePUhMTMS2bdswfPhwdOrUCYsWLTJ5340bN+KTTz7BmTNn8Morr+DAgQOGnIiJEydCo9Fg6tSpiI+Px+7duzF79mxMmjTJ7Omatm3b4s8//8TFixdx48YNi0YlbnXnnXdi8+bN2Lx5M06dOoWZM2ciMzOzxscrz97eHrNnz8aff/6Jw4cPY+rUqejfvz/69u0LAOjYsSO+//57xMXF4ejRo5gwYUKtnkt9YkBSD/Q1RrRawN5evgHY2wO33SYvWWOEiOpLeLg81UT37jIgyc+X0zT1cQqKjh074uDBg2jXrh0efvhhBAYGYsSIEejUqRP27t0LZ2dnk/ddsmQJvv76a3Tr1g2fffYZvvrqK4SEhACQOR0///wzMjIycNddd+Hhhx/GXXfdhdWrV5vdtnnz5kGlUiEkJASenp61yrmYPn06pkyZgsmTJ+OOO+5Au3btMHTo0BofrzxHR0e88MILmDBhAm6//XY4Oztjw4YNhttXrFgBDw8PhIeHY9SoUYiIiEDPnj3r5LGtTSGqyzBq5vQ5JFlZWXA1I+28uLgYW7ZswciRIw1zgceOAZMny6kZZ2cgN1eOjuiHO3NyZMb7Z5/xDL3Vqax/qe6wf62rLvq3oKAACQkJCAoKgsbcJS6VyMuTq2mKi2XOSM+etpkefuWVV7BixQrs2LED/fv3r9WxdDodsrOz4erqCuUtT2bhwoX4/fff8ccff9TqMZozU/1b1WvSks9QjpDUgy5dZMXV5GQ5OuLkVBaMsMYIEdmCUimX9rZsKRNYbZWrtmTJErz77rvYv3+/VaYWhBA4f/48du3ahS58k23QuMqmHujnbefNk/O0Pj6sMUJEtqXRAP/7n61bIU2bNs1qx87KyjKcuf2f//yn1R6Hao8BST3Rz9uyxggRUf1xd3dHYWGhrZtBZmBAYoI1CqOFh7PGCBERUWUYkJhQ14XR9PQ1RoiIiKgMv5sTETVCXCBJDUVdvRYZkBARNSKqv09E0xhKgVPzkJeXB6BiSX1LccqGiKgRsbOzg6OjI1JTU2Fvb1+h3kZzptPpUFRUhIKCAvaLFdzav0II5OXl4fr163B3dzcEyzXFgISIqBFRKBTw9fVFQkKC4Uy0JAkhkJ+fD61WazgxHdUdU/3r7u5uOBtxbTAgISJqZNRqNTp27Mhpm1sUFxdjz549GDx4MCsNW0Fl/Wtvb1/rkRE9BiRERI2QUqmsVen4pkilUqGkpAQajYYBiRVYu385yUZEREQ2x4CEiIiIbI4BCREREdkcc0iqoS/4kp2dbdb+xcXFyMvLQ3Z2NucwrYD9a13sX+ti/1oX+9e6atK/+s9Oc4qnMSCpxs2bNwEA/v7+Nm4JERFR43Tz5s1qT8OiEKw/XCWdTodr167BxcXFrHXt2dnZ8Pf3R2JiIlxdXeuhhc0L+9e62L/Wxf61LvavddWkf4UQuHnzJvz8/KotVscRkmoolUq0adPG4vu5urryH8KK2L/Wxf61LvavdbF/rcvS/jX3BLVMaiUiIiKbY0BCRERENseApI45ODjglVdegYODg62b0iSxf62L/Wtd7F/rYv9al7X7l0mtREREZHMcISEiIiKbY0BCRERENseAhIiIiGyOAQkRERHZHAOSOhQdHY22bdtCo9GgX79+OHDggK2b1Ci9+eab6NOnD1xcXODl5YXRo0fj9OnTRvsUFBQgMjISLVu2hLOzMx588EGkpKTYqMWN21tvvQWFQoFnnnnGsI39WztXr17Fo48+ipYtW0Kr1SI0NBSHDh0y3C6EwKJFi+Dr6wutVothw4bh7NmzNmxx41FaWoqXX34ZQUFB0Gq1aN++PV599VWjc6Wwfy2zZ88ejBo1Cn5+flAoFPjhhx+MbjenP9PT0zFx4kS4urrC3d0djz32GHJycixriKA68fXXXwu1Wi0++eQTceLECTFjxgzh7u4uUlJSbN20RiciIkKsXbtWHD9+XMTFxYmRI0eKgIAAkZOTY9jnqaeeEv7+/mLXrl3i0KFDon///iI8PNyGrW6cDhw4INq2bSu6desm5syZY9jO/q259PR0ERgYKKZOnSr+/PNPceHCBfHzzz+Lc+fOGfZ56623hJubm/jhhx/E0aNHxf333y+CgoJEfn6+DVveOLz++uuiZcuW4qeffhIJCQli48aNwtnZWaxatcqwD/vXMlu2bBEvvvii+P777wUAsWnTJqPbzenPe+65R4SFhYn9+/eL33//XXTo0EE88sgjFrWDAUkd6du3r4iMjDRcLy0tFX5+fuLNN9+0YauahuvXrwsA4rfffhNCCJGZmSns7e3Fxo0bDfucPHlSABD79u2zVTMbnZs3b4qOHTuKHTt2iDvuuMMQkLB/a+eFF14QAwcONHm7TqcTPj4+YtmyZYZtmZmZwsHBQXz11Vf10cRG7d577xXTp0832jZmzBgxceJEIQT7t7ZuDUjM6c/4+HgBQBw8eNCwz9atW4VCoRBXr141+7E5ZVMHioqKcPjwYQwbNsywTalUYtiwYdi3b58NW9Y0ZGVlAQBatGgBADh8+DCKi4uN+rtz584ICAhgf1sgMjIS9957r1E/Auzf2vrxxx/Ru3dvPPTQQ/Dy8kKPHj3w8ccfG25PSEhAcnKyUf+6ubmhX79+7F8zhIeHY9euXThz5gwA4OjRo/jjjz8wYsQIAOzfumZOf+7btw/u7u7o3bu3YZ9hw4ZBqVTizz//NPuxeHK9OnDjxg2UlpbC29vbaLu3tzdOnTplo1Y1DTqdDs888wxuv/12dO3aFQCQnJwMtVoNd3d3o329vb2RnJxsg1Y2Pl9//TWOHDmCgwcPVriN/Vs7Fy5cwAcffIC5c+fin//8Jw4ePIinn34aarUaU6ZMMfRhZe8X7N/qLViwANnZ2ejcuTNUKhVKS0vx+uuvY+LEiQDA/q1j5vRncnIyvLy8jG63s7NDixYtLOpzBiTUoEVGRuL48eP4448/bN2UJiMxMRFz5szBjh07oNFobN2cJken06F379544403AAA9evTA8ePHsWbNGkyZMsXGrWv8vvnmG3zxxRf48ssv0aVLF8TFxeGZZ56Bn58f+7eR45RNHWjVqhVUKlWFVQgpKSnw8fGxUasav6ioKPz000/YvXs32rRpY9ju4+ODoqIiZGZmGu3P/jbP4cOHcf36dfTs2RN2dnaws7PDb7/9hnfffRd2dnbw9vZm/9aCr68vQkJCjLYFBwfj8uXLAGDoQ75f1Mzzzz+PBQsWYPz48QgNDcWkSZPw7LPP4s033wTA/q1r5vSnj48Prl+/bnR7SUkJ0tPTLepzBiR1QK1Wo1evXti1a5dhm06nw65duzBgwAAbtqxxEkIgKioKmzZtwi+//IKgoCCj23v16gV7e3uj/j59+jQuX77M/jbDXXfdhWPHjiEuLs7w07t3b0ycONHwO/u35m6//fYKy9TPnDmDwMBAAEBQUBB8fHyM+jc7Oxt//vkn+9cMeXl5UCqNP7pUKhV0Oh0A9m9dM6c/BwwYgMzMTBw+fNiwzy+//AKdTod+/fqZ/2C1TsklIYRc9uvg4CDWrVsn4uPjxRNPPCHc3d1FcnKyrZvW6MycOVO4ubmJX3/9VSQlJRl+8vLyDPs89dRTIiAgQPzyyy/i0KFDYsCAAWLAgAE2bHXjVn6VjRDs39o4cOCAsLOzE6+//ro4e/as+OKLL4Sjo6P4/PPPDfu89dZbwt3dXfz3v/8Vf/31l/jHP/7BZalmmjJlimjdurVh2e/3338vWrVqJebPn2/Yh/1rmZs3b4rY2FgRGxsrAIgVK1aI2NhYcenSJSGEef15zz33iB49eog///xT/PHHH6Jjx45c9mtL7733nggICBBqtVr07dtX7N+/39ZNapQAVPqzdu1awz75+fli1qxZwsPDQzg6OooHHnhAJCUl2a7RjdytAQn7t3b+97//ia5duwoHBwfRuXNn8dFHHxndrtPpxMsvvyy8vb2Fg4ODuOuuu8Tp06dt1NrGJTs7W8yZM0cEBAQIjUYj2rVrJ1588UVRWFho2If9a5ndu3dX+p47ZcoUIYR5/ZmWliYeeeQR4ezsLFxdXcW0adPEzZs3LWqHQohy5e2IiIiIbIA5JERERGRzDEiIiIjI5hiQEBERkc0xICEiIiKbY0BCRERENseAhIiIiGyOAQkRERHZHAMSIiIisjkGJETU6Fy8eBEKhQJxcXEm9/n111+hUCgqnCSQiBomBiREVK8SExMxffp0+Pn5Qa1WIzAwEHPmzEFaWlqdPk54eDiSkpLg5uYGAFi3bh3c3d3r9DGIqO4wICGienPhwgX07t0bZ8+exVdffYVz585hzZo1hjNjp6en19ljqdVq+Pj4QKFQ1Nkxich6GJAQUb2JjIyEWq3G9u3bcccddyAgIAAjRozAzp07cfXqVbz44osAAIVCgR9++MHovu7u7li3bp3RtlOnTiE8PBwajQZdu3bFb7/9Zrit/JTNr7/+imnTpiErKwsKhQIKhQKLFy8GALz//vvo2LEjNBoNvL29MXbsWGt2ARGZwICEiOpFeno6fv75Z8yaNQtardboNh8fH0ycOBEbNmyAJef7fP755/Hcc88hNjYWAwYMwKhRoyqd+gkPD8fKlSvh6uqKpKQkJCUlYd68eTh06BCefvpp/Otf/8Lp06exbds2DB48uNbPlYgsx4CEiOrF2bNnIYRAcHBwpbcHBwcjIyMDqampZh8zKioKDz74IIKDg/HBBx/Azc0N//nPfyrsp1ar4ebmBoVCAR8fH/j4+MDZ2RmXL1+Gk5MT7rvvPgQGBqJHjx54+umna/wciajmGJAQUb2qbgRErVabfawBAwYYfrezs0Pv3r1x8uRJs+8/fPhwBAYGol27dpg0aRK++OIL5OXlmX1/Iqo7DEiIqF506NABCoXCZMBw8uRJeHp6wt3dHQqFokLgUlxcXOdtcnFxwZEjR/DVV1/B19cXixYtQlhYGJcKE9kAAxIiqhctW7bE8OHD8f777yM/P9/otuTkZHzxxReYOnUqAMDT0xNJSUmG28+ePVvpyMX+/fsNv5eUlODw4cMmp4TUajVKS0srbLezs8OwYcOwdOlS/PXXX7h48SJ++eWXmjxFIqoFO1s3gIiaj9WrVyM8PBwRERF47bXXEBQUhBMnTuD5559Hp06dsGjRIgDAnXfeidWrV2PAgAEoLS3FCy+8AHt7+wrHi46ORseOHREcHIx33nkHGRkZmD59eqWP3bZtW+Tk5GDXrl0ICwuDo6MjfvnlF1y4cAGDBw+Gh4cHtmzZAp1Oh9tuu82q/UBEFXGEhIjqTceOHXHw4EG0a9cODz/8MAIDAzFixAh06tQJe/fuhbOzMwDg7bffhr+/PwYNGoQJEyZg3rx5cHR0rHC8t956C2+99RbCwsLwxx9/4Mcff0SrVq0qfezw8HA89dRTGDduHDw9PbF06VK4u7vj+++/x5133ong4GCsWbMGX331Fbp06WLVfiCiihTCkjV2RER17JVXXsGKFSuwY8cO9O/f39bNISIbYUBCRDa3du1aZGVl4emnn4ZSyYFbouaIAQkRERHZHL+KEBERkc0xICEiIiKbY0BCRERENseAhIiIiGyOAQkRERHZHAMSIiIisjkGJERERGRzDEiIiIjI5hiQEBERkc39P5NXPA8HZiT7AAAAAElFTkSuQmCC\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "plt.figure(figsize=(6, 6 * 6/8))\n", - "plt.title(\"Simple test of CPU 12th Gen Intel® Core™ i7-1260P\")\n", - "plt.errorbar(qmatcha_range, qmatcha_times, yerr=qmatcha_errs, marker=\"o\", color=\"red\", label=\"QMatcha-numpy\", alpha=0.7)\n", - "plt.errorbar(qibojit_range, qibojit_times, yerr=qibojit_errs, marker=\"o\", color=\"blue\", label=\"Qibojit-numba\", alpha=0.7)\n", - "plt.legend()\n", - "plt.xlabel(\"Qubits\")\n", - "plt.ylabel(\"Time [s]\")\n", - "plt.grid(True)\n", - "plt.yscale(\"log\")" + "# Frequencies and probabilities\n", + "print(f\"Frequencies:\\n {outcome.frequencies()}\\n\")\n", + "print(f\"Probabilities:\\n {outcome.probabilities()}\\n\")\n", + "print(f\"State:\\n {outcome.state()}\\n\") # Only if return_array = True" ] }, { @@ -655,12 +418,18 @@ "id": "dd84f1f3-7aa5-4ad1-ae09-81e0aff75b5b", "metadata": {}, "source": [ - "### Compute expectation values" + "### Compute expectation values\n", + "\n", + "Another important feature of this backend is the `expectation` function. In fact, we can compute expectation values of given observables thorugh a Qibo-friendly interface.\n", + "\n", + "---\n", + "\n", + "Let's start by importing some symbols, thanks to which we can build our observable." ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 12, "id": "0b46e315-7786-4247-bd2a-83ea1c5842eb", "metadata": {}, "outputs": [], @@ -670,7 +439,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 13, "id": "37385485-e8a3-4ab0-ad44-bcc4e9da24ca", "metadata": {}, "outputs": [ @@ -678,10 +447,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "0: ─RY─RZ─o─────X─RY─RZ─o─────X─M─\n", - "1: ─RY─RZ─X─o───|─RY─RZ─X─o───|─M─\n", - "2: ─RY─RZ───X─o─|─RY─RZ───X─o─|─M─\n", - "3: ─RY─RZ─────X─o─RY─RZ─────X─o─M─\n" + "0: ─RY─RZ─o─────X─RY─RZ─o─────X─RY─RZ─o─────X─M─\n", + "1: ─RY─RZ─X─o───|─RY─RZ─X─o───|─RY─RZ─X─o───|─M─\n", + "2: ─RY─RZ───X─o─|─RY─RZ───X─o─|─RY─RZ───X─o─|─M─\n", + "3: ─RY─RZ─────X─o─RY─RZ─────X─o─RY─RZ─────X─o─M─\n" ] } ], @@ -697,7 +466,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 14, "id": "ddecc910-7804-4199-8577-a7db38a16db8", "metadata": {}, "outputs": [ @@ -705,77 +474,51 @@ "name": "stderr", "output_type": "stream", "text": [ - "[Qibo 0.2.15|INFO|2025-02-10 16:44:30]: Using qibojit (numba) backend on /CPU:0\n" + "[Qibo 0.2.15|INFO|2025-02-12 14:36:17]: Using qibojit (numba) backend on /CPU:0\n" ] - } - ], - "source": [ - "# We can create an Hamiltonian form\n", - "form = 0.5 * Z(0) * Z(1) +- 1.5 * X(0) * Z(2) + Z(3)\n", - "hamiltonian = hamiltonians.SymbolicHamiltonian(form)" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "a6599df3-989e-4131-8664-3451d0cc4372", - "metadata": {}, - "outputs": [ + }, { "data": { + "text/latex": [ + "$\\displaystyle - 1.5 X_{0} Z_{2} + 0.5 Z_{0} Z_{1} + Z_{3}$" + ], "text/plain": [ - "\u001b[0;31mSignature:\u001b[0m \u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobservable\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mDocstring:\u001b[0m\n", - "Compute the expectation value of a Qibo-friendly ``observable`` on\n", - "the Tensor Network constructed from a Qibo ``circuit``.\n", - "\n", - "This method takes a Qibo-style symbolic Hamiltonian (e.g., `X(0)*Z(1) + 2.0*Y(2)*Z(0)`)\n", - "as the observable, converts it into a Quantum Matcha Tea (qmatchatea) observable\n", - "(using `TNObsTensorProduct` and `TNObsWeightedSum`), and computes its expectation\n", - "value using the provided circuit.\n", - "\n", - "Args:\n", - " circuit: A Qibo quantum circuit object on which the expectation value\n", - " is computed. The circuit should be compatible with the qmatchatea\n", - " Tensor Network backend.\n", - " observable: The observable whose expectation value we want to compute.\n", - " This must be provided in the symbolic Hamiltonian form supported by Qibo\n", - " (e.g., `X(0)*Y(1)` or `Z(0)*Z(1) + 1.5*Y(2)`).\n", - "\n", - "Returns:\n", - " qibotn.TensorNetworkResult class, providing methods to retrieve\n", - " probabilities, frequencies and state always according to the chosen\n", - " simulation setup.\n", - "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", - "\u001b[0;31mType:\u001b[0m method\n" + "-1.5*X0*Z2 + 0.5*Z0*Z1 + Z3" ] }, + "execution_count": 14, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ - "qmatcha_backend.expectation?" + "# We can create a symbolic Hamiltonian\n", + "form = 0.5 * Z(0) * Z(1) +- 1.5 * X(0) * Z(2) + Z(3)\n", + "hamiltonian = hamiltonians.SymbolicHamiltonian(form)\n", + "\n", + "# Let's show it\n", + "hamiltonian.form" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 15, "id": "163b70a3-814a-4a62-a98a-2ffca933a544", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "-0.21133814688683614" + "0.4355195352502318" ] }, - "execution_count": 31, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# And compute its expectation value\n", "qmatcha_backend.expectation(\n", " circuit=circuit,\n", " observable=hamiltonian,\n", @@ -784,24 +527,23 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 16, "id": "2d8c4a9c-eca3-49d0-bdbf-ab054172c4e5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "-0.2113381468868367" + "0.43551953525022985" ] }, - "execution_count": 32, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Try with Qibo\n", - "\n", + "# Try with Qibo (which is by default using the Qibojit backend)\n", "hamiltonian = hamiltonians.SymbolicHamiltonian(form)\n", "hamiltonian.expectation(circuit().state())" ]