diff --git a/tests/lava/tutorials/test_tutorials.py b/tests/lava/tutorials/test_tutorials.py
index 349e828d0..c45a38967 100644
--- a/tests/lava/tutorials/test_tutorials.py
+++ b/tests/lava/tutorials/test_tutorials.py
@@ -216,6 +216,14 @@ def test_end_to_end_01_mnist(self):
"tutorial01_mnist_digit_classification.ipynb", e2e_tutorial=True
)
+ @unittest.skipIf(system_name != "linux", "Tests work on linux")
+ def test_end_to_end_02_ei_network(self):
+ """Test tutorial end to end 02 E/I network."""
+ self._run_notebook(
+ "tutorial02_excitatory_inhibitory_network.ipynb",
+ e2e_tutorial=True
+ )
+
@unittest.skip("Tutorial is text only and does not contain code")
def test_in_depth_01_install_lava(self):
"""Test tutorial in depth install lava."""
diff --git a/tutorials/end_to_end/convert_params.py b/tutorials/end_to_end/convert_params.py
new file mode 100644
index 000000000..84febe0c1
--- /dev/null
+++ b/tutorials/end_to_end/convert_params.py
@@ -0,0 +1,331 @@
+# Copyright (C) 2021-22 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# See: https://spdx.org/licenses/
+import numpy as np
+import warnings
+from scipy.optimize import fsolve
+from scipy.special import zetac
+from scipy.special import erf
+
+
+# Define auxiliary functions for weight conversion.
+def _mean_input(num_neurons_exc, gamma, g_factor, weight, rate, bias):
+ '''
+ Calculate mean input to single neuron given mean excitatory weight.
+
+ Parameters
+ ----------
+ num_neurons_exc : int
+ Number of excitatory neurons
+ gamma : float
+ Ratio of inhibitory and excitatory neurons
+ g_factor : float
+ Factor controlling inhibition-excitation balance
+ weight : float
+ Mean excitatory weight
+ rate : float
+ Mean rate of neurons in network
+ bias : float
+ Bias provided to neurons
+
+ Returns
+ -------
+ mean_inp : float
+ Mean input received by each neuron
+ '''
+ mean_inp = num_neurons_exc * (1 - gamma * g_factor) * weight * rate + bias
+
+ return mean_inp
+
+def _std_input(num_neurons_exc, gamma, g_factor, weight, rate):
+ '''
+ Calculate mean input to single neuron given mean excitatory weight.
+
+ Parameters
+ ----------
+ num_neurons_exc : int
+ Number of excitatory neurons
+ gamma : float
+ Ratio of inhibitory and excitatory neurons
+ g_factor : float
+ Factor controlling inhibition-excitation balance
+ weight : float
+ Mean excitatory weight
+ rate : float
+ Mean rate of neurons in network
+
+ Returns
+ -------
+ mean_inp : float
+ Mean input received by each neuron
+ '''
+ return num_neurons_exc * (1 + gamma * g_factor**2) * weight ** 2 * rate
+
+def _y_th(vth, mean, std, dv_exc, du_exc):
+ '''
+ Effective threshold, see Grytskyy et al. 2013.
+
+ Parameters
+ ----------
+ vth : float
+ Threshold of LIF neuron
+ mean : float
+ Mean input of neuron
+ std : float
+ Standard deviation of input
+ dv_exc : float
+ Integration constant of voltage variable
+ du_exc : float
+ Integration constant of current variable
+
+ Returns
+ -------
+ yth : float
+ Effective threshold of neuron in network
+ '''
+ y_th = (vth - mean) / std
+ y_th += np.sqrt(2) * np.abs(zetac(0.5)) * np.sqrt(dv_exc / du_exc) / 2
+
+ return y_th
+
+def _y_r(mean, std, dv_exc, du_exc):
+ '''
+ Effective reset, see Grytskyy et al. 2013.
+
+ Parameters
+ ----------
+ vth : float
+ Threshold of LIF neuron
+ mean : float
+ Mean input of neuron
+ std : float
+ Standard deviation of input
+ dv_exc : float
+ Integration constant of voltage variable
+ du_exc : float
+ Integration constant of current variable
+
+ Returns
+ -------
+ yr : float
+ Effective reset of neuron in network
+ '''
+ y_r = (- 1 * mean) / std
+ y_r += np.sqrt(2) * np.abs(zetac(0.5)) * np.sqrt(dv_exc / du_exc) / 2
+
+ return y_r
+
+def f(y):
+ '''
+ Derivative of transfer function of LIF neuron at given argument.
+ '''
+ return np.exp(y ** 2) * (1 + erf(y))
+
+def _alpha(vth, mean, std, dv_exc, du_exc):
+ '''
+ Auxiliary variable describing contribution of weights for weight
+ mapping given network state, see Grytskyy et al. 2013.
+
+ Parameters
+ ----------
+ vth : float
+ Threshold of LIF neuron
+ mean : float
+ Mean input of neuron
+ std : float
+ Standard deviation of input
+ dv_exc : float
+ Integration constant of voltage variable
+ du_exc : float
+ Integration constant of current variable
+
+ Returns
+ -------
+ val : float
+ Contribution of weight
+ '''
+ val = np.sqrt(np.pi) * (mean * dv_exc * 0.01) ** 2
+ val *= 1 / std
+ val *= (f(_y_th(vth, mean, std, dv_exc, du_exc))
+ - f(_y_r(mean, std, dv_exc, du_exc)))
+
+ return val
+
+def _beta(vth, mean, std, dv_exc, du_exc):
+ '''
+ Auxiliary variable describing contribution of square of weights for
+ weight mapping given network state, see Grytskyy et al. 2013.
+
+ Parameters
+ ----------
+ vth : float
+ Threshold of LIF neuron
+ mean : float
+ Mean input of neuron
+ std : float
+ Standard deviation of input
+ dv_exc : float
+ Integration constant of voltage variable
+ du_exc : float
+ Integration constant of current variable
+
+ Returns
+ -------
+ val : float
+ Contribution of square of weights
+ '''
+ val = np.sqrt(np.pi) * (mean * dv_exc * 0.01) ** 2
+ val *= 1/(2 * std ** 2)
+ val *= (f(_y_th(vth, mean, std, dv_exc, du_exc)) * (vth - mean) / std
+ - f(_y_r(mean, std, dv_exc, du_exc)) * (-1 * mean) / std)
+
+ return val
+
+def convert_rate_to_lif_params(shape_exc, dr_exc, bias_exc, shape_inh, dr_inh,
+ bias_inh, g_factor, q_factor, weights, **kwargs):
+ '''Convert rate parameters to LIF parameters.
+ The mapping is based on A unified view on weakly correlated recurrent
+ network, Grytskyy et al. 2013.
+
+ Parameters
+ ----------
+ shape_exc : int
+ Number of excitatory neurons in rate network
+ dr_exc : float
+ Integration constant for excitatory neurons in rate network
+ bias_exc : float
+ Bias for excitatory neurons in rate network
+ shape_inh : int
+ Number of inhibitory neurons in rate network
+ dr_inh : float
+ Integration constant for inhibitory neurons in rate network
+ bias_inh : float
+ Bias for inhibitory neurons in rate network
+ g_factor : float
+ Factor controlling inhibition-excitation balance
+ q_factor : float
+ Factor controlling response properties of rate network
+ weights : np.ndarray
+ Recurrent weights of rate network
+
+ Returns
+ -------
+ lif_network_dict : dict
+ Parameter dictionary for LIF network
+ '''
+ # Copy weight parameters.
+ weights_local = weights.copy()
+
+ num_neurons_exc = shape_exc
+ num_neurons_inh = shape_inh
+
+ # Ratio of excitatory to inhibitory neurons.
+ gamma = float(num_neurons_exc) / float(num_neurons_inh)
+
+ # Assert that network is balanced.
+ assert gamma * g_factor > 1, "Network not balanced, increase g_factor"
+
+ # Set timescales of neurons.
+ dv_exc = 1 * dr_exc # Dynamics of membrane potential as fast as rate.
+ du_exc = 7 * dr_exc # Dynamics of current 7 times as fast as rate.
+
+ dv_inh = 1 * dr_inh # Dynamics of membrane potential as fast as rate.
+ du_inh = 7 * dr_inh # Dynamics of current 7 times as fast as rate.
+
+ # Set threshold to default value.
+ vth_exc = 1
+ vth_inh = 1
+
+ # Set biases.
+ # First calculate relative biases for rate model.
+ if bias_exc >= bias_inh:
+ rel_exc_inh_bias = bias_exc / bias_inh
+ rel_inh_exc_bias = 1
+ else:
+ rel_inh_exc_bias = bias_inh / bias_exc
+ rel_exc_inh_bias = 1
+
+ # We then determine the the bias for the LIF network.
+ # We have to be careful not the reduce the bias since a too small bias
+ # results in inactivity.
+ bias_exc = 5 * vth_exc * dv_exc * rel_exc_inh_bias
+ bias_inh = 5 * vth_inh * dv_inh * rel_inh_exc_bias
+
+ # Get the mean excitatory weight.
+ exc_weights = weights_local[:, :num_neurons_exc]
+ mean_exc_weight = np.mean(exc_weights)
+
+ # Perform weight conversion.
+
+ # First determine approximately stationary firing rate in inhibition
+ # dominated regime.
+ # See Dynamic of Sparsely Connected Networks of Excitatory and
+ # Inhibitory Spiking Neurons, Brunel, 2000.
+ # We simplify the calculation by working with average acitivites.
+ bias = (bias_exc / dv_exc + bias_inh / dv_inh) / 2
+ rate = (bias - vth_exc) / (gamma * g_factor - 1)
+
+ # Function describing mapping of rate to LIF weights problem about
+ # finding a zero.
+ def func(weight):
+ '''
+ Adapted from Grytskyy et al..
+ '''
+ mean_inp = _mean_input(num_neurons_exc, gamma,
+ g_factor, weight, rate, bias)
+ std_inp = _std_input(num_neurons_exc, gamma,
+ g_factor, weight, rate)
+ alpha = _alpha(vth_exc, mean_inp, std_inp, dv_exc, du_inh)
+ beta = _beta(vth_exc, mean_inp, std_inp, dv_exc, du_inh)
+
+ return mean_exc_weight - alpha * weight - beta * weight**2
+
+ # Solve for weights of LIF network retaining correlation structure of
+ # rate network.
+ with warnings.catch_warnings():
+ warnings.filterwarnings('ignore', '', RuntimeWarning)
+ try:
+ mean_exc_weight_new = fsolve(func, mean_exc_weight)[0]
+ # Determine weight scaling factor
+ weight_scale = mean_exc_weight_new / mean_exc_weight
+ except Warning:
+ # Theory breaks done, most likely due to strong correlations
+ # induced by strong weights. Choose 1 as scaling factor.
+ weight_scale = 1
+
+ # Scale weights.
+ if weight_scale > 0:
+ weights_local *= weight_scale
+ else:
+ print('Weigh scaling factor not positive: No weight scaling possible')
+
+ # Scale weights with integration time step.
+ weights_local[:, :num_neurons_exc] *= du_exc
+ weights_local[:, num_neurons_exc:] *= du_inh
+
+ # Single neuron paramters.
+ # Bias_mant is set to make the neuron spike.
+ lif_params_exc = {
+ "shape_exc": num_neurons_exc,
+ "vth_exc": vth_exc,
+ "du_exc": du_exc,
+ "dv_exc": dv_exc,
+ "bias_mant_exc": bias_exc}
+
+ lif_params_inh = {
+ "shape_inh": num_neurons_inh,
+ "vth_inh": vth_inh,
+ "du_inh": du_inh,
+ "dv_inh": dv_inh,
+ "bias_mant_inh": bias_inh}
+
+ # Parameters Paramters for E/I network/
+ network_params_lif = {}
+
+ network_params_lif.update(lif_params_exc)
+ network_params_lif.update(lif_params_inh)
+ network_params_lif['g_factor'] = g_factor
+ network_params_lif['q_factor'] = q_factor
+ network_params_lif['weights'] = weights_local
+
+ return network_params_lif
diff --git a/tutorials/end_to_end/tutorial02_excitatory_inhibitory_network.ipynb b/tutorials/end_to_end/tutorial02_excitatory_inhibitory_network.ipynb
new file mode 100644
index 000000000..319acda1a
--- /dev/null
+++ b/tutorials/end_to_end/tutorial02_excitatory_inhibitory_network.ipynb
@@ -0,0 +1,1966 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "3fd51524",
+ "metadata": {},
+ "source": [
+ "*Copyright (C) 2022 Intel Corporation*
\n",
+ "*SPDX-License-Identifier: BSD-3-Clause*
\n",
+ "*See: https://spdx.org/licenses/*\n",
+ "\n",
+ "---\n",
+ "\n",
+ "# Excitatory-Inhibitory Neural Network with Lava"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8ac8ba24",
+ "metadata": {},
+ "source": [
+ "**Motivation**: In this tutorial, we will build a Lava Process for a neural networks of excitatory and inhibitory neurons (E/I network).
\n",
+ "E/I networks are a fundamental example of neural networks mimicking the structure of the brain and exhibiting rich dynamical behavior.
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3fbb06e6",
+ "metadata": {},
+ "source": [
+ "#### This tutorial assumes that you:\n",
+ "- have the [Lava framework installed](../in_depth/tutorial01_installing_lava.ipynb \"Tutorial on Installing Lava\")\n",
+ "- are familiar with the [Process concept in Lava](../in_depth/tutorial02_processes.ipynb \"Tutorial on Processes\")\n",
+ "\n",
+ "#### This tutorial gives a high level view of\n",
+ "- how to implement simple E/I Network Lava Process\n",
+ "- how to define and select multiple ProcessModels for the E/I Network, based on Rate and [Leaky Integrate-and-Fire (LIF)](https://github.com/lava-nc/lava/tree/main/src/lava/proc/lif \"Lava's LIF neuron\") neurons\n",
+ "- how to use tags to chose between different ProcessModels when running the Process\n",
+ "- the principle adjustments needed to run bit-accurate ProcessModels\n",
+ "\n",
+ "#### E/I Network\n",
+ "From bird's-eye view, an E/I network is a recurrently coupled network of neurons.
\n",
+ "Since positive couplings (excitatory synapses) alone lead to a positive feedback loop ultimately causing a divergence in the activity of the network, appropriate negative couplings (inhibitory synapses) need to be introduced to counterbalance this effect.
\n",
+ "We here require a separation of the neurons into two populations: Neurons can either be inhibitory or excitatory.
\n",
+ "Such networks exhibit different dynamical states. By introducing a control parameter, we can switch between these states and simultaneously alter the response properties of the network.
\n",
+ "In the notebook below, we introduce two incarnations of E/I networks with different single neuron models: Rate and LIF neurons.
\n",
+ "By providing a utility function that maps the weights from rate to LIF networks, we can retain hallmark properties of the dynamic in both networks.\n",
+ "
\n",
+ "Technically, the abstract E/I network is implemented via a LavaProcess, the concrete behavior - Rate and LIF dynamics - is realized with different ProcessModels.
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "89344cf6",
+ "metadata": {},
+ "source": [
+ "#### General imports"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "257a6fe8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from matplotlib import pyplot as plt"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "de1acd9c",
+ "metadata": {},
+ "source": [
+ "#### E/I Network Lava Process\n",
+ "We define the structure of the E/I Network Lava Process class.
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "497c0d06",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import Process level primitives.\n",
+ "from lava.magma.core.process.process import AbstractProcess\n",
+ "from lava.magma.core.process.variable import Var\n",
+ "from lava.magma.core.process.ports.ports import InPort, OutPort"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "159d9263",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class EINetwork(AbstractProcess):\n",
+ " \"\"\"Network of recurrently connected neurons.\n",
+ " \"\"\"\n",
+ " def __init__(self, **kwargs):\n",
+ " super().__init__(**kwargs)\n",
+ " \n",
+ " shape_exc = kwargs.pop(\"shape_exc\", (1,))\n",
+ " bias_exc = kwargs.pop(\"bias_exc\", 1)\n",
+ " shape_inh = kwargs.pop(\"shape_inh\", (1,))\n",
+ " bias_inh = kwargs.pop(\"bias_inh\", 1)\n",
+ " # Factor controlling strength of inhibitory synapses relative to excitatory synapses.\n",
+ " self.g_factor = kwargs.pop(\"g_factor\", 4)\n",
+ " # Factor controlling response properties of network.\n",
+ " # Larger q_factor implies longer lasting effect of provided input.\n",
+ " self.q_factor = kwargs.pop(\"q_factor\", 1)\n",
+ " weights = kwargs.pop(\"weights\")\n",
+ " \n",
+ " full_shape = shape_exc + shape_inh\n",
+ " \n",
+ " self.state = Var(shape=(full_shape,), init=0)\n",
+ " # Variable for possible alternative state.\n",
+ " self.state_alt = Var(shape=(full_shape,), init=0)\n",
+ " # Biases provided to neurons.\n",
+ " self.bias_exc = Var(shape=(shape_exc,), init=bias_exc)\n",
+ " self.bias_inh = Var(shape=(shape_inh,), init=bias_inh)\n",
+ " self.weights = Var(shape=(full_shape, full_shape), init=weights)\n",
+ "\n",
+ " # Ports for receiving input or sending output.\n",
+ " self.inport = InPort(shape=(full_shape,))\n",
+ " self.outport = OutPort(shape=(full_shape,))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "01b9eabc",
+ "metadata": {},
+ "source": [
+ "#### ProcessModels for Python execution"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "bc319315",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Import parent classes for ProcessModels for Hierarchical Processes.\n",
+ "from lava.magma.core.model.py.model import PyLoihiProcessModel\n",
+ "from lava.magma.core.model.sub.model import AbstractSubProcessModel\n",
+ "\n",
+ "# Import execution protocol.\n",
+ "from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol\n",
+ "\n",
+ "# Import decorators.\n",
+ "from lava.magma.core.decorator import implements, tag, requires"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4ed07fb5",
+ "metadata": {},
+ "source": [
+ "### Rate neurons\n",
+ "We next turn to the different implementations of the E/I Network.\n",
+ "We start with a rate network obeying the equation\n",
+ "\\begin{equation}\n",
+ " \\tau\\dot{r} = -r + W \\phi(r) + I_{\\mathrm{bias}}.\n",
+ "\\end{equation}\n",
+ "The rate or state $r$ is a vector containing the excitatory and inhibitory populations.
\n",
+ "The non-linearity $\\phi$ is chosen to be the error function.
\n",
+ "The dynamics consists of a dampening part ($-r$), a part modelling the recurrent connectivity ($ W \\phi(r)$)\n",
+ " and an external bias ($I_{\\mathrm{bias}})$.
\n",
+ " We discretize the equation as follows:\n",
+ " \\begin{equation}\n",
+ " r(i + 1) = (1 - dr) \\odot r(i) + W \\phi(r(i)) \\odot dr + I_{\\mathrm{bias}} \\odot dr\n",
+ "\\end{equation}\n",
+ "Potentially different time scales in the neuron dynamics of excitatory and inhibitory neurons as well as different bias currents for these subpopulations are encoded in the vectors $dr$ and $I_{\\mathrm{bias}}$. We use the error function as non-linearity $\\phi$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "44795c5b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from lava.magma.core.model.py.type import LavaPyType\n",
+ "from lava.magma.core.model.py.ports import PyInPort, PyOutPort\n",
+ "from lava.magma.core.resources import CPU\n",
+ "from lava.magma.core.model.model import AbstractProcessModel\n",
+ "\n",
+ "from scipy.special import erf\n",
+ "\n",
+ "@implements(proc=EINetwork, protocol=LoihiProtocol)\n",
+ "@tag('rate_neurons') # Tag allows for easy selection of ProcessModel in case multiple are defined.\n",
+ "@requires(CPU)\n",
+ "class RateEINetworkModel(PyLoihiProcessModel):\n",
+ "\n",
+ " outport: PyOutPort = LavaPyType(PyOutPort.VEC_DENSE, float)\n",
+ " inport: PyInPort = LavaPyType(PyInPort.VEC_DENSE, float) \n",
+ " state : np.ndarray = LavaPyType(np.ndarray, float)\n",
+ " state_alt : np.ndarray = LavaPyType(np.ndarray, float)\n",
+ " bias_exc : np.ndarray = LavaPyType(np.ndarray, float)\n",
+ " bias_inh : np.ndarray = LavaPyType(np.ndarray, float)\n",
+ " weights : np.ndarray = LavaPyType(np.ndarray, float)\n",
+ "\n",
+ " def __init__(self, proc_params):\n",
+ " super().__init__(proc_params=proc_params)\n",
+ " \n",
+ " self.dr_exc = proc_params.get('dr_exc')\n",
+ " self.dr_inh = proc_params.get('dr_inh')\n",
+ " \n",
+ " self.shape_exc = proc_params.get('shape_exc')\n",
+ " self.shape_inh = proc_params.get('shape_inh')\n",
+ " \n",
+ " self.proc_params = proc_params\n",
+ " \n",
+ " self.got_decay = False\n",
+ " self.got_bias = False\n",
+ " self.weights_scaled = False\n",
+ " \n",
+ " def get_decay(self):\n",
+ " '''Construct decay factor.\n",
+ " '''\n",
+ " dr_full = np.array([self.dr_exc] * self.shape_exc + [self.dr_inh] * self.shape_inh)\n",
+ " self.decay = 1 - dr_full\n",
+ " \n",
+ " self.got_decay= True\n",
+ " \n",
+ " def get_bias(self):\n",
+ " '''Construce biases.\n",
+ " '''\n",
+ " self.bias_full = np.hstack([self.bias_exc, self.bias_inh])\n",
+ " self.got_bias = False \n",
+ " \n",
+ " def scale_weights(self):\n",
+ " '''Scale the weights with integration time step.\n",
+ " '''\n",
+ " \n",
+ " self.weights[:, self.shape_exc:] *= self.dr_exc\n",
+ " self.weights[:, :self.shape_exc] *= self.dr_inh\n",
+ " self.proc_params.overwrite('weights', self.weights)\n",
+ " \n",
+ " self.weights_scaled = True\n",
+ " \n",
+ " def state_update(self, state):\n",
+ " \"\"\"Update network state according to:\n",
+ " r[i + 1] = (1 - dr)r[i] + Wr[i]*r*dr + bias*dr\n",
+ " \"\"\"\n",
+ " state_new = self.decay * state # Decay the state.\n",
+ " state_new += self.bias_full # Add the bias.\n",
+ " state_new += self.weights @ erf(state) # Add the recurrent input.\n",
+ " return state_new\n",
+ " \n",
+ " def run_spk(self):\n",
+ " \"\"\"The run function that performs the actual computation during\n",
+ " execution orchestrated by a PyLoihiProcessModel using the\n",
+ " LoihiProtocol.\n",
+ " \"\"\"\n",
+ " \n",
+ " if not self.got_decay:\n",
+ " self.get_decay()\n",
+ " \n",
+ " if not self.got_bias:\n",
+ " self.get_bias()\n",
+ " \n",
+ " if not self.weights_scaled:\n",
+ " self.scale_weights()\n",
+ " \n",
+ " a_in = self.inport.recv()\n",
+ " self.state = self.state_update(self.state) + a_in\n",
+ " self.outport.send(self.state)\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1cd93212",
+ "metadata": {},
+ "source": [
+ "#### Defining the parameters for the network\n",
+ "Next, we need to constrain the network with the needed parameters.
\n",
+ "First, we define the dimensionality of the network which we identify with the total number of neurons as well as the single neuron parameters.
\n",
+ "We here follow the common choice that the ratio between the number of excitatory and inhibitory neurons equals $4$ and that the connection probability between two arbitrary neurons is identical.
\n",
+ "The recurrent weights must *balance* the network, i.e. the average recurrent input to a neuron must be less or equal than $0$.
\n",
+ "This implies that we need to increase the strength of the inhibitory weights, the `g_factor`, to at least $4$. We choose $4.5$ to unambiguously place the network in the inhibition dominated regime.
\n",
+ "Finally, we set a parameter that controls the response properties of the network by scaling up the recurrent weights, the `q_factor`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "1caca08a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Fix the randomness.\n",
+ "np.random.seed(1234)\n",
+ "\n",
+ "# Define dimensionality of the network.\n",
+ "dim = 400\n",
+ "shape = (dim,)\n",
+ "\n",
+ "# We represent the dimensionality by 400 neurons. As stated above 80% of the neurons will be excitatory.\n",
+ "num_neurons_exc = int(dim * 0.8)\n",
+ "num_neurons_inh = dim - num_neurons_exc\n",
+ "\n",
+ "# Single neuron paramters.\n",
+ "params_exc = {\n",
+ " \"shape_exc\": num_neurons_exc,\n",
+ " \"dr_exc\": 0.01,\n",
+ " \"bias_exc\": 0.1}\n",
+ "\n",
+ "params_inh = {\n",
+ " \"shape_inh\": num_neurons_inh,\n",
+ " \"dr_inh\": 0.01,\n",
+ " \"bias_inh\": 0.1}\n",
+ "\n",
+ "# Inhibition-exciation balance for scaling inhibitory weights to maintain balance (4 times as many excitatory neurons).\n",
+ "g_factor = 4.5\n",
+ "\n",
+ "# Factor controlling the response properties.\n",
+ "q_factor = 1\n",
+ "\n",
+ "# Parameters Paramters for E/I network.\n",
+ "network_params_balanced = {}\n",
+ "\n",
+ "network_params_balanced.update(params_exc)\n",
+ "network_params_balanced.update(params_inh)\n",
+ "network_params_balanced['g_factor'] = g_factor\n",
+ "network_params_balanced['q_factor'] = q_factor"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7e500634",
+ "metadata": {},
+ "source": [
+ "Finally, we have to set the weights given the above constraints. To this end, we sample the weights randomly from a Gaussian distribution with zero-mean and a standard deviation that scales with the ```q_factor```."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "7cadbf55",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def generate_gaussian_weights(dim, num_neurons_exc, q_factor, g_factor):\n",
+ " '''Generate connectivity drawn from a Gaussian distribution with mean 0\n",
+ " and std of (2 * q_factor) ** 2 / dim.\n",
+ " If a excitatory neuron has a negative weight, we set it to 0 and similarly adapt\n",
+ " positive weights for inhibitory neurons.\n",
+ " W[i, j] is connection weight from pre-synaptic neuron j to post-synaptic neuron i.\n",
+ " \n",
+ " Paramerters\n",
+ " -----------\n",
+ " dim : int\n",
+ " Dimensionality of network\n",
+ " num_neurons_exc : int\n",
+ " Number of excitatory neurons\n",
+ " q_factor : float\n",
+ " Factor determining response properties of network\n",
+ " g_factor : float\n",
+ " Factor determining inhibition-excitation balance\n",
+ " \n",
+ " Returns\n",
+ " -------\n",
+ " weights : np.ndarray\n",
+ " E/I weight matrix\n",
+ " '''\n",
+ " # Set scaled standard deviation of recurrent weights, J = q_factor**2 * 6 / full_shape.\n",
+ " J = (2 * q_factor)**2 / dim\n",
+ " weights = np.random.normal(0, J,\n",
+ " (dim, dim))\n",
+ " \n",
+ " # Impose constraint that neurons can **either** be excitatory (positive weight)\n",
+ " # **or** inhibitory (negative weight).\n",
+ " exc_conns = np.full(weights.shape, True)\n",
+ " exc_conns[:, num_neurons_exc:] = False # Set entries for inhibitory neurons to False.\n",
+ " inh_conns = np.invert(exc_conns)\n",
+ " \n",
+ " mask_pos_weights = (weights > 0)\n",
+ " mask_neg_weights = (weights < 0)\n",
+ "\n",
+ " # Set negative weights of exciatory neurons to zero and similarly for inhibitory neurons.\n",
+ " # This induce sparsity in the connectivity.\n",
+ " weights[mask_neg_weights * exc_conns] = 0\n",
+ " weights[mask_pos_weights * inh_conns] = 0\n",
+ "\n",
+ " # We finally need to increase the inhibitory weights by a factor to control balance.\n",
+ " weights[inh_conns] *= g_factor\n",
+ " \n",
+ " return weights\n",
+ "\n",
+ "# Generate weights and store them in parameter dictionary.\n",
+ "network_params_balanced['weights'] = generate_gaussian_weights(dim,\n",
+ " num_neurons_exc,\n",
+ " network_params_balanced['q_factor'],\n",
+ " network_params_balanced['g_factor'])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ac80469e",
+ "metadata": {},
+ "source": [
+ "#### Execution and Results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "e609ac8c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from lava.magma.core.run_conditions import RunSteps\n",
+ "from lava.magma.core.run_configs import Loihi1SimCfg\n",
+ "# Import monitoring Process.\n",
+ "from lava.proc.monitor.process import Monitor\n",
+ "\n",
+ "# Configurations for execution.\n",
+ "num_steps = 1000\n",
+ "rcfg = Loihi1SimCfg(select_tag='rate_neurons')\n",
+ "run_cond = RunSteps(num_steps=num_steps)\n",
+ "\n",
+ "# Instantiating network and IO processes.\n",
+ "network_balanced = EINetwork(**network_params_balanced)\n",
+ "state_monitor = Monitor()\n",
+ "\n",
+ "state_monitor.probe(target=network_balanced.state, num_steps=num_steps)\n",
+ "\n",
+ "# Run the network.\n",
+ "network_balanced.run(run_cfg=rcfg, condition=run_cond)\n",
+ "states_balanced = state_monitor.get_data()[network_balanced.name][network_balanced.state.name]\n",
+ "network_balanced.stop()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7febf4a0",
+ "metadata": {},
+ "source": [
+ "#### Visualizing the activity\n",
+ "We first have a look at the activity of the network by plotting the numerical value of the state of the first $50$ neurons."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "f4cdddfe",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbYAAAE9CAYAAABnfkdrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAABbYUlEQVR4nO3dZ5gc13ng+/+pqs6TcwBmEAkiB4IEmDPFTGXJkqxg7ZWus72215L37tq79u6z9nrtlSXbCla0skRSzKQk5kwCBEnkHCbn1LnCuR+6ZqZnMAgkMNODmfenp1RVp05Xvd1o9jtV59QppbVGCCGEmCuMQgcghBBCnE+S2IQQQswpktiEEELMKZLYhBBCzCmS2IQQQswpktiEEELMKVahAzgbVVVVetGiRYUOQwghxCyxffv2Xq119VTbLojEtmjRIrZt21boMIQQQswSSqnjp9omlyKFEELMKZLYhBBCzCmS2IQQQswpktiEEELMKZLYhBBCzCmS2IQQQswpktiEEELMKZLYhBBCzCmS2IQQQswpF8TII0KIuUVrDZ4GD7SnQWvQgNZof55bB5iizF/Wo2XklzFhf2P1mPja8WP5ZRMCPCng8aIptk1dPmlh8iFOt/0Mrz2p+GxjmMppNp3WGV6nT1Mh1FxCoDb2Lg98ZpLYhJgDtOuhbX/KumPLXtZFOx7YXq6Oo9GuB64eX3Y8tKvRrgbXQ/vruHrsNbh5dfxEpL1cnbHl0STlT6MJS7tMfI2np/iR12M/hNrPTtrPYmNbJpT5NSeXTX6dHt/n2Kv0+HbyysdnJ2eYk3+iR/c1cX187XT7ON3xTnXMSWX6dMebumSqY7wTenLyP7tXTVnadNtlLLhj47vY39mRxCZEAWhPo9MOXsrBS7vozOjcxctfTjv+3K+TGU9a2nbR2dwynsbTHq52cLWN6+Xmzui6dvHyJle7eOSV4eIpz5/r3HxsypVrPDytwS/R2kOTO+7ostbe+Poplj0vr8xfFvNLWNezAElsQsxq2vVw4zbecBZ3JDd5SRsv4SevpI2XHJ3beCkH13XIumkcncX2Mtje+NzRGWydwVEOtsriYGPrTC5ZeQ6uZ+N4Nq5r47hZHMfG85xzfh/KMDCsACoQBCuEtgK5uRkAK4BnBMAw0KYFykAbFtowQRlgmHgqt+z5ZVoZaMNAKxNPKbQyc2VK4SkDMPCUwkOhUYBCK5W7Wqj8dQCV2547x/LL8taBCa/T2n9N7kRybDn/9WPz/Kue+Atqqqt7asL6qa5IMmn7qa4OjsaYX3j6uqeObWzz2eznDE5XRZ1h+5THUCfXWVy9iE1nDuVdk8QmxBl4WRd3MIM7kMYZyOAOpnH9BOb5ScyN22S9NCk3TtqNk3aTZN0UWdJkjQxZnSar02TcFBk7SSabwHGyZzy2YVkQLYVoCV64CC9YjmcF8QJhXDOIZwVxzCCuEcBR1thkY2BjYmtFVhtkPXA0OB7Yo3NX5yZPjy1nXQ/HO2PjCZx7Dj1rKm+avD6aakZToNIn18stq/HX6Km2T/z9Hcshkz6Kyb/RU/xmn7qePvX2d7yv071On2W901DvqPY7d+jZE7BlybTtXxKbmPe01ngJG6cnhdObwu5N4fancQbSuAMZnHiapDNC0hkm4QyRdEdIG0nSJEm7cVLZOKn0yJRnTFopzFgpXnElXrQcJ9KEHSoiE4iRMSOkjRAZTNLaJOUZpFxIOpDIeiSyLomsO3GHtj+lpn4vCggAlvYIoLE0Y5MJmBpMFKaGoD83GN2mMDHH6hljZbnthr8PhcLw1xW5emrCuppQNrZMLlFM2J73uqkT2PT+wJ5XpzwdOtu2Kf889KTqZzxHmrJ04n7eafvYu+1RcnZKVNe07n/aEptS6lvAnUC31nqNX1YB/ARYBBwDPqy1HpiuGITIp7XGHcpid8SxOxM43bkk5vSkyCQTjNh9jGT7GXEHSBpxkt4wicwgydTQhIZzDaiSKrzSWpziJWQjZaSCxSTNGCMEGXJNhrIwlPYYTDvjZ0AekPAnXwgIaZeg9gh6ENIQ0IparQhqCGIR1BAaXdfKT1wKSzO2nJuPJ56T37yL0h5Ka3+eW0d7uTneWB3tl/u9QUDn2tdAg/LGuiUoNHrs9Gd8ridnKUODYeQuuxmglMqtG4BpoAyFMgwwVW6bMnJX6AwFSqEM5e8rVze3DwOUPzdG6ygMwy/P21/ueLljGqafLk0jV24aGIZfbzQOlYtRocZuiFL+JUNl5J0DGoa/bbQob5tSaD9slPI3qQn7Gq2fvz62C9TYuh6tx8QYxv6d8183Gu944P5nN/FbMRb75NBH95tfMFnee3m3VRbVl5329edqOs/YvgN8BfheXtkXgCe11v9LKfUFf/3PpzEGMU9pT+N0J8m2jGB3JMh2JLA7E6TiQwxmuhm0e4irQeLuIMPpXlLpkfHXKhNdvRCnqpF0bD2JcDlDRpR+N0h3StMVt8m6/g95irGzpwAQ8xxinkvUUzRoxVJtEtYQ0YqIp8aWw1oR8c9exg/sYngOhuegPHts0p6N1jbgoJXrJwqNNkEFDAgYqKCFEbIwwkGMcBArGsSKhAkUhQkWhwnFYgQjYYLhIGYwgGkqTENh4mFqF0N7mLiY2sMYTYKeC66L69q4joNn27h2Bm1n0K6D9hxwHbRng+vgeTbayYKXzW0bm+feA9oGfx3PBe34Za6fVG1yXSh1LuGS66iSy6Pu6D9srlx7uWSqcy1l2tUovNxZk9Iov3ekylvHX9eKXDJXjJfntVaNtsD5q/nfKiDv9zrvlEhNdYajT7PtdK8bzVenbRCbFMvJwZ72uBNfO9W+zsWZz/b6F91Fw8f++bwdcbJpS2xa6+eUUosmFd8DXOcvfxd4Bkls4jxw41myJ0bItoyQPTFM5sQIw4ke+rOdDDk9DNHHYKprLIFpwCupwa5dSrJ0E0PBcnp1lLaUon04i6uBrD8BRVpT7GYp8QzWeyZFWhHzIKYVRZ4i5imC+G05noPpZjDcLHgZcDNAFo2NZ7kQUhiRIGZJhEB5jHBZEeGKIiLlpURKiwmHLULKJahtLDeD4WRxUkns5DBuoh8nOYCXHULbwyg7jnbi4KRQXhLDTaF0BpXIYMSzmD02hnZySQsXAw8DD1NpDMYnU48uexijy5rcss4lBPNMTW8aPNTYUZy8Izr+3MXA1bnyk6dcufY7kniMdioBT+fOMDwMP90Zfvox/LqTy8f3MTpn0rrWaqyem1+ucvsbe19jczVhPrGMKcry6qmTy7yxzqD5+zPG6492FPHrjL924vHH03BeZ5up6unJ2/PjHf18z+Rs09/p6y3v9Vhzlnt6N2a6ja1Wa93hL3cCtaeqqJT6HPA5gKamphkITVxI3IRN5sggmcNDZA4Pkujspz/TTl+2nX7dTV+ijaydO5VyA2HsxlUkFq+nL1xFmxPm6JDffuUAfbn/EMpdh3JXsdmzKPUUpZ6ixJ8sFMpzMZ0kppNEu0m0zqAtGx0xMEvDBKtLiTVWUrqwkdKKKDHDI+SlUZkkmZEB7MEO3JFWdKIdne7BdPox7CGsrhRmZ5YAWQK4BJSbm2uPgPawPA/LHf+pdbRBmuD4pMeXE/5yihhpHSSlgiR1iIyKkNYRbELYOoijA7g6iEMQR1t4OoCLhasDeNrCw0R7Jhorlyy0CTrXi1Frv5VN53fN8Od6YvtZftvbeJnKa287Vd3RPTI2n9zxI798Qp3R9QmdRNQp648tn2bf+d5N2dm0FZ7qeKc7xjvZflZ11ITZOTndPros9zRbz13BOo9orbVSJzeT5m3/OvB1gM2bN09vS6aY9bTrkTk6THpfP5nDg8Rbe+lJt9CdPUG308JQvBsA1wiQXbCa4aV30RGs5mjSpHXYzu1kINeGVe3aXOQaVLgByj1Fhaso0QqlNZadxLCH0V4CbWYgqrCqi4k1VVO+rJGKqhKKdQIjOUi6txVn4CDeyDGMdAem3Uvw2AjhYykiKktIu7n2M9fF8nKjXIwQYUgXMUgRgzrGAJWM6AjDxBjRpSQpJuUVkdZF2F4U24vg6BDaC6K1BdrC1AYWfqcQlD/Ptc2NLudvC5DrKBLxm4wM/CYnRpu91FiZytuuJtRVefX9bXk/gmMd8I2J7Taj21Te/mB8H0rpsR/0/OQ3ut/R/UxY9/9/8vnLyecz4+1Xp64zxX5U/tZ8U/9UT5nUTtdGJdg/cgC4edr2P9OJrUspVa+17lBK1QPdM3x8cQFxEzbpAwOk9/aR3NdL99BxOtJH6HFb6R/pADSZSCnJxZfSU7KI43aUQ4N+Z41+KPE86hy40rWocQ2q/QRmeA6BzCA4w3hGGkpNIs3VVK5uoqaxiWJ3BLe3lUzX23hD+7CSLQR7e4n2xomRJeZlCTsuyoMhYvToMnp0KT1U0qWX0UM9g24tCbeUrFeE60XwdAjlWYS0QVgrwt54W1tIQwhFmYIq5ScmpbAUWAoCyk9SCkwFpqExFJhK53osjs791+XKFAYKUylMNX1DwmrP9Tua5Nq9cut+G5g32vlkfMp1TNG5trSx5dGbtPNegyZvvKu8IbH0xG1j5aMX0U7epqfcxqR9+8NrjbXpjZePH/ekd3/y8pl6IuopXnPK7VPs64zb80um61inqP8ONnnN03tT/kwntgeBTwH/y58/MMPHF7Ocl7RJ7e4j+VYPIwc66UgeoT17mM7kEbJ2GjsYI7F4M11lt3EgE+PYkA0pCCShznG4xDWpdw3qHYMiDyw7jpEdwCMO5SZFFzdQt2EJtZFaVO9xMu2voQfeIphpoejNQUreSlPsZAk4HoMU0aYraddVtOi1tHvN9LqNJJxqHC8GXoiwNoh5iphW/hzqlKLZgKBSBBUExuZgmZqgGp0UQaUIKOOsk4/2HLSTATcLbhbt2uDaaC+bm7t587wbuEfLtOegcXOTypsrF3DwlAvKQRsunuGCsnNzw0WbNhgunumA5YDhjd4TMHYKpsdOCUGP9vUfvWo5Vj5pWeWX5+1r8qmcfxKkyW3zUH49v51pdE6uR2J++ehN2t7o9tH6/nHG2ttGX6fBM8bbxUZvkNbkxzHxrGzKNDLWtnaKf8+xrpMTeXlnfFOn1LwXqZPLx/I2o3Gfan+T4psUy9j+9NTbT47r1DHnC7o103i+Nr3d/X8EXAdUKaVagb8kl9B+qpT6LHAc+PB0HV9cOLTtktrTR/LNHob3dtA6so8Tmf10x4/jaY9E+SL619zNAVXDvgEXnYVAFzQ6Ltc4Fk2OSa2rsNwsVroHTw+iaoNUbV7GgouWUZTsJHP0WXTva0SGj1H23DDlbpqQ7dJNGUd1HfvdxRzWy+h2lxB3avCcGFHPpESPt7M1GorlShExIGzkklXQ1EQMj7BShJQiZJz6PyntpNHZJNpOoLP+ZCdw7RSOk0Y7aTw3jacyeCqDa6TxzAyelcILpvCCaXQkCyGNDml0gIlTUOOYiqxlkjVN0oZF1jLJGCZZZZExTBxt4mgL11O4noXnGbieiadNXM9A6yCuF0FrA88z0doYm9C59jV0XkYazT7av6Tnr6vROYwv59VRo+uef6nQySvz/4ee0Ho3YS4ubD1FrdO6/+nsFfkbp9h043QdU1xY7K4EiVc7GdrWRlv/Pk5k9tERP4zjeQw2rKVzxSfZmSqiO+nCINQ5HpfbFs2OQb1rEHTSmKl23EiSonULaNy0mGovSObgm5h9L1N6rI2KowmKsln6dCn7vCZe8y7lkLuWAbsZ1ymhyDOp8AzK3VziWmEoYoafvAKamKGJKkXYME9qN9FOFp0ZQqeH8TLD6MwQmcwIXmYIR4/gWAncQBwvFMeNxCFm48U0XhV4UU06YjISCBIPBElaAZIqSNoNknWDZO0gjhvGcYtx3QCeZ4EXANfE0CbKMzG0gaENTM/ESBmYSRPjXTyJSsHYpcx8Hl5u/EjloRmf69F72ZRm9P620QGFcz0wJg9gnOuvOLoNld+XzyPvPMufe/4IIuP9Aclbzj/HUHrqAbbGyvTJ5blkm7sUppT2O42M9q/06+jxtr/RbTB+wqL06MnY+CXACd+OvO4DxtjiVP0Vx2M92/Kx3St96m2n2cfkc0jFyfvJv9VA5W08058V+Z/96Swbnt5uEzLyiJhR2vFIvtVD4rVOug8e4nD8LY4n9pB10gxWXUTHho+xPVnCYMbD6oVFjmajHWCJbVLsOARSHbjBEaJrG1i8uZnivi7co09QlDhA1fNDlGbSdHhV7PCW8Zb3Ydrsi0nbNRS5QapdgxpPsck0KDEhZiiiQY9iA4oMEyPvcqB2bXRqAC/Vj071k0324ab7sY1BnHA/TqwfrzSJW6LxGiBVZNAfjjAQiDCgIiSyMTJ2FNsuxXWD4AZRbgDLtbC8AIFEADM+OZWMs/zJw8MxHFxl4yoHTzlolUHj4ioXTzm4uBimg6FdLFxM7RDUDgFt+x1YHCL+FMYhjJcrZ3RyCOESUTYhXMLKIYRDQEFeTpm1tM7/yc5PR1Ncx/TPLvVJ5eNdVk75utGu+FNuh9P/7L+beidf7py48XT7PM+x6DPEMmHbmf+4estae8Y650ISm5gRXtIm/mongy8c51jnWxxJvU1foo14qIL2VXexw6mhO+Vh9cMSG66xgyyxDSLZOCrTjloQoOmGldRkTbzDT1Kc2kX984NYGcUb3nJecK/loLOJeHYhZU6IetdgoVKsMRUlpqI46FFqKGLm+FdeuzZeogcv3oWT6MZJdpE1e7FjnTiV/bgVHplF0BeL0hOJ0auLiaeKSWeb8ewVGG4QywkSyoaw+ib+p2QAESCgHGwji2tk8FQWV8XRZgZtZAnqNFGdptjNUIRNic5SqrOU6TTlKk2FSlGishjvsJ3d0wG0DqIJAiE0wfFJR9G5cUrQ/oQ2/bmFJrecwiKpLfDXx+ubfr3cLQHe6J1xKneZ0iM38LFHbpDj3HY1VsdFoZWBq4wJZd5omT93x9YVrmHmzu2Uyp07jbaD+RMw3lbmt5NNKBs95chrIxtvK9O5H+y815BfB3JnplN3zZyYW87kNPlETyrKX5/cDDdx2+glWr+jDOOjkExuFstfz/9bRXH6HKlOs/aO8mmexStrzq7iuySJTUwrZzDDyLMt9L9ylIP92ziUeJOknaJ7wSXsX34nO4cM1DAscuD2bIBltklRZhjttBHZVMvSDfWEjzxHuPuXNG7vIpzxeMNbzlPurRzKXoaXraXRMWnQiutMg3JLURbyqDANAkbujEhrD2+kB2+4jcxwGxmnjUxRG05VN06Nx+DFAdpjJXSoMoaSZWQzazHsCEE7TCgewoiP/wUaApSRJWuk8Iw0rjFExkhjeElK3SSVOkmDl6CJYRqNEYq1nRs0Y4rbdrQ28XQRHsVoong6iqYSjwjai6AJM6IjaCK5Mh0mQ5iMCpJWYTJGgIwRJG0ESJtB0laAjBXEDhg4lsKzwDPBs0CZCgIKwzIwArmhpQzTwLAMTMvEsgws08QKmFiWRdAyCQZMAlaAYMAiEggQCgYIWUECAZOAZRA0jbHem6fr3q79R91o7fqPq3HR/qNzXM/F0R6ednA9/7E7noeHg+dp/zE8+Y/F8R+T4/mXR7Uef/wNuXK0xkOjvdzxPHK9Icces+PmRjfJPcvNQ3saT4+WeeO9Icf268fv+fX9Oic98Sz/8TuTN+Xf+ny6Z6lNeoRPftUJ+5h0kJOelXaa3penP97kriV5xzjpTZ3m+Gc4zS8vmt4WKUlsYlq4QxmGn2mh68W97Bt4jWPxnYwQoGXFLbzqNTKU1ZQMwJUZi7VZi/JsAjJHCa+tYNm6IsIHnqIsuYMFLw7Slq3h1+5l7Mhej51pYIFj0aQNVliKSguqQoydiWnPxRvuwB04RnLkGOlgC5mqE9iNNl2rohyLltOVqSadvBgjewlhO0qwPwj9ubijaAwziW3GccxBTCNOqTdCgzvCEj3ERaqPai990hANng7g6XI8ynB1Da6+CM8rZVAX41GErWMkVZS4EWXEijEYLGIoHCYTMXFCQNjACBlYIZNgOEAoHCAaC1MSi1Aai1EWi1EcCVIeMHN/YesstpMm7aZJOUkyToa0kyKZGSGTSZDJJMhm42SdJI6dwHVSeE4a10mhvQzaH+5KZRzctI2nHbLaAVyUdlA45Mbj8CeVGxtEKQ+lPAw1vpy7T230XjW/DW3CNJPfvPNr+m6UmN/27Rpk09Jrpm3/ktjEeeXGs4w83ULXC/vY3fcix+K7GAxVcGT1R3g1WYKTgiWO4qZMgEW2JpxohYWw/OoGio88T+XIy9S9Nswbzgp+6HyYo+nLqLBLWOoavMcyqLYUVSE9lsi8TBy36wDp/sOkjGNkq4+QbLY5vKGMY0YNQ8l6SF9ENFtMoDcAQBjQZhLbHCFr9RB2h1jgDrJS97FWdVHkuuNDE2qFqytwqcHVS3D1FgZ0NVldzohRTL9VQl+olL5YlEzMREcNrCKLotIoFRVF1JaXUV1aSkU0SI3SZOwRBjL9DCb7GBjpIh7vIpnowc4O4dojaCeBl0qQTqTI9KYZJk2HymCqLKZpY5kOpulgGKf/ixhyP8rhyYUm45fyPIV2/eeUeWrKydMG2jPwtMLVBp420TqQ6ymZG4wr12NyrG0qVz7an/+kMjXav99/hhsGqFzXldxZX25dqfHtyvCDVgbjA/SqsWU1uk+Ve9cKf4Bi7Q+AzOhr8QdP9sc/GR3oWBn+sn93uX8cpUz/UKPrht8OO37cyWeqE2/unpzRjbx6E7coldfeetLVvvzXneZ4k24ZyT/+SdsmvG7yPk+3n9PUPU1sk128cvoeWQOS2MR5oh2P+IttdD6xm909L3B0ZCc94Ur2rP5N3kxEsUZgddZkc8aiJp0A7zBVNyyiKfMW5b0Ps2BbP287y/lW9hO0prfQlI2wBIMtAYOasKbSzP3wedlcIkv1HyAZOkCq+TitlxRxMFZNT6IOkjcRyxRjduZ+KKLKJmUNkjWPUkIfK71ettBGrZsaS16ujuHqRmx9Ea6+ll6vgQGjgq5AJe3F5YyUBtHFBuHyMNU1pSxpqGVhZSmV2qY03UN0pJ3wwDFGhlpJJTtxM93YgwP0DgwzqJIcMdNYZpZAwD7pB80EikdXLHC1wvMMPNfAtQ1cx8R1A2TcoB9nCE0IjzCoEBhBlBFGGUEMI4JhhTDNKJYVIRCIEAjECAVjhIMxwqEiwlaEsAoQROcea6NUroef1ihPY3gaQ7vgeuC6aNtBOzbaccBx0I4zRZkLnot2vdzc88D1b9b2dN623KU9z3VxtYerwfM8XE/jaY2rde7yI/7cr6O1xtH+ZcjR/pZ++WjfSzS4fmORzq8zedn/e8BTjD2EdKx/jGZ8//76xD6eirxbvf1LnHnHy//HHXsy6FjBhM4XGj2pvj+f1CA21vY1erP5pAqT18cb//ztU9xHpyc3sk1ypouKU99X987Wr7nxWho/1HzqIM6RJDZxTrTWpHf30fvQfnYfe5Z9w6/TEyxl7+qP80YiRigOV2QsNmYsyhJd6KoRLro+QtmxH7C49RBd6Wp+Yr+fw+mrWZCNsgKT6wOKxpgm6p+VuYPHyXbuJMlOEosP07KpiD2RegbiiwknLyE8EIYBiCqHpNWPE9hPk9PJVt3Gat2L4Y+o5XhV2HoRjl5Pr9dEr1HLiUgt7RWlOOUm0eoozQuqWbWwkaZABmvkBHQfoK9/F6n4cXSmg6HjvexpHeJwIEkgMPH5azEgpsA1DBzXxM5YOHaQpBvD8apwiaKNIpRZgmmVEQyVE45WURyrpjhaQZEZo8S0CGmdGxvSdiCdxksm8RLJ3NyfnGSSlJ0lZbtkHJeM65LxXNJukowbJ6M9RjRkNGQ1pIG0YZAOBMkGgmSCIWzLwrYsHNPEGZ2bFo5p4Zqmv276yxauMboexjWiOCETN2riGgae4Xcg8Tt0jC57hvKfnK3GnpwtxGCihy3TuH9JbOJdc/rT9N9/kAM7XuTtoefo8jx2rfggb6TLCMTh8ozF5rRJSfwE1sUBVtbvprbnJ1TtTvCofSVfTf82JZl61nomlwcNGqO5S4zac3C795DsfZNE2Vt0r0iyY0MjnYkmwsl1hAfDMAhhM4VtdlJNO1vcdi7VrYRsjdYmtl5E1tvMkF5Cr2rkUGwBHdXFGLUhFjRXs2lZMw1qhFTvXrLtbxIfPIBOnKBzfxfDR4cIBu2x91kExExwsMimAqQzUUbcBXiUglWOFawiHKuntGQBVUUN1FpRIraDEY/jjYzgDg3jDg/hDI8wnEwymM4wnHUYdLrp8jo5qBWDVoBENEoyHCUZjpAMh0mFwqRDIdLBMOlQlHRxGemKENlAgGwgeE7/dkprDO1heP5o/tqfT7l+cpmlXSKOjWHn1pV/sTH3PDj/IqQeH5Ny/KKkmnSxEgw1XqYYHQ5s4piWxmiZ/1yxXHueGhuvMnex0L8fa7TO6KSUf6E0V9lQuVMW/8Jm3iPLpnpd3nadt6+xbSe/hry5cVIiH71nbLQ34yn+fU45XuXJdU7umJh/I9vUt7SPde48xR8aEzpJTlHnpIusZ/H3Sv4jmtasu/jMLzgH6uTeLLPP5s2b9bZt2wodhvBpVxN/qY2Wh7bzevdjtKc72bfoRl5SS9AeXJKxuDRtUho/QWidxcrI4zTFnyaRKuG7mQ/TkbqC1XaQZQGT5iCUWSbac3F79pLp2c5w7XYOrgqwM9xEZriR4kwZCkXGSJO1OlngtHOdd4T19GAAjldHRq/E9pbTqRazu6yJgYYY1YvL2bRyCUXBJPvat9Pe9iqZ4X2EdAuxUP+EMy7tQTYeIB0Pk7bLsanCCNYSiS2kvHwpjbEFVGuDQDyB29WN093FSP8gPckkveksPa5Hr6fojUbpKy2nv7SMweIS4pEYiUiEZChCKhhCG6fvjmC6LgHXGZ88F2tsPbctjCas8Ec7yQ3dFTJGRz4xcsuGImIahE0zN7dMIqZFxDKJWhahgIVpmmOTYRgYhjHl8pm2j04y8K+YSUqp7VrrzVNuk8Qm3gm7K0HPj/ewc8+T7B56mYMVq3m54goGHcXKrMk1SYuqeAuhNQarQj9lefJVdqWW89PMJwikLuISbdEcUjQGcoPzuoMnyLS9yHDVqxxYbfJmcCkMLyDqxAAYCfRRrFu5xj3Mtfo4JmB7C8h4a0h6q9kbvoiDC2soXlzC5tVLCAcH2Xn4KQa6XsHKHqAo1EMwmB2L30mZJAcjJLOVOKqOYHQx5RUX01yyhDoHVHcP2RMt9HR3055I05GxaVcG7WUVdFZW01NeSV9pGYNFJac8awo4NhE7S9jOELazBB2biOcSw6NIKYoNKLVMSi2TskCA8lCAilCQsnCYSChIMJibAoHA2PLoumVZkkCEQBKbOA+01sRfaufI/S/yevdjnHAzvLLkHg7YRdQ7ihtSQRbF+zGbB1lX8RDLEy/zSmoD96V+i8ZUDetMk6UhRbll4tkpnJZXSXgvcHh9D6+WLscdaSJqx/DwSAQ6WeQd5S5vD4t0HFcXkfE2kXAvYVd0JYeaqqldXsmW1Qs51PkCLUd/iUrvoSTYQTCUS2Keq0j1h4knK7DVAsLFK1lQs5GlqoxQTw/pI0do6e7jWCLFEQyOV9bQWltPR2U1PWWVZIITk5bSHrFMmlgmRVEmRbFjU25AuaGoDJhUh4LURkLURyOUx6LEYjFisRjRaJRIJIJlyVV/Ic4nSWzinLgjWXp/spc3X3+Mt4deYXftFl6MrkV5imtTATYlsxjBY6xb8QIr40/yamo99yZ/i6XpGjYELJaEIGKYuPEuMid+TU/zy7y0vJ6O5HJK0pVoNPFAF83eEd7v7mQBSWyviZS3lQ7W82r9MoIXV3D5xoUc6XiOzhO/ImjvoiTai2FotAfpgRAj8SqyxhJKKzezsmw1tYMJUgcOcqSzm32JNPujxRxa2MyJukY6y6tw8pKN4bmUpJOUpBKUZ1LUGlAXMGkMB2kqirKwpJjy0hJKSkooLi4mHD6pI/0FQXsa1/XwnNzctTWe6+E6Hq7jL9serqvRbq53ovZyc88dX54wd6cum1iXCfvL9VTU4D+nTsP4svZ7L+rJ6+PL2n/R2Lrn9wLU2n8azuj2k/c3Wm/C5zJh6DA9XnZSnSleN2Fdj+1Hn6LShKfhnFT55GNNCmvK/Z72Z/wMP/En3Xj9Dl572v2e5rVXvH8pq69ufPc75/SJTf6MFKeVPjzIie+8wssnfsFBN86zyz5OixNlecbgxmSA8sQBlm45zpr4v3OwZyl/nvwSS9M1fDBgsaxIETQMnO69DPf9mv3rjvLSkpUER+4k0B/AMkcIWdv5qLuDxfYIttdM0n0frwcu5c2Lmll16WLKi3oI7/0JRvxVDrzRiWFoqixIDMbo7L2IQPGlrKjeSnPEI3ViP3vaOngre5T7Fmj2Ny+mbe3VOBvHv+ZF6STliWHW97Sw0FQ0hwMsK4mxtLKMqopmKioqiMViM365z/M02ZRDNu1gp11/2cXOuDi2i5P1cLIT57btnlTmZF0cezQ5+cnKySUqz/bwvJn9Q1YZCmWAYSgMQ+XWVa5MKX/4J38+Wg6jy5O3T6o7aZvye3kopTBM/143xfh+yNtP7iATY1X5y/7KxJm/oCbWP6lO/n1fk+uMV564z/HK+eUn7XOKY520nymc8dt8uu/7Oe136uLyutiZXnlO5IxNTElrTfy5Nnbf+wSv9j7KztKVPFu2FdNV3JIKsmZkiNiiY2wJfxUvEeBfUr9HdXIFlwYsloVyCc3ufJuR+EO8dmmKPWo1JckaXOWSsY5znbeLW93DeF4VSe9G9ltbefOixazb0kQy9RodR35OmbWXUDgDQKI3zFB8EaHSy1ldvpm6rn5adu7l5aE4rzU2s695KS219Tj+LQJBx6Z6ZJDadJxFlsGKWJh1lWU011RTU1MzLcnLcz3SCYd03CadyJvi9lhZJuXkkpafuLLp3NzJTDHm1ikoQxEIGphBk0DQwAqaWAF/HjSxggamZWBayh8yy8A0FaY1OoSWv2yOL+e2+ctmbtkwjfGkZKqxhKHyklR++ViZn8xGk5gQ00HO2MQ74mUc+n62n9efu583RnbwfNNd7FfVNGcNbk9YlGT3sXb1IyxN7+RfB36LROIabjQsVhYZhP2E1pd4iGc2a1qdNcRSxQSNFEFrB590X6MxmyXlXc4J7+M8vWgNS69qImtvo+jY/2Xg0D4CAYeqgMFAVyW91gYW11zPOtfl8P7dvJgc5N7Fx9i95CJ6b14FgOU6VI8Msr67hYuDJpvKi1nfUMOCxuWUlpae04+r1ppM0iExlCE5lCU5lCExlCU5lCUxnCtLDGVIjdhkU84p92NaBuGYRSgWIBi2CBcFKKmKEAybBCIWwbBFKGIRCJsEwxbBSG4eCOUS1YSkZcpAT0KcjiQ2MYEzlKHtG6/x/K6fsNON88vFn2DYDXJ9KsCW4TgVzW9wdejbvDa4iX+Pf41r3ChrwyalloHTf4S+/p/y9KUu7c4aoqkisAZptJ7jE84OAk4FI+6HeKTkGpKXNbNgQR+hfd8gdeJtrIBLhWXS116HEb2GTbFLWN59mOdb2vlWfQfbL15L9625RBa2M9QN9bGx6yhbSmNc1VhH8yWXUFZW9o6TmNaa1IjNSF+a4b4UI33p3NSfZrgvzUhfCid78vD6VsgkVhokVhqiuqmYSHGQSFGAcCxvKvKnWAArKN3hhZgpcilSjMm2xzny1Wd55siP2BZt5JmKq4l4inviQRbGD7Fx9U+oTrXxj/H/yJLkcraGLRYETdxkH8kT9/LUJV0cV+uJOjES1gCbeJ0PO7txvFX0urfzy0WXsuTqSjpbfkgk8xSxaBzXVvR11KNiN7A5sI7EG7t4eCTNk2s3sb9pCa5pYrkODYO9rHLSXFtRzNamRhY1NxOJRM76vTlZl8HuFINdSQa7Egx0JRnsTDLYlSSbnngZMBS1KK4MU1wRpqQyQlFFiFhpiKifyKKlQYJh+ZtQiEKSXpHijNL7+9n7jV/ybPu9PF19JW9FltNsG9wVN6iKvs7NNV/jqZGreWPkU9ykQqwKKyztkT7yCNsuep3txRsoypaTsAa4hNf4kLMH272EI/p9vHLJWpasGKHz4L9SFduPMmC4s5i4dznroldjv7Gfh9IuT67fzKHGZlCK0uQIy4b7uDwa5D3NDaxctoyioqIzvg+tNYnBDL0tcXpb4/S2jtDbGmeoJzWhh1dReYiy2ijltVFKa6OUVIYproxQXBkmFJGkJcRsJ4lNnFbyrW52/Nv9PN/7K55ovIOjVg2XpS2uH0qwePHPWcNL/P3wn7EidTFXRCwqLQOnZx9H+TmPLW8mmm4gbSa5yHiNT9k7yLpbOMh72XHFakpKXoe+H1NSNICTMejuWEpdxftZfHCYB9p6eHDjFvY35Ub6rowPsSrez22VJdy88iIaGxsxzjBSRzpu03l0iM4jQ3QdHaanZYRMYrytq6Q6QvWCIioaiyivi1JWG6WsJkogdOqnVwshZj/pPCJOKfF6J698+4c8P/gqjzV9iB5i3JoMsGngOFtW/wvd8Sr+dvifuZUYa4oMlJNi6MCPeWjTEAn3MoIZj2jgDf6j/SIh+2J26//JW9esIWw9SVXynwln0iTtMK2t17LRuILhnfv5WrPi1fXXYm8OUJqMc03nMd5bW8b1m1dQV1d3yrYorTVD3Sla9w/QeSSXzIa6U0CuB15lY4ylG2uoWlBE1YIiKhcUySVDIeYh+a9+Hou/1M5L3/s+T8d38fDCj5LRQT6QCHBR+k1uW/VlfjTwEYjfyqfDAWoDBnbXbt4q+gUvbVpF1GnGDpzgc+6vWJgppdX9U568dBNFsSepdL5CyMwyPFzM8PA9rO+sYmdrH/93axMdd28m6Nhc3NfBHcUh3r92JQsXXnnKZBYfyNC6v5+2fQO07h8gPpDr/h8pCVK3uIRVVzZQt6SE6qYSOQsTQgCS2Oat4edaePH73+Op1GEebvwAlmfxsXiA5cFfcfWin/N3vX/FlsxitsZMAtpl6OCP+fmGEbS7FaUTrA08yvuyLfTZv8F3L76JkqZtNCT/iJCZZbC7lLj6MAv3GfxbqIQvbL6CzIYQVSODfLD3BJ+5eDHrrruNQCBwUlza03QdG+bo270ce7uX/vYEAOFYgMYV5VxyWzkLVpRTWhORXoZCiClJYpuHRl5u57l//xZPZVt5uOG9RDyTj46YrKn8AQ3mbv53zz9xlypmZczAjXezO/s9ntm4nLBTgxncw19knyKQvYJHi/6IxBUD1Ax+kZgXZ3iomMTAR6ne7fLP1Y28cO1mFLCsr4MPFAX46Jb11NbWnhSP63i07Onn6Fs9HN3ZR2o4izIUDctLueL9y1iwspyqxiLU2TwbQwgx70lim2cSO7p54Tvf5ld2B4/U3UWJZ/GRYcXW+n8iYSse6P+ffCoUoi5gkG57lQebtjEU2Ag6yZbAA9ySTnLY+wIvXNdESeLLVNntpOwQna13U7Enwr8uXMRrN63D8lw29bTw+YU13HL3jSeNrag9TfuhQQ683sXh7d1kkg7BsEnTmkoWr6uiaXUl4djJZ3RCCHEmktjmkdTuXl7++vd4In2Ch+vupMI1+ciQ5vqmv2Zbcj3J4Q/y0WiAYgU9R3/Ez9ZA0L0YJ3iEP8s+TihzHT9Y+H6sBb+mia/iBRStx6+k6cRSvlPawAu3bCboOlzRdZzfXbqAa6+746RR7Qe7kux5oZ0Dr3eRGMxghUyWrK9i+aW1LFxZgWnJqBpCiHMjiW2eyBwf5tWv/ojHE3t5pP4eyj2T3xh0uGnRX3H/8HtpTl7JTTELy82wt/87PLN6IYYXYGHwGT6dOc5R5495+uoo9c7fEImk6D7RQNngrTyfjPHYlVcDsKX7BH+2opnLb7gL0xzvyOE6Hkfe7GH38+207R9AGYqm1RVc8YGlLF5XLZ0+hBDnlSS2ecDpTfHmV+7lscHtPNTwXoo8k48Muty2+P/j3wc+xSWZTWyOmXjJXp427uVI8zJcFece4xesSy3kJzX/Dd30IEuCO0gmwnR3f4hj+4N8/bobSIfCrO5p5Y+banjPpDO05HCWnc+0svv5NlIjNsUVYbbcvYSVV9YTKw0V8BMRQsxlktjmODdhs+8rT/Box9M82PBegl6Ajw7DHU3/hW8OfJ7rsqvYELPI9B3ivurnSASXYwdb+NPsIxj2B/j2ZRfRZP4d4UiK9qPLCRzZxJfWXMGxWxdQP9THHwQzfOy9txAKjSeqwa4kb/76BPte6cR1PBatqWT1NY00ra7EkA4gQohpJoltDtOOx7Gvv8Cjh+7lgbo7gCAfGVbcvvC/8o2B3+dWZwmroxbx7rf40cLdKNVMMPQ2f5F+gz36T9ixZQ8riv6J9HCQjoMf4OnBBTx649VEsxk+1tfCX1x3OVVVVWPH62uP8/rDRzm8owfTNFixtY4NNy2c9mcvCSFEPklsc1jXfbv45fbv8Yvq64mbRXx4xOKOxv/Ot/t/h7u9JSyPmPR3vcJPmzswqGZh6Bk+mRrm3so/w2r+IU1FnXSdWEj60FX8w+Yb6F1RwcbuFv567TIuWbll7D6ywe4krz98lAOvdxEImVzynmbWXr9ALjcKIQpCEtscFX+9nV8/+k3uK99Ie7CGuxMB7qn5P/xg6FPc7S1hWdikq/NZ7ls0DCrM5YEHuTZZx9dX3MTSii9jmg5HD13LK33refiGayhOJ/nTVDe/n3fZMTGU4bUHj7D35U5MU7HpliY23txMuEi66QshCkcS2xyUbR3hhW99jwdD5RyMLeW6pMX7yr7FvSN3c5u7gmVhk9bup3h4cQqtDO4w7mNlYivf2VTCxaXfJj0c5sSx9/P1hdfQtryODd0t/O9NK1m7fBkAru3x1lMtbHv0GK7jsfbaRjbd2ixnaEKIWUES2xzjJmze/soDPJRu5bXa21idMXlv6GGeymzmJmc1KyImx/ue5fGmLI7h8VF1L1XJ23hgcwcryp6nr72K/Qdv4xtX3o6B5rdHOvjze24eu8H66Fs9vPDzQwz3pFi8voorPrCMsppogd+1EEKMk8Q2h2itOf7dl3m47Ul+VX8PtQ580N3J0UCYrZnNrIpYtPS+yOMLUmTNFJ/17sPVH+DFS1+lqbSD40dW8MuRe3jmui3UD/XxtwsruOWS24DcZcfnfnyAIzt6KK+PcfcfbGDhqooCv2MhhDiZJLY5ZPjFFp545Yc8WHMTJhYfSgxhle2lIf4J1scs2ga28+iCYWwzw//j3U+/+SHa1/6aiugIe/Zfx49L7ubw0mau6D7BV67bQkNtLVpr9r7UwUv3HsLJelz+vqWsv2khpikjhAghZidJbHOE3Zng+e9/l/tKLmbAKuXDccXK8vvpGP4818QsOkf283B9N7Zp8xnvfk4EP0RizaNEAxm2730v31l8N/FIlE8PtvLf33crwWCQ+ECGp763h5a9A9QvK+WG31xJWa1cdhRCzG6S2OYAbbvs+uoj/MIe5GDZJq5KWdxa/jXeGPp/eV80yFC6nYeqDuOYLp/U93Eicif2mocI4vL0/o/x/VV3EnRs/puZ5DP33I5hGBx5s4en/30fju1yzUcvYs01jTK6vhDigiCJbQ7ofGAXDx/5Jc/W3clCW/HRyEP8aviTfCocJusO8YuS7dimxYd4gLbIrbhrH0dlDR47/Gl+sv5WquND/MviGq5auxon6/L8z/ez67k2qhYWcctnV8sN1kKIC4oktgtc+tgQTz7yXR6svBoTi484Lbyg1/KRQDkBZfOz4LPYVoQbjAcYDl6Pu+bXuBmD+499joc33EDzQA//vvliLmpayHBvise+tpPeljgbblrI1nuWYgakLU0IcWEpSGJTSv0x8B8ADewEPqO1ThcilguZtl3e/Np93BuqpCdUxfvjGjvSwq32jVSGFQ85T5CMxlgdeIIStYH+tU/j2orvdvwRz63bysq+Dn5w9SU01NbQsqefJ765CzTc8bvrWLS26swBCCHELDTjf44rpRqBPwA2a63XACbw0ZmOYy7ofHA3v+h4nR2l61mfNriq6FEWpq9nWdjipdTT9BRFqQm/xFq9gL71r+E5im92/QnPrdjKpT2t3HfT5dTXVPPGL4/z0JffJFYa4oNf2CxJTQhxQSvUpUgLiCilbCAKtBcojgtW5sQwTz3yfR6vvJIST/Nh6xWOJO/iQ9EA+1Jvs69cQ2g/d2Ut3t64F1Nr/q3zz3hp2WYu72nh+3fcQDgc4Znv72PPix0su6SG63/zYoJhuTothLiwzfivmNa6TSn198AJIAX8Umv9y5mO40KmXc3b33iQe4MlDAbK+FgixR5rCZ8Ixeh1e3m+rJN0oI//mDnCSxuyRANZvtr+Z7y8bDNb/aQWMEM8+i9vc2J3P5tvX8Rldy0eG9RYCCEuZIW4FFkO3AMsBhqAmFLqE1PU+5xSaptSaltPT89Mhzmr9T9zmF+0vMyO0g2syxg0BndzO81YRpbHw9vIWGk+5zzLs2ugODbMNzv/kJcXX8bWnlZ+cMcN4Fj84h920LJ3gOs+voItdy+RpCaEmDMK0eXtJuCo1rpHa20D9wFXTK6ktf661nqz1npzdXX1jAc5W7nDWZ772Q95rGIrRZ7mA8YOquwraQoZ/Np7jrRlcpt6nFeX1lBZ0csP2/4DzzVfxYbeNn5wx/V4GZP7/v4NBrqS3P7ba1l9dWOh35IQQpxXhUhsJ4CtSqmoyp0m3AjsLUAcF6SjP3qBn3k2/cEKbstkaLFXsSUSZIf9Nl0xg/rQy/RUNVPbcIIH2z7Ao823sby/kx/efCVuyuD+//MG6bjNPX+4QTqJCCHmpBlPbFrrV4GfA2+Q6+pvAF+f6TguRKlDAzzyykO8VnYJy7JQbx3jzmA5vfSzvaibbOg464OKsmV7ebHnKn7W8BEah3r50VWbMDJB7v8/b5BNO9zzRxuoW1Ja6LcjhBDToiBd4LTWfwn8ZSGOfaHSnmbHt+7nwaIloEzep4+w0N1IcdTlp2auXe3j7KR99TAnhpfwzbLfoSwZ5wcbL6KIIu7/hzfQnua9f7yRqgXFhX47QggxbWRYiQvE4EvHuK9jJ4djS7g8rRhxmlkXCfCct41UwOBm40mOrswSd2P8Y+CLKOCfF1XTWFzDA1/agedq3vvHmySpCSHmPElsFwAv6/L8T37Cryo2U+64bKKF20OlHKeD49EkxeE36V9YRLA4zd9n/iuD0TL+IpBh66JlPPilN8kmHe7+gw1UNMiYj0KIuU8S2wWg87Fd/CyTYChQxm12kiV6ORHL4fngLlKBPi6JDlKxoJ2vD/4+R8sX8xvDHXxq6+U89OW3GOlPc8fvrqO6Sc7UhBDzgyS2Wc4dyfLkI/fyWulGFmU9ilGsjQR4Vm8na8KtgZdwVnTw1OANvFRxNVt6Wvmft9/ME9/YRV9rnFs/t4aG5eWFfhtCCDFjJLHNcsd+/jL3miGyRpBb3T5uMGs5bnTSGklSHt7GyPIs7W49/178/1A33M83brqSl39+hJa9A1z78RXSpV8IMe9IYpvFnL4Uj734KG+XrGVNWlOj66gMerxs7SEV6GVZ5SCBUof/4/1nlIYvL6un/Y0Rdj/fzqb3NLPqyoZCvwUhhJhxkthmsf0/eooHwvUYWnG1F+fKUJRX1B4ypuaG0GtElnTz1ZHfoydWy++5wyxwqnjx3kMs3VTN1nuWFDp8IYQoCElss1S2O8EvdjzHwaLlbM1o1hs1xK0hDoV6CUV24iyP80r6cl4tuZKre1r47Iat/Orbe6hpKuamT69CGTL2oxBifpLENkvt//GTPBFbRMRz2ORlWB42ec54i4yRZG11K+miEN8M/A7VI4P807VX8sQ3dmNaBrd+fi1W0Cx0+EIIUTCS2GahbFeC+99+iRPRJq7KaLZaVew1WxgKOKwrfpHIon6+kvwTsmaIv2koZefDHQx0JLjlP6ymuCJc6PCFEKKgJLHNQvt+9Gt+GVtM1HXY4JpUhVzesA6TDZ2gePEgv07fwt6iNdw92MGi4SoOvt7FlnuWsPDiikKHLoQQBSeJbZaxe5Lct+tlWiMLuDYDV4ZKeN08iGN4bK3YSbIkxg+Cn2HhYC9fWHcZL917iEXrqtj0nuZChy6EELOCJLZZZv/Pn+JXsSVEXYerdBAVTHDQ6qS4+C3Ciwf4aur30Bj83dIGXvzBESJFAW785Ep5UKgQQvgksc0i7nCW+994hbZIIzdlYGM4ysvGXhwjw0WNx3hVX86e6DruHOrC2GUy0Jnkxs+sIlwUKHToQggxa0him0WOPfAyv440EHEdrtZBBgJ9dAZGWFr5MtRovqM+T83IIL+3YA27nmtjw81N0q4mhBCTSGKbJby0w8MvPMXxaDM3phVrwhFeMfdjW/3UNXXxvcxnSFoxvlgW5tWfnaBqYRFb75absIUQYjJJbLNE2+Nv8UignIDrchNB2oKdjJgZ1tW8xrGiJbwQvp6relup2B8jm3K46TOrMAPyzyeEEJPJL+MsoF2PJ375OAdjS7k5rbg4EuYN4zA63Eppcx/ftD9PLJPiTxou5tD2HjbfvojKhqJChy2EELOSVegABPS/dpQHCWBouJ0AxwLtJE2b9Q07eNG8jlazmc8Nd7LvaZfKxph07RdCiNOQM7ZZ4PkHHmF30QquTnssjYTYYR4mVHKQwIIMP9KfpH6on6uHGkgOZ7nhkysxLflnE0KIU5FfyAJLHR/g3sFhXMPiHh3gSKCVjOGyqmEX9zkfIm4W8buBIAde6mXDTU3UNJcUOmQhhJjVJLEV2Ns/+yVvFC1nY9pmeTjMW+Yxisp2E68p5gnrTjb2thHbblFUEeLSuxYXOlwhhJj1JLEVkDuS5WcH9pGwivioE+REsJ2s4XBR4z5+5Hwc0/X4fLCe/vYkV31oOQEZtV8IIc5IElsBHXvkNV6MNtKcdVgVCbPTPE5pxW66KuvZFric6/s76XkmzsJVFSzZUF3ocIUQ4oIgia1AtKd56Pnn6AjX8+msRWewi5SRYfnCvfzQ+U3C2Qx3xWtxbI+rP7xcxoIUQoizJImtQAbfbOExFaXYcVkfivCmeYyKqj0cLr2IvYG13NrfTffrcdbfuJDyulihwxVCiAuGJLYCefnBX3IwtoSPp2Eo1EvCSLO4cR8/dD9BUTrBVS0VREuCbL59UaFDFUKIC4oktgJwhtLc29kFmFwdiPKWdZzy8oPsKV3LcWsp9wwOMnQkw6V3LiYYlnvohRDinZDEVgBHHn6Z7dFmbk45GOE4g0acRQt2ca/3YUqTI6zdX0JZbZRVV9YXOlQhhLjgSGKbYdrT/OKl1xgMlPNBQuy2WigrbeFg+QpOmIu5bWCIRKfN5e9dimHKP48QQrxT8ss5w4Z3tfOkCrHU9qiMaFrNPpoW7OBe7yMUp+Ks3BWjbkkpizdUFTpUIYS4IElim2EvP/hLDkUX8x8ycCDQSnFxF8crl3DUXMYtPQM4g5or3r9UuvcLIcS7JIltBnlph3tbO7GwWBYJc9DsYEHDDu7VH6IonWDVzgiL1lZSv6ys0KEKIcQFSxLbDGp75i3eiDby4aRDV6iTQGSInuoqDhkXc2N3P8QNLr1TxoMUQohzcVaJTSkVUUqtmO5g5rrHfv0ifYFKbrbC7DPbaKx7k0fV3YTtNGt2h1m0tlJG7xdCiHN0xsSmlLoLeBN43F/foJR6cJrjmnPsviRPJLMstzUqkiAdHMKrd3jDuIytnT0Yw6acrQkhxHlwNmdsfwVcBgwCaK3fBOQX+B068MgL7Ik187ms5oDVTn3tfp4I3IHpOWzeF6BZztaEEOK8OJvEZmuthyaV6ekIZq7SWnPva2+hVZSmSJAWo5tYQzvPcT3rOjuJ9Ae59A75W0EIIc6Hs0lsu5VSHwNMpdRypdSXgZemOa45JXmkjxdUiPemXTpCHVRWtvBs5AYcLK44rFi4spzaRXK2JoQQ58PZJLbfB1YDGeBHwDDwR+dyUKVUmVLq50qpfUqpvUqpy89lf7Pd9oef5HCkmfcoi/1mO9X1B/iVvpWlfZ2Ut4fZeEtzoUMUQog544wj7Gqtk8B/9qfz5UvA41rrDyqlgkD0PO57VtGe5t79x6mKroZoEqK97KtcQVyV8N4Tw1QvLGbBxeWFDlMIIeaMMyY2pdTTTNGmprW+4d0cUClVClwDfNrfTxbIvpt9XQhG9newPVDGb6UcjkQ6aKjbx1f5NGXJIZoPhtn02WYZZUQIIc6js3kmyp/mLYeBDwDOORxzMdADfFsptR7YDvyh1jpxDvuctV5++BnaQ/WstiyetrqortMcVhdxS+sJSiurWLqputAhCiHEnHLGNjat9fa86UWt9X8ErjuHY1rAJuBftdYbgQTwhcmVlFKfU0ptU0pt6+npOYfDFY72NL842s5G22Ao3EtVzTGeCtxMwM2ydneADTc1yQj+Qghxnp3NDdoVeVOVUuo9QOk5HLMVaNVav+qv/5xcoptAa/11rfVmrfXm6uoL86xmeE8bbwbL+LjjcdDsoLiulZe5itUd3ZSZMS6+vK7QIQohxJxzNpcit5NrY1PkLkEeBT77bg+ote5USrUopVZorfcDNwJ73u3+ZrPnH36GvlA91drjUFE720s3Yasgl+z3WHlFvTwdWwghpsHZ9IqcjjuHfx/4gd8j8gjwmWk4RkFpT/PgiU5uscpoL+mkuvYw3+d3aBjqob4vxpprGwsdohBCzEmnTGxKqfef7oVa6/ve7UH9Ybk2v9vXXwiGdrfxVrCCv3I1b1odhOsCdKt67jx2gkWrmymrmbN3OAghREGd7oztrtNs08C7TmzzwXOPPctQsBbLSBKrOM6zgWsJuhlWHQqy9rcXFDo8IYSYs06Z2LTWc+7y4EzRWvPQsU7utkpoK+2mtK6d1/g8Kzu6qamopGllRaFDFEKIOeusei8ope4gN6xWeLRMa/3fpyuoC13ieD+7g6X8pdbsCLUyWLkYWwVZf1Cz9toFKENuyBZCiOlyNt39vwp8hFyHDwV8CJDBDU/j9UefYyRQB9EUlbWHeU7dQGWin8X9RazYKl38hRBiOp3N3cFXaK0/CQxorf8bcDlw0fSGdWF7cPdRPpD2aLG6seuyHFHLWH8szvJNNYRjgUKHJ4QQc9rZJLaUP08qpRoAG6ifvpAubJneOG+aEa40Ff1FR3gtthnTc1h3OMCqKxsKHZ4QQsx5Z9PG9rBSqgz438Ab5HpEfmM6g7qQ7X78FQaCjdjBESpqjvMS97Cov4sFRRU0XFRW6PCEEGLOO5sbtP/aX7xXKfUwEJ7iidrCd//ru7nbW8SJaBdDdRGGVRnXHR1m1VUNMoq/EELMgLPpPPK2UuovlFJLtdYZSWqn5qZsXvdMthqaeMlhXg9dSshNs7olLJ1GhBBihpxNG9td5MaI/KlS6nWl1J8qpZqmOa4L0vHn3qI11IgXTVJW28o2trC0q4flq+qIlYYKHZ4QQswLZ/PYmuNa67/TWl8CfAxYR24gZDHJ/U9t45asSbvVRUtNFWkVYd0RzcorpK+NEELMlLO9QbuZ3L1sHwFc4D9NZ1AXIu1pXo1n+ITpcqRiP68GtlKUHWHVYAlNqysLHZ4QQswbZ0xsSqlXgQDwU+BDWusj0x7VBWjkcBdHglWY4QyRum52soH1be2s2LwC05KHiQohxEw5mzO2T/rPTROn8dwjL7DJK6HTOsrhygW4ymL9EcWK35JOI0IIMZPOpo1NktpZePxgK7d6DiPle9lmXUp5up+VqpLaxSWFDk0IIeYVuUZ2HjgjGXYbYcLRLMHaXvayiqVtw1y8pV7uXRNCiBkmie08OPzsm8TMOnoCvRyrqkcrk7XHLFZsqS10aEIIMe+czQ3aUaXUf1FKfcNfX66UunP6Q7tw3P/Mdt5ruwyW7eONwCbKMgNsKq6ltFqeki2EEDPtbM7Yvg1kyI3qD9AG/M20RXSB0VrzesqmLuIQqO1iD2tZ2j7ERZdKpxEhhCiEs0lsS7XWf0duVH+01klyz2UT5Lr5dwZrSISHOVFdg6dM1hw1WbqxptChCSHEvHQ2iS2rlIqQG9UfpdRScmdwAnjm0Ze42Q7RV3KAHcFNlGYHubSkjqJyGUJLCCEK4WwS218BjwMLlVI/AJ4E/nw6g7qQ/OpAKxtNF13Txi7WsqRzgOWXyGVIIYQolLN5bM0vlVLbga3kLkH+oda6d9ojuwBox2O/CnBDNE1rTSWuCrDmiMXSu+UypBBCFMrZ9Ip8Umvdp7V+RGv9sNa6Vyn15EwEN9t1vXmYRlVDb+w4e8IrKXJGuLRILkMKIUQhnTKxKaXCSqkKoEopVa6UqvCnRUDjjEU4iz3y6Mvc6GkylQd4iw009fZw8Wa5DCmEEIV0ukuRnwf+CGgAtjPeE3IY+Mr0hnVheKGrn/fFLI7UhUmrKCuOWiy9TS5DCiFEIZ0ysWmtvwR8SSn1+1rrL89gTBcEN5ml3SxmMNzHwZLFBLwsVwaqiJXJZUghhCiks+k88mWl1BpgFRDOK//edAY22x1+YSdb3RLila+zQ72fBUPtrFq/sdBhCSHEvHc2nUf+EviyP10P/B1w9zTHNev94sk3WBuwiddm6FU1LD2hWbK+utBhCSHEvHc297F9ELgR6NRafwZYD5ROa1QXgO3xJESyHK1oAOCykRLKamVsSCGEKLSzSWwprbUHOEqpEqAbWDi9Yc1u9nAKx6gkXnqYt631NCTbuHR1c6HDEkIIwdkltm1KqTLgG+R6R74BvDydQc12bz+9neu8IIM1rRxWF9HUmWSxXIYUQohZ4Ww6j/yOv/hVpdTjQInW+u3pDWt2e+S5nawLl/BidRkA69pj1CySJ2ULIcRscFYjj4wua62Paa3fnu8jj+xM26SjIxyKLqXIGebq5iYMQx54IIQQs8Epz9iUUmEgij/yCOM3aJcwj0cecZIZqlUlI5WvslN9kAVDnSzbsKrQYQkhhPDJyCPv0L5n32SLUrTWpYmrEpac6GXh3eWFDksIIYRPRh55hx546i0uDoc5UVaP0h7XWjVYQbPQYQkhhPCdbhDkS5VSdaNJTSn1SaXUA0qpf/IHR56XdiZTpEo62Bu8mPp0OxvXLip0SEIIIfKcrvPI14AsgFLqGuB/Ad8DhoCvT39os4+bdqihjMGaIxziIhp7R2heM29zvBBCzEqnS2ym1rrfX/4I8HWt9b1a6/8CLDvXAyulTKXUDqXUw+e6r5ly8IWdbDIM2mojeMpkTUcJpdUy2ogQQswmp01sSqnRNrgbgafytp3x/rez8IfA3vOwnxnzwK+2EQvbHC5qIuwluXHhokKHJIQQYpLTJbYfAc8qpR4AUsDzAEqpZeQuR75rSqkFwB3Av53LfmbaW/Ekmcrj7DbWsmj4BMvXyUNFhRBitjllYtNa/w/gT4DvAFdprXXea37/HI/7f4H/BHjnuJ8Zoz1NLaV01PXQp6pp6ITGi8oKHZYQQohJTntJUWv9yhRlB87lgEqpO4FurfV2pdR1p6n3OeBzAE1NTedyyPOiY+cRNpgG2ytzDza4PCvd/IUQYjY6m0GQz7crgbuVUseAHwM3KKW+P7mS1vrrWuvNWuvN1dWFH2D4wQdeIhp2OBptoszp56rVSwsdkhBCiCnMeGLTWn9Ra71Aa70I+CjwlNb6EzMdxzu1vauHTOVR9qlVLBhuZ9GaqkKHJIQQYgqFOGO7IJXrElrrBxhRpSzotOShokIIMUsVNLFprZ/RWt9ZyBjORqpnmJWWyYmK3JiQ1webUEpG8xdCiNlIztjOwnMPvUI0bHMksojqbBdb1iwqdEhCCCFOQRLbWXhyx14yVSfYp1bSONzFwpWVhQ5JCCHEKUhiOwtxN0hLQ5y0itLcFSJWFip0SEIIIU5BEtsZeLbLKsPiWHmufe2m4iUFjkgIIcTpSGI7g0Ov7KE67HEkvJj6TCuXrF9U6JCEEEKchiS2M3jwkVdxqjo4pC6icbhThtESQohZ7nyM0j+n7R8aIbsyQUaFaeyNEYoGCh2SEEKI05AztjNYasQ4Xl4EwHvKzvkxdEIIIaaZJLbTyAzGaQ7A8Wgj1XYXl26Q8SGFEGK2k8R2Gs8++AqU93PIWEFjvI26paWFDkkIIcQZSGI7jSe37aSjcYC4Kqa2zyAQksfUCCHEbCeJ7TQsJ8CRqtzN2NcG5DKkEEJcCCSxnYLWmiUBk5ZYPSXuENdtWFHokIQQQpwFSWynMNLeRyyW4JC5jKbEMRqWlxc6JCGEEGdBEtspPPyTZ+lv7KdPVVPTnyEck/vXhBDiQiCJ7RReP3yUIzW5+9cvc5sKHI0QQoizJYntFOoIcby4koiX4pa1awsdjhBCiLMkiW0K2tM0hDTHgotoSh2n+eKqQockhBDiLElim0LHnhPo6n5aWUjtcBexUnn+mhBCXChkEOQp3P/Tp+hf5qKVQfOwnK0JIcSFRM7YpnC0d4DjZREA7ly6qcDRCCGEeCcksU1hsRngRLSeOruT9euaCx2OEEKId0AS2yRaa8pjGY4aS2iMn6C0JlLokIQQQrwDktgm6TrQxkDjMCOqlKqhJEqpQockhBDiHZDENsm9P3yCozW5UfwvNVYWOBohhBDvlCS2STq7+zlRUkZIp7lj88ZChyOEEOIdksQ2SVMQTgQX0JxuoXFpRaHDEUII8Q5JYpskWJGiRTVTN9xGMCy3+QkhxIVGEluegbY+2he4uMqidihY6HCEEEK8C5LY8jz0gyc4VpHr3n9X0xUFjkYIIcS7IYktz8FjrbRGq6h0e7nskosKHY4QQoh3QRJbnoUBaAksYGGyndJquTFbCCEuRJLY8pjVSbpVHdUjnXJjthBCXKAksfnSI0naGnIfR11CztaEEOJCJYnN99gPfk2L33HkAxffXOBohBBCvFuS2Hxv7txFa7SSGqebDZuWFjocIYQQ75IkNl+DpThhLWRBqp1wUaDQ4QghhHiXJLH5jJosfaqamuHOQocihBDiHEhiI/cMttGOI7XScUQIIS5oktiAw28corU8l9A+uuGOAkcjhBDiXMx4YlNKLVRKPa2U2qOU2q2U+sOZjmGyn33/Z7REq6hzOlm9flGhwxFCCHEOCjF8vQP8idb6DaVUMbBdKfUrrfWeAsQCQJG2OWEtZNlIi4zoL4QQF7gZP2PTWndord/wl0eAvUDjTMeRL1DtMqAqqR3qKmQYQgghzoOCtrEppRYBG4FXCxlHR33uETXlw9lChiGEEOI8KFhiU0oVAfcCf6S1Hp5i++eUUtuUUtt6enqmLQ4nk6WtPIrSHr95xcem7ThCCCFmRkESm1IqQC6p/UBrfd9UdbTWX9dab9Zab66urp62WB77zqO0R8up8XpYtXbRtB1HCCHEzChEr0gFfBPYq7X+h5k+/mS7d+2gNVBPQ7obK2AWOhwhhBDnqBBnbFcCvwncoJR6059uL0AcAJSVenSrOuqGp+9ypxBCiJkz433btdYvALPmYWf9DbmOI6XDvQWORAghxPkw70ce6agMA3BVwzUFjkQIIcT5MK8TW29bH23FJRTpEe686YZChyOEEOI8mNeJ7Vtf+lfagjUsyHYQjgYLHY4QQojzYF4ntpiXot1opCEuHUeEEGKumNeJLVMXxFZBqgYlsQkhxFwxrxNbZ00IgCo7VOBIhBBCnC/zOrG1lRUR0Fn+3/f+dqFDEUIIcZ7M28TWeayT9nAlDW4nNfVlhQ5HCCHEeTJvE9t3v/SPtFqNNCalfU0IIeaSeZvYghUmI6qEuqH+QocihBDiPJq3iW2wOgJA2aAkNiGEmEvmbWLrqogCsKFxQ2EDEUIIcV7N28TWWVRKmTfAh9734UKHIoQQ4jyal4mtu62XjmAVDXYXypg1DxoQQghxHszLxPbtf/hbOox66pIDhQ5FCCHEeTYvE5tRGSCrQtQMDhY6FCGEEOfZvExsg9UxAEqkR6QQQsw58zKxdZXnEtsVq+ThokIIMdfMy8TWGSul2uvm1lvvKnQoQgghzrN5l9gGugdoD1ZTn+0udChCCCGmwbxLbP/2939Dl6qjLjFY6FCEEEJMg3mX2LyqCK6yqB4cKnQoQgghpsG8S2z9VbmOI8UDcg+bEELMRfMusXWVFWFqh+svubHQoQghhJgG8y6xdUTLqPG6ufaG2wodihBCiGkw7xJbV6CKumxfocMQQggxTeZVYvvK33yRHlVNTVI6jgghxFw1rxLbiKVxlUXVcLzQoQghhJgm8yuxVRQBUDIwXOBIhBBCTJd5ldh6S3Nd/cu1WeBIhBBCTJd5ldi6YyWU637+4E//utChCCGEmCbzKrF1hcqptXsKHYYQQohpNG8S29uvvkaXWUtterDQoQghhJhG8yaxPfr4j0iqGNUjI4UORQghxDSaN4ktXV4CQPmgdPUXQoi5bN4ktv7yXFf/yKB09RdCiLls3iS27pJiQjrNPbf/ZqFDEUIIMY3mT2ILl1LndrF608ZChyKEEGIazZvE1mVVU5vtL3QYQgghpllBEptS6lal1H6l1CGl1Bem+3j/+D/+nD6jipqEtK8JIcRcN+OJTSllAv8M3AasAn5DKbVqOo8ZD+WG0Kockh6RQggx11kFOOZlwCGt9REApdSPgXuAPdN1wIZgMb+7/acE4onpOoQQQohZohCJrRFoyVtvBbZM5wE/+wdfnM7dCyGEmEVmbecRpdTnlFLblFLbenpkfEchhBBnpxCJrQ1YmLe+wC+bQGv9da31Zq315urq6hkLTgghxIWtEIntdWC5UmqxUioIfBR4sABxCCGEmINmvI1Na+0opX4PeAIwgW9prXfPdBxCCCHmpkJ0HkFr/SjwaCGOLYQQYm6btZ1HhBBCiHdDEpsQQog5RRKbEEKIOUUSmxBCiDlFEpsQQog5RWmtCx3DGSmleoDj52FXVUDvedjPXCSfzanJZ3Nq8tmcmnw2p3Y+PptmrfWUo3dcEIntfFFKbdNaby50HLORfDanJp/Nqclnc2ry2ZzadH82cilSCCHEnCKJTQghxJwy3xLb1wsdwCwmn82pyWdzavLZnJp8Nqc2rZ/NvGpjE0IIMffNtzM2IYQQc9y8SGxKqVuVUvuVUoeUUl8odDwzTSm1UCn1tFJqj1Jqt1LqD/3yCqXUr5RSB/15uV+ulFL/5H9ebyulNhX2HUw/pZSplNqhlHrYX1+slHrV/wx+4j9iCaVUyF8/5G9fVNDAp5lSqkwp9XOl1D6l1F6l1OXyvclRSv2x/9/TLqXUj5RS4fn8vVFKfUsp1a2U2pVX9o6/K0qpT/n1DyqlPvVuYpnziU0pZQL/DNwGrAJ+Qym1qrBRzTgH+BOt9SpgK/C7/mfwBeBJrfVy4El/HXKf1XJ/+hzwrzMf8oz7Q2Bv3vrfAv+otV4GDACf9cs/Cwz45f/o15vLvgQ8rrW+GFhP7jOa998bpVQj8AfAZq31GnKP4Poo8/t78x3g1kll7+i7opSqAP4S2AJcBvzlaDJ8R7TWc3oCLgeeyFv/IvDFQsdV4M/kAeBmYD9Q75fVA/v95a8Bv5FXf6zeXJzIPcX9SeAG4GFAkbt51Jr8HSL3HMHL/WXLr6cK/R6m6XMpBY5Ofn/yvdEAjUALUOF/Dx4G3jPfvzfAImDXu/2uAL8BfC2vfEK9s53m/Bkb41/AUa1+2bzkXwLZCLwK1GqtO/xNnUCtvzzfPrP/C/wnwPPXK4FBrbXjr+e//7HPxt8+5NefixYDPcC3/cu0/6aUiiHfG7TWbcDfAyeADnLfg+3I92ayd/pdOS/fofmQ2IRPKVUE3Av8kdZ6OH+bzv15NO+6yCql7gS6tdbbCx3LLGQBm4B/1VpvBBKMX0oC5vX3phy4h1zybwBinHwZTuSZye/KfEhsbcDCvPUFftm8opQKkEtqP9Ba3+cXdyml6v3t9UC3Xz6fPrMrgbuVUseAH5O7HPkloEwpNfqE+fz3P/bZ+NtLgb6ZDHgGtQKtWutX/fWfk0t08r2Bm4CjWuserbUN3EfuuyTfm4ne6XflvHyH5kNiex1Y7vdWCpJr4H2wwDHNKKWUAr4J7NVa/0PepgeB0V5HnyLX9jZa/km/59JWYCjvcsKcorX+otZ6gdZ6EbnvxlNa648DTwMf9KtN/mxGP7MP+vXn5BmL1roTaFFKrfCLbgT2IN8byF2C3KqUivr/fY1+NvP+ezPJO/2uPAHcopQq98+Kb/HL3plCNzbOUIPm7cAB4DDwnwsdTwHe/1XkLgG8DbzpT7eTu8b/JHAQ+DVQ4ddX5HqSHgZ2kuv5VfD3MQOf03XAw/7yEuA14BDwMyDkl4f99UP+9iWFjnuaP5MNwDb/u/MLoFy+N2OfzX8D9gG7gH8HQvP5ewP8iFx7o03ubP+z7+a7AvyW/zkdAj7zbmKRkUeEEELMKfPhUqQQQoh5RBKbEEKIOUUSmxBCiDlFEpsQQog5RRKbEEKIOUUSmxDniVKqUin1pj91KqXa/OW4UupfpuF4K5RSz/jH2KuU+rpfvkEpdfv5Pp4QFwrrzFWEEGdDa91H7r4vlFJ/BcS11n8/jYf8J3IjyT/gH3OtX74B2Aw8Oo3HFmLWkjM2IaaZUuo6Nf6ct79SSn1XKfW8Uuq4Uur9Sqm/U0rtVEo97g99hlLqEqXUs0qp7UqpJ0aHJZqkntyNsABorXf6o+v8d+Aj/pncR5RSMf9ZWa/5gxnf4x/j00qpB/yzvoNKqb+c/k9DiOkniU2ImbeU3JiUdwPfB57WWq8FUsAdfnL7MvBBrfUlwLeA/zHFfv4ReEop9ZjKPfSyTGudBf4r8BOt9Qat9U+A/0xuCKfLgOuB/+2P0g+5Z159AFgHfEgptXm63rQQM0UuRQox8x7TWttKqZ3kHlD5uF++k9zzrFYAa4Bf5YYhxCQ3VNEEWutvK6WeIDeq/D3A55VS66c43i3kBnr+U389DDT5y7/yL6GilLqP3PBr2875HQpRQJLYhJh5GQCttaeUsvX4uHYeuf8mFbBba335mXaktW4nd0b3LaXULnIJcTIFfEBrvX9CoVJbOPkxIjLGnrjgyaVIIWaf/UC1UupyyD1ySCm1enIlpdSteW1ydeQGnG0DRoDivKpPAL/vj0KPUmpj3rablVIVSqkI8F7gxWl4P0LMKElsQswyfjvZB4G/VUq9Re5pDFdMUfUWYJdf5wngz3TuUTNPA6tGO48Afw0EgLeVUrv99VGvkXtO39vAvVpruQwpLngyur8Q85RS6tPkHhfye4WORYjzSc7YhBBCzClyxiaEEGJOkTM2IYQQc4okNiGEEHOKJDYhhBBziiQ2IYQQc4okNiGEEHOKJDYhhBBzyv8PsD152pHGMSIAAAAASUVORK5CYII=\n",
+ "text/plain": [
+ "