diff --git a/docs/source/kernels.rst b/docs/source/kernels.rst index 5ac842d3c..f9acc0c07 100644 --- a/docs/source/kernels.rst +++ b/docs/source/kernels.rst @@ -127,6 +127,12 @@ Specialty Kernels .. autoclass:: MultitaskKernel :members: +:hidden:`RBFKernelGrad` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: RBFKernelGrad + :members: + Kernels for Scalable GP Regression Methods -------------------------------------------- diff --git a/docs/source/means.rst b/docs/source/means.rst index 4dcde4c56..324668988 100644 --- a/docs/source/means.rst +++ b/docs/source/means.rst @@ -39,3 +39,9 @@ Specialty Means .. autoclass:: MultitaskMean :members: + +:hidden:`ConstantMeanGrad` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ConstantMeanGrad + :members: diff --git a/docs/source/utils.rst b/docs/source/utils.rst index dcf63af5c..86354584f 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -25,6 +25,12 @@ Pivoted Cholesky Utilities .. automodule:: gpytorch.utils.pivoted_cholesky :members: +Quadrature Utilities +~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: gpytorch.utils.quadrature + :members: + Sparse Utilities ~~~~~~~~~~~~~~~~~ diff --git a/examples/10_GP_Regression_Derivative_Information/README.md b/examples/10_GP_Regression_Derivative_Information/README.md new file mode 100644 index 000000000..b0b29f89c --- /dev/null +++ b/examples/10_GP_Regression_Derivative_Information/README.md @@ -0,0 +1,3 @@ +# GP regression with derivative information + +This is a new feature, and is likely unstable. Enjoy! diff --git a/examples/10_GP_Regression_Derivative_Information/Simple_GP_Regression_Derivative_Information_1d.ipynb b/examples/10_GP_Regression_Derivative_Information/Simple_GP_Regression_Derivative_Information_1d.ipynb new file mode 100644 index 000000000..1c0e58b6a --- /dev/null +++ b/examples/10_GP_Regression_Derivative_Information/Simple_GP_Regression_Derivative_Information_1d.ipynb @@ -0,0 +1,304 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# GPyTorch regression with derivative information\n", + "\n", + "## Introduction\n", + "In this notebook, we show how to train a GP regression model in GPyTorch of an unknown function given function value and derivative observations. We consider modeling the function:\n", + "\n", + "\\begin{align*}\n", + " y &= \\sin(2x) + cos(x) + \\epsilon \\\\\n", + " \\frac{dy}{dx} &= 2\\cos(2x) - \\sin(x) + \\epsilon \\\\ \n", + " \\epsilon &\\sim \\mathcal{N}(0, 0.5)\n", + "\\end{align*}\n", + "\n", + "using 50 value and derivative observations." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/gpleiss/miniconda3/envs/gpytorch/lib/python3.7/site-packages/matplotlib/__init__.py:1003: UserWarning: Duplicate key in file \"/home/gpleiss/.config/matplotlib/matplotlibrc\", line #57\n", + " (fname, cnt))\n" + ] + } + ], + "source": [ + "import torch\n", + "import gpytorch\n", + "import math\n", + "from matplotlib import pyplot as plt\n", + "import numpy as np\n", + "\n", + "%matplotlib inline\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting up the training data\n", + "We use 50 uniformly distributed points in the interval $[0, 5 \\pi]$" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "lb, ub = 0.0, 5*math.pi\n", + "n = 50\n", + "\n", + "train_x = torch.linspace(lb, ub, n).unsqueeze(-1)\n", + "train_y = torch.stack([\n", + " torch.sin(2*train_x) + torch.cos(train_x), \n", + " -torch.sin(train_x) + 2*torch.cos(2*train_x)\n", + "], -1).squeeze(1)\n", + "\n", + "train_y += 0.05 * torch.randn(n, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting up the model\n", + "A GP prior on the function values implies a multi-output GP prior on the function values and the partial derivatives, see 9.4 in http://www.gaussianprocess.org/gpml/chapters/RW9.pdf for more details. This allows using a `MultitaskMultivariateNormal` and `MultitaskGaussianLikelihood` to train a GP model from both function values and gradients. The resulting RBF kernel that models the covariance between the values and partial derivatives has been implemented in `RBFKernelGrad` and the extension of a constant mean is implemented in `ConstantMeanGrad`.\n", + "\n", + "The `RBFKernelGrad` is generally worse conditioned than the `RBFKernel`, so we place a lower bound on the noise parameter to keep the smallest eigenvalues of the kernel matrix away from zero." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "class GPModelWithDerivatives(gpytorch.models.ExactGP):\n", + " def __init__(self, train_x, train_y, likelihood):\n", + " super(GPModelWithDerivatives, self).__init__(train_x, train_y, likelihood)\n", + " self.mean_module = gpytorch.means.ConstantMeanGrad()\n", + " self.base_kernel = gpytorch.kernels.RBFKernelGrad()\n", + " self.covar_module = gpytorch.kernels.ScaleKernel(self.base_kernel)\n", + "\n", + " def forward(self, x):\n", + " mean_x = self.mean_module(x)\n", + " covar_x = self.covar_module(x)\n", + " return gpytorch.distributions.MultitaskMultivariateNormal(mean_x, covar_x)\n", + "\n", + "likelihood = gpytorch.likelihoods.MultitaskGaussianLikelihood(num_tasks=2) # Value + Derivative\n", + "likelihood.initialize(noise=0.01*train_y.std()) # Require 1% noise (approximately)\n", + "likelihood.raw_noise.requires_grad = False\n", + "model = GPModelWithDerivatives(train_x, train_y, likelihood)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training the model\n", + "The model training is similar to training a standard GP regression model" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/gpleiss/workspace/gpytorch/gpytorch/utils/pivoted_cholesky.py:101: UserWarning: torch.potrs is deprecated in favour of torch.cholesky_solve and will be removed in the next release. Please use torch.cholesky instead and note that the :attr:`upper` argument in torch.cholesky_solve defaults to ``False``.\n", + " R = torch.potrs(low_rank_mat, torch.cholesky(shifted_mat, upper=True))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iter 1/50 - Loss: 60.662 lengthscale: 0.693 noise: 0.014\n", + "Iter 2/50 - Loss: 58.924 lengthscale: 0.744 noise: 0.014\n", + "Iter 3/50 - Loss: 57.026 lengthscale: 0.798 noise: 0.014\n", + "Iter 4/50 - Loss: 55.471 lengthscale: 0.853 noise: 0.014\n", + "Iter 5/50 - Loss: 52.139 lengthscale: 0.905 noise: 0.014\n", + "Iter 6/50 - Loss: 51.552 lengthscale: 0.950 noise: 0.014\n", + "Iter 7/50 - Loss: 50.446 lengthscale: 0.988 noise: 0.014\n", + "Iter 8/50 - Loss: 46.764 lengthscale: 1.015 noise: 0.014\n", + "Iter 9/50 - Loss: 48.350 lengthscale: 1.028 noise: 0.014\n", + "Iter 10/50 - Loss: 43.598 lengthscale: 1.036 noise: 0.014\n", + "Iter 11/50 - Loss: 42.887 lengthscale: 1.037 noise: 0.014\n", + "Iter 12/50 - Loss: 42.121 lengthscale: 1.032 noise: 0.014\n", + "Iter 13/50 - Loss: 39.327 lengthscale: 1.026 noise: 0.014\n", + "Iter 14/50 - Loss: 38.159 lengthscale: 1.020 noise: 0.014\n", + "Iter 15/50 - Loss: 36.058 lengthscale: 1.018 noise: 0.014\n", + "Iter 16/50 - Loss: 33.545 lengthscale: 1.019 noise: 0.014\n", + "Iter 17/50 - Loss: 30.586 lengthscale: 1.024 noise: 0.014\n", + "Iter 18/50 - Loss: 29.251 lengthscale: 1.036 noise: 0.014\n", + "Iter 19/50 - Loss: 24.608 lengthscale: 1.053 noise: 0.014\n", + "Iter 20/50 - Loss: 25.399 lengthscale: 1.069 noise: 0.014\n", + "Iter 21/50 - Loss: 22.303 lengthscale: 1.091 noise: 0.014\n", + "Iter 22/50 - Loss: 19.724 lengthscale: 1.119 noise: 0.014\n", + "Iter 23/50 - Loss: 22.927 lengthscale: 1.141 noise: 0.014\n", + "Iter 24/50 - Loss: 19.074 lengthscale: 1.164 noise: 0.014\n", + "Iter 25/50 - Loss: 17.078 lengthscale: 1.176 noise: 0.014\n", + "Iter 26/50 - Loss: 14.719 lengthscale: 1.181 noise: 0.014\n", + "Iter 27/50 - Loss: 14.614 lengthscale: 1.183 noise: 0.014\n", + "Iter 28/50 - Loss: 10.786 lengthscale: 1.180 noise: 0.014\n", + "Iter 29/50 - Loss: 9.239 lengthscale: 1.166 noise: 0.014\n", + "Iter 30/50 - Loss: 6.483 lengthscale: 1.151 noise: 0.014\n", + "Iter 31/50 - Loss: 6.814 lengthscale: 1.141 noise: 0.014\n", + "Iter 32/50 - Loss: 4.227 lengthscale: 1.146 noise: 0.014\n", + "Iter 33/50 - Loss: 4.655 lengthscale: 1.158 noise: 0.014\n", + "Iter 34/50 - Loss: 2.414 lengthscale: 1.174 noise: 0.014\n", + "Iter 35/50 - Loss: 2.902 lengthscale: 1.190 noise: 0.014\n", + "Iter 36/50 - Loss: -3.138 lengthscale: 1.209 noise: 0.014\n", + "Iter 37/50 - Loss: 2.241 lengthscale: 1.228 noise: 0.014\n", + "Iter 38/50 - Loss: -6.051 lengthscale: 1.241 noise: 0.014\n", + "Iter 39/50 - Loss: -3.690 lengthscale: 1.251 noise: 0.014\n", + "Iter 40/50 - Loss: -2.811 lengthscale: 1.256 noise: 0.014\n", + "Iter 41/50 - Loss: -9.964 lengthscale: 1.259 noise: 0.014\n", + "Iter 42/50 - Loss: -5.040 lengthscale: 1.253 noise: 0.014\n", + "Iter 43/50 - Loss: -9.899 lengthscale: 1.250 noise: 0.014\n", + "Iter 44/50 - Loss: -10.464 lengthscale: 1.241 noise: 0.014\n", + "Iter 45/50 - Loss: -8.486 lengthscale: 1.234 noise: 0.014\n", + "Iter 46/50 - Loss: -9.840 lengthscale: 1.231 noise: 0.014\n", + "Iter 47/50 - Loss: -12.451 lengthscale: 1.235 noise: 0.014\n", + "Iter 48/50 - Loss: -12.304 lengthscale: 1.246 noise: 0.014\n", + "Iter 49/50 - Loss: -7.173 lengthscale: 1.257 noise: 0.014\n", + "Iter 50/50 - Loss: -11.317 lengthscale: 1.268 noise: 0.014\n" + ] + } + ], + "source": [ + "# Find optimal model hyperparameters\n", + "model.train()\n", + "likelihood.train()\n", + "\n", + "# Use the adam optimizer\n", + "optimizer = torch.optim.Adam([\n", + " {'params': model.parameters()}, # Includes GaussianLikelihood parameters\n", + "], lr=0.1)\n", + "\n", + "# \"Loss\" for GPs - the marginal log likelihood\n", + "mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, model)\n", + "\n", + "n_iter = 50\n", + "for i in range(n_iter):\n", + " optimizer.zero_grad()\n", + " output = model(train_x)\n", + " loss = -mll(output, train_y)\n", + " loss.backward()\n", + " print('Iter %d/%d - Loss: %.3f lengthscale: %.3f noise: %.3f' % (\n", + " i + 1, n_iter, loss.item(),\n", + " model.covar_module.base_kernel.lengthscale.item(),\n", + " model.likelihood.noise.item()\n", + " )) \n", + " optimizer.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Making predictions with the model\n", + "Model predictions are also similar to GP regression with only function values, butwe need more CG iterations to get accurate estimates of the predictive variance" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAr4AAAFwCAYAAABejXgsAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzsvXl8HPV9//+ave9Lty0fkm0MmMPCooGEBLDdtOCEAnFqStokrQttSdL2V9cEl5DriyGB8PsWkibEtQtJExKKAzkKTQg2CSVAi4wgHHawLfnQuSvtfc3sHN8/5tjZ1UraUzsjfZ6Phx/SrmdnP6OZ+czr8/68Pu83JQgCCAQCgUAgEAiExY6h2Q0gEAgEAoFAIBAWAiJ8CQQCgUAgEAhLAiJ8CQQCgUAgEAhLAiJ8CQQCgUAgEAhLAiJ8CTVBUdRWiqJOUhT1VYqitlMUdTtFUUca8D29FEU9Ue/9Fn3HVoqiftnI7yAQCAQ9QFHUJSX69lsr+HxNffZC9PmEpQlFsjoQakXqnO4VBOE16fXtgiDcV4f9bhcE4aDqtU8QhGit+53nO58QBOGjjfwOAoFA0AMl+vavAvilIAjPlfn5ivrsZvT5hKUHifgS6gpFUT4Ar9VpP7+vet0LoLfW/RIIBAKhar4N4KvlbFhpn036fMJCQYQvod5sFQThObVtQJoq+6r0+1aKon4p/bydoqhL5A9Kr7dK02n9APopitqu2vdXVdveqtp2zv1K/79dmrbzSb8/If28tdT03Rzt96nbKU0HbpX/1e2vSCAQCBpDEIQhSGK0RF+4XeqDt1MUdbv0EbnfLKf/nbXPn+XzpC8mVAURvoR6sYOiqG8DCABA0VTYt+VfpPcD0s+DAHYAopAFMCS975N+huVpL6nDjUrb3q7adkiyVpTcr+p7D0qfiQIYAnAvgF5BEPYB+Kvig5mt/QD2AHhN+v818vfIbSn7r0UgEAj6xCf9LOgLpT62V/q5T91nl9P/ztXnF39esqORvphQFUT4EurF44Ig/BXK63DCJd7bJH+2DH/wparvGZJez7ZfNU9I0YSA5Fl7TYoMzPc5Nb0AfFJEeRpiB/770oI+35yfJBAIBB1TZGUr7gsh/98svtxa+1/l87N8P+mLCWVBhC+hrhRFSuXOrxyf1kmoptDUny+2LUAUu/I+ewG8Wmbz/gNiVCAsRZh75fZKfrJiSrX/VYgRh9cA7INo7fisIAibAJDpNQKBsJi5FaLABGb2hfNRTv87W59f8PlZvp/0xYSyMDW7AQR9I3VQl0C0OkSl6SmZV6URvQ/AVtVihV7pc1sBXCKt3L1P8tLKnz0I0cawHcBz8vdQFNUrCMJnJW8XAFwifXbrLPtVIg+CIEQpigoLgvAaRVEBaZutEKMUl0iC+xKKoi6ROtMZ7Ze+63bp8wBwaVGbCQQCQfcU9e1y3x1VWREK+kK5P5b7z6I+e2i+/hdiQGO2Pr/g86W+H6QvJpQJSWdGIBAIBAKBQFgSEKsDgUAgEAgEAmFJQIQvgUAgEAgEAmFJQIQvgUAgEAgEAmFJQIQvgUAgEAgEAmFJQIQvgUAgEAgEAmFJQIQvgUAgEAgEAmFJsGB5fO+44w6SN41AIOiWr3zlK9T8Wy0eSJ9NIBD0Tql+e0ELWHzpS1+q+DPBYBDt7e0NaE3lkLaURkttAbTVHtKW0mipLcD87fnCF76wgK3RDqTPrh9aagugrfaQtpRGS20BtNWectoyW79NrA4EAoFAIBAIhCUBEb4EAoFAIBAIhCXBglodCARCZcTjcUQiEeRyuZr3xfM84vF4HVpVO1pqC1DYHrPZDL/fD4/H0+RWEQgEAqHeEOFLIGiYUCiE5cuXw2q1gqJqW1uVy+VgNpvr1LLa0FJbgHx7BEEATdMYHR0lwpdAIBAWIUT4EggaRhAE2Gy2ZjdjyUBRFGw2GwSBJDQgEAiExQjx+BIIhHkZHBzE4OBgw78nGo3iySefrPrzC9VOAoFAIOgTInwJhEXC+Pg4tm7diomJiar3MTg4iP379+PQoUPYv38/hoaGAABerxcHDx6sV1NnxefzlfyeQ4cOobOzU2lPNBrFtddeq7yW6enpwf79+xveTgKBQCDoE2J1IBAWCffeey9eeukl3HPPPXjooYcq/nw0GsX999+Pxx57THnv5ptvxmOPPYZAIFDPps6J3++f8d6WLVuwefPmgvf27t2L3t7egvd8Pl9D20YgEAgEfUOEL4Ggc3w+H7LZrPJ637592LdvH2w2G6LRaNn7OXjw4Axx6ff7cejQIWzatAmDg4M4dOgQXn/9dezcuRNHjhwBALz++uu44YYbcPjwYQQCAfT09GB4eBgHDx5ET08P1q9fj5///Od47LHH8KlPfQq7du0Cy7J44YUXlO17enpw4MABbNy4Ea+99lrJ9u3cuRMHDhzA3r17ceTIEWzZsgVDQ0M4fPgwAGDz5s2KED506BAOHz6M3bt34+abb8YzzzyjbCt/ZzgcVtr/t3/7t+X/wQkEAoGgW4jVgUDQOUePHsWOHTtgt9sBAHa7HTfddBOOHTtW8b5isdis/9fX14ctW7Zg48aNOHDgAF5//XUcPnwYmzdvxuc+9zls2rRJEb2bN2+G3+/H3r178fGPf1zZx4033oje3l58/vOfL9j+zjvvxA033IAtW7agp6en5Pdv2bJFEbkyvb29CAQCCAQCeOqppwq2jUaj8Pl8yv6K26huP4FAIBCWBkT4Egg6p6urCx6PBzRNw2azgaZpeDwedHZ2VrSfzZs3K1FcmeHhYWzZsqXgPdn2cMMNN2Dnzp24//77wTAMvF4v+vr6FCGpth1s3rwZDzzwADZt2qS8V7x9OWzfvh133nmnsp8HHngAXq8XGzduBICSEe5IJFLyO9XtryQyTiAQCAT9QoQvgbAICAaDuOWWW/DCCy/glltuweTkZMX76O3txe7du5XFbQ888AC+8Y1vKP8fjUYVC8HOnTvx1FNPKdHd++67DwcOHMChQ4cQDodx5MgRDA4OKoJy+/btGBoaUsTwl7/85YLtd+3ahaeeegqHDh3C8PDwrJkZdu7cqURyARREb4eGhvDMM89geHhY2Ube36FDh3D33XcXfKe6/cQbTCAQCEsDaqHyVd5xxx3Cl770pYo/FwwG0d7ePu92R8cTsJgMWNPmrKZ5dW3LQkDaMjtaak+tbTlx4gTWrl1bl7ZoqWiEltoCzGxP8d/9C1/4Ar7yla/UVkFEZ1TbZ49PTKKrs6MBLaqcxdQX1BsttYe0pTRaagugrfaU05bZ+u1FEfEdPBvFc8eCeOatCZwIJpvdHEIFnJpO45GXTuPIGTLVTCAsBiIZttlNIBAIhFnRvfDNMBz+Zzjv4XvxZBgcT6ou6YE0w+EXb08iSbN46eQ0RqOZZjeJUAEcL4Bh+WY3g6AxptO5ZjeBUCbBBI3njgYRSTPNbgqBUBKWq/8zpmbh29/ff6v076v1aFClvD0eR071h0lkcxieSjWjKYQKGTwbBaM6d6+eisyxNUFLcLyASJpBJM0gRZMIHyHPdIpcD3qA5QQ88+YEjk4kcOhYqNnNWRCmkjReOD6FNMM1uyk1k8iyiGcW/yBzOlX/Y6xJ+Pb3928F8NzAwMA+AL3S6wXlnfHEjPeOThC7g9ZhOR5vjxWeu7ORDKIkWqQLUjSrzKykGI7MshAUIunFL3wbEYVaaN6dyiAhDVrHY9lFP+PGcjyefnMSb4zE8OzRYLObUxPRdA4/eHUEPxwYRWyRi99ggq77PmuN+PYCkMXukPR6wZiM0yVP+tlImkzBapzT4Qxoduao+0SIDFq0DscLyKruL0EQkFkEERRCfVjsHt/fjsTw8AundL8u4d1QodA9tsgDRidCKcSzol44G05jLJqd5xPa5eXhMGiWA81yeGU43OzmNJSppMaE78DAwD4p2gsAlwAYqL1J5TM8XdrSwPECzkYW9+hV7wzNYkcZnkovcEsIlZLJcSjOBpNhZ75HWJowHI9EdnGK3zTD4TcnwxAg4JWhsG6nmiNpBuGi2bWhqdSivoffLpodfmc83qSW1EaSZnEylH9+ngimFoV1oxRphkOqAcdWl5LF/f39lwB4bWBgoHStUYlgsPLphbkSy791ahqpWfwfbw2Pwy14Kv6+atuy0Oi5LYIg4O3ToYKoocxQKo0zo0bYzNWPyfT8tymG53nkcvV5uHJcfTqQNM2CL7I28DyQoXMwG8vL+FWvttSL4vbwPF9Vf0UQmU4xcNvq8njRFG+PxcHyYr/FCwJ+OxrHFWtbmtyqyjkTnhkYyuY4BBMMOjzWJrSosaRoFuNFEd6hqTR4XoDBoK8shceDyYIBCi8IOBlK4cLl9dU7WqAR0V6gTsIXwNaBgYHPzrdRtfnfSn2OznHIIgmn01LyM0lYGpJvTis57AD9tmUyTsNoTcI5S//KmF1Y2e5asPY0mlraEo/H65rvdq59DQ4O4rbbbsM3v/lN9PX1AQCGhobwsY99THmP5XgI4Eo+LHgYYDaX36VoKY8vUNgeg8GgqWtIb4RTDFa3OJrdjLrz7mShHeB3k0m8b00AFKUv8VRK+ALiOovFKHzPhDMQUDhYp1kOY7Esuv32JrWqOoZKzIqemk4vUuHbmGwjdcnqMDAwcJ/0+4ItbhuNZWdcyGoiqRyyOW1FlQgi8y2iGI3p13vVSGw2a03/3G4XbLbZH2p9fX1K5TaZWCyGnp4eRQgzcyzqoRfBgh9CfQgvwvRY0XRuxnGlGRYT8cZEpRqFIAgYn6WPne19vTOb9VFvlkiG5Uueo9FoZsYs3GIgnGpMP1JTxFcSul/t7+//LIAAgI/WpVVlMJ8xXYCAiTi9KKMOemd0nnM3sUg7Xz3g9Xpn/b+hoSE8/fNfwun1YeWqVfB6fHjxhV8BAK74wFU4c/oUDnzrIezatQuHDx/G3r17F6jVBK0RaUAKomYzm0g6NZ1Gl9e2wK2pnlCSKbmwGFi8wndsluMa0ZnwHY1mSvqwcxyPyQStq+uwHKZTDJzW+lumatrjwMDAcwD8dWpLRZRzgwYTRPhqkYn43OduKsmA5XiYjLqvr1JXstnaIkvllgm+8cYbsX//fmzatEmJ9ALAnXfeiVs+9ffgIeDM6dPY9uGN8L4p3v7P/Own+OvP/B3+4/vfwZYtW/Dkk0/W1FaCvokswrSEI7PMVOlNPM3V/9Ish1gmB69dWzakWkjSLBLZ0tdjMEHr6lkzl+4Zj2UXlfAVBAGRdK4hwlcfZ7sIjhcQKsP0PKmzKailQCTNzGtB4QUBoQZ5ewjzs2XLFhw+fHjG+wIAt8eLCy/aiCs+cCUe/vqD8Hq8uPDCiwEAsVgUXp9vgVtL0CI0yy26leazzURNJuiCIkpaJzjPczHUgLypzWQuHaC3Z81skWsAurPczEciyzbsvtKl8J1OMWUlzF9sN/BioNzBCDl3C8/g4CAOHDiAoaEhbNq0CT09PRgcHFT+ff6LX8b3v/sIXvz1rxCLRLFi9WqcOX0Kb775Bk6fGsahZ3+BU8OnMDQ0hOHhYQwODjb7kAhNZDEVo0lmWSRnqVAoCIKugizBxNxCT09CsBzmC5Lp5dzxvIDQHOeuEYUemkkjZ410mW+m3BOcYlhkGA52i7HBLSKUS7nnbrF1vnqgr68Pjz32GABg165dyntHjx4FAMQyOez5/JeU7VeuXq38vu3DfwQA2P7HN6HVZcEzzzyzQK0maJVohsEy3+KYep2cp9+aiNO6yA7Acvy8Cw8btZK+Wcx3POXMHmuBcJpRUumVIpEVF/TbzItD7zRS+Ooy4lvJyGaqQasCCdVR7rlrVP4+QvWUM+3ECwIpX0wAAMQWUQW3+fqjYEIfi8KmU7l5i1RML7Jn5tQ8zxy9RErLaediGrREG1gcRpfCd2qeqRo104voQtA7giCUfWOGy+igCQsHz5cvaFkifAlo7INroZmv35rPPqAVplPzi6dklgVToriQHmFYHolZLCoykXQOrA482uXMgi4m4RtpYEpE3QlfnhcqGpEuxnySeiWayZVtVmd5flE9OPXOXFNsM7clwpcAxBaRx3c+QSFPM2ud6TLSzAkQFk1WjlJaIR4O4Zu7P4F4OARADMgUl2/WIuUE8RZTtL6R/YfuhG80k6voIdyoBMiEyqk0+h5ehLlA9UqOK1/MshVsS1i8xBbJwJVheSSyYtSwWDSp0UO0Tf08jATD+D8fexP33bIShx9vgXqCrZHRtoWk1PP/uce+hVNvH8Fzj31rzu20RjmidrGctxzHI0k3biCpO+E728mfrUMi4kk7VOq3Xiw38WKgkihu8cA0Go2SvL5LhJERYPgtNwCxyl9mEaQ0i6QZpUpoKdEko4doW0TVxsfuAeLhHQiNuPBfj3bg5afzKfkXy2ybOnJ9x4cvxe5rfoKXn/4MBOE2vPz049h9zQbsua5P8+cuRbMFMwocC7zxggev/JeAf/nHv1B0z2LRO/EMO2dl3lrRXVaH2S5QdYd046c/r7wv55N0kMwOTafSUfVimW7TE4ODgzhy5Ah6enoAQKnAVo7wffjrD+KCiy7GW799A3f90+0wUBQAwOfz4eDBg7jxxhsb2nZCefT3998q/bpmYGDgs/Xa78QEsHatFZShG//0nSPwtbYhns3pPqtOOJXDnuv6wObk/suJl58ewctPvwcmM4N7fyqm7dO6eGJYMYomHstqAEcBMAD2Afg0nvoXM/q3UrDYFo/VQX0cl/7+MbzyXyukV++D0cTioveP4EN/uVvzglH97OR54Lt7V+CdV9wAugHcgV9+/2F85DN3LRq9E1MVHIlMBbF165/ie9/7Hjo7O+uyf90J32LxVNghAS8//ThefvpxmMwWpUOKpBk4LNpPNbPYqdTqsBjLntbK158/WfVnOY7H329dN+v/R6NR3H///UpKMwB48sknIZSRqSEWiyISCeOKK6+C1+8DxwswGCnl//3+phR4JBQhlZl/bmBgYKi/v/+J/v7+rVIFzprp7ATc7hASiTb87NvP4M/u/ARiGRYdnnrsvXlEMznsefRZ/Oe/3o83XzoJlvkFgJUwWSK4Ze9xZTutL6SOpHMQIGDPo8/i4dtjCI0aADwKk2U3LNYPIp04B2+8QOPSD8YWjT87Ks0ahifM+J9fdAPgAXwXwCfBsV+G2bIbnkCb5mcX1R7kwee9kuhNS+9cj1eeeRyvPLMBJrMFHzkb1L/wlWYccgyFh+8/jNd+M4h77rkHDz30UF32rzurQ/HIbM+jz2LD5R+HwXg/gHtgsqxE39XbsOfRZ5VtFlMidb3CcnzJ9EbJqBFv/LcboVHLDLuK1jujxcbBgwexadOmgvd27dqF4yeH8P3viIUrnv7ZT/Dir3+Fj22/Hi/++le498tfAAC8+frrOHP6FF789a9wxz/8HTheQDQaxQMPPIBDhw7htddeAwAMDQ1h//79+PGPf4zBwUEcOnQI1157LQ4dOoQ777xT2ebOO+/Ek08+iSeffFL5zJNPPkmKYtROL4Ct0u9D0uua8fl8sNlsSCR+DgD47YtJ7L5mA/rPWV6P3TeVSJqBJ9AGq8MJlvk6gJUAAJbx42f7LlS8sVqPksr9qdPbhkjoSgCA0fRv4HI0Olf/GgDw+gteAEA8q/9UdDwvICYdxyvP+CHwFLxth3HZtb9Ax6owgHaMnrgQgHi8Ws7sIF9bggA8/x8tAICV678No+kfxQ2ozyq6J5rR/3MznmGx57o+/NMffQpHXvwsBOF57Nu3DzabDb46VAfVlfDleGGG94iiOnF88AHw3D8C2AOWeR5GUwc8gTZlm8XiV9IzcrRBzdiQFff/1Rp8754V+Nqta/DDr71e4J9jOH7RlT3VG729vbjrc5/DzR//JK648ir854+fxBVXXgW3x4P1a1YjFo0AAC7cuBErV63GFVdehYsu7gPLC7jzzjtxww03YMuWLYp14nOf+xw2bdqE1atXY3h4GFu2bIHP58OWLVsQjUYBAA888AB27tyJG2+8EZs3b1Y+09PTg+Hh4ab9LRYDAwMD+wYGBvZJLy8BMFCP/R49ehQ7duyA2fwqAIAyfAB9V2/DI8+8VI/dNxVZdATPtgPYCquDwSVXfwYmcxgjx+343YALgGirS82TOquZyM/Bs+/awTJeWB3j+Mw/347Lrt0Bi+3noCgBJ99wIEdToFlO9/7seJaFIAgQBOD1X4vTDh+7fQ0+8pm78IEbxLzLNtffABAzO2g577Q8aDn7rg2TZ2xw+3Po7HkVHPsIgGlA2AgutwGeQNuiCPTFsuIsS8dK2ZX1Aux2O2666SYcO3as5v3ryuoQTc/M7fqzf+0Ak/XB4TkFu9OP6fFeDP32+hmfIzSX4nQxHAt8/yvdSMdNAEbA8904PrgTwNcK7CqLYdpGL2zfvh233XZbwXuHDh0SHwqxKLxeH+KxGGKxKBw2O+hMCzIpCtnUzPMzlzXC6/VixYoVWLdOtF2UskEEAgEAUEb3Xq8Xvb29ioAm1EZ/f/8lAF4bGBh4ba7tgsFgWfszGo0wm83I5X4FABD4y2A0P4E0byp7H41AHkxViyAIGJuKguMF+NrFtSObfn8aH/yzj8O/LIVD3w/gN//pxorzJwEAJ85OoMtjaUhbauX0eBSpVBbv/K8oAs+/XICvcwX+4M//AQDw8D+mMXHKid+9TqHngiROjoyj3VX6WOpNI/42ozEaqVQK48N2RIIWuPwMWldNIZUCevuyoAxdGH7bjvBUBlY7j5MjE+ACtqafJzVyW84GI5icnMR37z4L4Fac+54w4uFx9P/BNqRjYbzzSgtGh85DKpXCmQkOa92NGbQs1N9mNBiB0epAMr4RAGAy/Tey2SxMJhMMBgOCwWBNbdGV8C2e+g6NWPD6r7wwmgT83UM55OgQHvgbD6JTH0I8fByegDiCIxHf5lN87l477EXwrBWty2nccvcQvrX7NKJT7wNwO8zW23HBe7fgQ3+5G7FMbtGUPdU6Pp8Pu3fvxv79+9HT04NYLIaNGzfizi9+GU//5Mfw+v1472W/h4H/fh6nT01idDSJkbNn8Zvn30Yi8Rbe+u0biMWi+O0bgxgcHMSuXbvw1FNPYePGjRgeHsbg4CDuvvtuHDhwAFdeeSXWrl2L4eFhDA8PY2hoSNlm165duP/++5Uor/yZzZs3o6enpy5TXQRsLWdhW3t7e9k7TCQSuPXW9+HR7zJgsquQCJtAWRwV7aMR1PL9ySwLmz0JjgN+96o4QLvsD9JwOp24/JoMDn0fOPG6FxaTC2arAIPdg/b22U3NTf1bnGHgdBpx+m3xONb1icchs/biLCZOOTF2vAUXvIeH2elDe7trwZpX77/NRC4Gp5PB+AlxEL1+Uxput3i8Tiewcn0Gp486MHGyDee/JymdO19D2lILvkArKHMCL/34O4hN3Q4AuOi9NM699F8AAEf/14p3XgEcrh1wOocBi7Wh7W/030YQBAjmJKy8gHRMdGI9++zn8fjjnZiYmCj4/mrbojPhWyhgB57zQRAoXHJ1BIEO8f/Of08Cb7/swcBzXmz+42kAUmoMQQBFUTP2SVgY1OcuHg7hJ9/OAViOzTumEOhsxcpz/w3RF98H4M+Roz8Lm8MlTtuQQUsBn7l6TdWfzeXm/1v29fWhr6+v4L3pJIObP/5JAAB/7TZMjdJ48MGbAdB48KF/B4RWGE0X4/qbPgpQAp5+7tcwUBTa3Fbs2rULALBlyxZlf3v37kUul4PZbAYAPPPMMwU/5W3UFL8mVE9/f/+tAwMD90m/121x2+OPPw4A+MWLWZx+x4IrrrsPiWx6nk9pG3l1+dgJG1JxEwKdDDpXi9XPvC0sutdlMHLcjpNvOnBuf0rT/VUsw4LJUjh91A6KErB6QxxAPqiw5qI0XvxJC07+1gFALMqhZ+KSdWH4LfF4ei8ovBbPuSSF00cdePc1J85/T1KzeafbWvygaRrAMgA/BJDCgc+fA5NZwL0/HUTvhSkYDAJGT9jAZCnETdq1bJRDmuHA8QImTtkgCBYsW5XDe9+7Ae9974N1+w5deXzV4knt29m0Naa8f+kHxfD3W7/Jj7pZnkdK534lvaO2m/xs349Bpy+GwZjBxe+PAwB4/g04vUMAXFjf/2UkIuKgRaud0VJBEARwKnuRwWhCLidGRSjqLCCchsHIgGMNSCfy3QlfRiYIwsIjZXX4an9//8n+/v5II76jY1UGADB+ygqW17dPX+5/hiTxtPbiFNTxk3MuSQIA3j0iRka1aqvLMBxolsPZd+3gWAOW9WbhKJoO770wBQA4c8wONqf/BW6ixzd/7no2FAtf8dwdHxTPnVafNT9/8Qj6rtoGo+kPAACU4dfou3qrsoDfahfQ1ZsFz1M48zu77v3ZcrGYM78TM3GtPX/+MtuVoquIr7pTGXnXhvCEBW5/ruCCXrcxBZOFx9l37UhEjHD7xQsgnmHhsurqcBcNgiAgmlbnwvwiAIDnfog7b/gLJfXcoR+68PPvAC7fX+KmXdcCyI/aCc2B44UCX30maYTAm2AwMmhb3oJUzACGDoPhOpGKm+DwcAWfNRrILIuWkKK7Dc0t17FS7I/Hh60AxAeZXn368oKn4bdLi6f1/SkcfrwNvzviAjCppM/SGrKoGzkuRnhXrM/M2Mbh5tHSxWB63ILgiBXxdm0KwXKJZ3IIjVqQipng8rFoXV54black4HJzCN41opM0oCYVZvHa/a0wOpwgmPFjDsC/6IyIyqz+rwMRk/YcfodB9ZenEY8y+o2f7Y84Bp5V7xW15zPADDX9Tt0FfFVTyO9/YpYHeiiKxIwqM6vxSZg7UXiyPXYQN6fFNf5tI2eSdIcWJ7HnkefRd9V20BRoqg1mn5SkHrugveK0d93XnGDk/RTjJy3plJcuCIdF282T4CC2WqDr70Lbd0+UBRAZwzgubzQJRHfpYkS8R0WH1xJDWc6mI9ElgXPq6bLLywUvqvOTcNiE8XT1/+/v8PI2Dh4DV73clqvkRNiFK17bbbkdl294vvfu+c7GBkdX5jGNYh4lsWZY+Lxrj4/jWKno9EELJOOd+S4HSma02RKs1iGRTIahtP7hwCA89/jUGZEZVYYj+lqAAAgAElEQVSfL16Xp94Rj1fPNpWE1F+cPd64iK9uhG9amqqROfGGaFJf35+cse25l4rvHXs1L3y1Oo2xFJCjIJ5AG4zmdgjCJQBocOxzBSPXjpUMWroYZJJGjJ0UH5rZHAeG1V5ntFBQFDUjk8lCohavgkAhmxaFr3qalKIEWB3i62wq36VwTWx3LZD1ALXRtiIDihIQGrGCzeWnLvVIPJtD6KwF6YQJ3tYc/B2FzxGjCVi+VhT6Z45Z8Oz3v6k8uLVEXHr+jUoR3+XrSgvfZT3i+6ERD5585OtN7XtqgWF50CynDL6WrSl9vN3nyMLXBgGCJu0dsUwON9/+EDLJXlCUgD+5fTs+cVeh33WVJHxPH3NAEKDJa7BcElkWOYbC5BkrKIOA1efUX7vpRviqhSudoXD2XTsMBmHG1BMArOsTI76n3nYoycX13PnqnajKrjB5ZhUAI7rXxXH5tg/PGLn2XCCPXB3Ke0s5Wm+1WhGNRpv2AFILXzptgCAAFhsPg7GwPTanODjJqFKb6THiKwhi4Q2r1drspugWq51HoDMHjqUQGrHqPuI7Kg3CV5yTmRE13HNdH4bfelh61Y+Xn34cHX635jKPxLI5ZFMGhEatMJp4dK6aKQT3XNeHZ793i/TqIrz0nz+E3W7X3LGUg/y8nzgl3sddPbMI33XioGVEii5qMUAWz+QwetIGnqPQsYqGzTEzEORrY2F3ccgkjYiHTZoU8OWSyLIIjVog8BRauhhYbfV/jujG9Kr29w6/7QDPUVixPqM8cNW0Lmdgd3GIh82ITZnga2N1fSHoHbVFZfmaz+Ds74ALLhew5U/umrHt6vPTGPilD6feseP9UjrmWIZFq2tpCpHOTjGFSzgcrln88jwPg6Gysa68whYA0gkj6IwBNpYHe6Zw8QTPU4jFTIjHAVrKHmE0ULN6O6tpSyOR20NRFKxWa91qwi9V2lfQmB63IDRi0a3w5XgBKTofNZRtAGr2PPos/n3vKzj1DgBcCrPVhi1/uA0PP/jAwjZ2HuKZvIDv6qFhMgN0kR15z6PP4kcPfQfv/A8AXASz1YZtH/ow/vmB+xe8vbUiRzxln3nX6tLT5SukiO9ZyU8az7JwLkzq4rLgeAFJmsPIcXGx/opzZnqzAYCigK7VWQy95cTEKSsS6/V5zwGi8A2eFs9bx8r62xwAPQlfSTzFwyEcfPBVAJ9QvLzFGAyief/dIy4cG8jhtcOfxF9/6UGgb9kCtpggox5Fnz4qeeVmOXd5r5IYraeopR2tN5lM6O7ursu+gsFgxXkP9794CpmcKHLv+8s1CI1a8en/fxirzpvZAd/7F2sRnrDgH755El09NJwWE/7ifavq1pZGorX26J22bgZH/xeYGrMgkS19r2udJM1CgJCPGpYQT55AG3ztI8A7APB7yNE0zDan5gZO8SyLidOi9W+26Kcn0AZ3IAUgBqATOdoLq92luWMph0SWRTJqRCJihtXOzbCoyLR30zBbeUSCFqQTBsSzLLo0JHyTDFdwDS7rnV0Idqym88JXp/ccIN53k2dEod8o4audkMs8yOLpuce+hdiUWCtdFkmlWCmtWn3pZ8M49fYR/Pjfvq7JRQdLgZgUraczFCZOW2EwCrMurmjrZuBws4hPmxEJiis5l7LVoZkwLK+I3mTUiNCoFWYrr0wPFrPyXNnrKE4bphltLhYhNJ7WZWI4MTRq1XQZ37mQB9xKxHcWwZijj8FkTgBoxyWb/wbB4ORCNbEseF6QxIQURVs1u5hIxcJw+cRKexsu/2tMTGrrWMolSbPKeQPeRjIaKrmdwSiKXwAInrUqXmitkKTF/ndCioB2ri59DQJAp3ReJ07bdDvLInuzlWt1qQvfj15+DnZfswEvP/0fAMS0Ho986few57q+ktsffnwnAGB82AdBEPDy0z+Ew6FPv5KeEQRBycwwctwOgafQ1ZOF2Vp6EGIw5BccjJ7Q/6pwPaPOm31aErMrzsnAOMs80SpJ+MrbChB0vciCUD1ty8UH1tSoRYxa6XCRVJJmkYobEZs2w2wVfcul+OTnH8Sq88RHad9Vn8XffPlfFrKZ85KgxQJOwbOSmFhR6HFQL+T8xF0P4pxLxMXG57/nVtz1f/914RpaRxJZVhGLdOYVPPfYt2bdVh4ITJ62as4SmaA5CELeq9w5x6ClSxLFE6esyOY45HQYdJCf9Xnh25j0gLoRvp//7i/Rd9U2mMwXAvAAGEHf1f1KKqxi/v4bd0i/XQwAMFtt+KMbP4pjx44tSHsJIkk67xGVE1KvLJFDUo28slgesS9lq0MzKbCoSIsN55xlOVdaWXw0vzCRnLuliZwzNTRqgSAIuixikciyKptDFnNZ0juUaJv2xJPcnlJRtI0rfPir96/Gcp9deU+O1k+N6tef/ZHL1uGn3/6R9OoYXn76cey+ZkPJQJn895g8Y9Vcf5WkOUSDZtAZI1w+Fi7f7PeR+hrkOX32vSmaBZsTLVIUJaCtewlHfLM5DjavmMSZzV0kvfvqjCTOarpW+2C2RAG4YTSfA5ahYXFoz3u12FEvbJOFb6nk6Wrk3IpjQ2JHrbUHyVJBLXxPKcJ39nO3rDebTwgvpTUj525p4mlhYbLwSMVMyKQMypStnkjQbD5KOs+Ua6dKdNAsBzqnneNNZHNIJwxIRkyw2Hh428T72m014b29AZiNBnzw/HYYpMhvmzxoGdOv8N372CG4/ZdLr47DbLWh7+pteOS/XsZfvm81VgTyg3Ml4ntGOncaSp+ZoLlZbQ7ndblxXpdbee1w83D7c2AZA2LTJl1ajJI0h8ikBTxHwdeeg6UBGR0AnQhfuXpOMhpG5+qbAQAr1tMzUmEVY7EPAQC27XwEl127A5M69SvpGbVnakxJCzS7TwkAuiQDvxzx1eu0jd6RhS/PASOS7UT28ZbCZAZal4uLKk7+VtxOj1EHQu0YDIWRwxSjv+sgSbOYGhNXOrV256dcTQYDLljmgcWYf3yqp8sBbQ34Etm8v7d9Ba1Ers/vsCuVFV1WE9a1i4vf5Gi9GPHVjoCvBJM7ACa7HABgNJ8Gy9Bwuz24/vLzYLcYsXV9myL0lYivdO60dMxJhitpc+hpdWLrue3Yem47VrXkRXzrMrHPnh7T57lL0iymx8V7rqWrcVUQdSJ8xZP5ibsehMP1AQDAB//08hlJnIvpu0pcUZ7LrsGNn74Le762r7ENJcxAfgBkUwaEJywwmvh5py/aumkYTTzCExalIAIRUAuPXO0pNGpBjjbA187A6Zm7M+XY1wAALzwp/iTnbemiCCidRg6TWQ5To5LwVT2Et5zbhqvXt+EPNnQo78l5cSfPWMHz2hO+QVn4SiKPoij0BGwF263vkITvsvx5S2RY3fmz0wwHmhZAZ1oB8Ljt/i/jsmt3AJkozNJgxWUz4RzpeAMdOZgsPGLTZmRSBqQ0ZMtJ0TxCI+I12K7yZr9ntb/k7y3LdH7P0SymxsRF7S1djVtoqAvhK6/qFwRg4szcefnUdPVIkcNT8iIp7VzQSwV50DKuGrUa5ykhbjTmR7dyHkY9TtvoHTkbx4k3xJ/tK+Kzbrvnuj7svmYDQiM/BQAMv0Vj9zUb8JHL1jW+oQRNEpBSSEWCZqR02PcWRHwlEd/msiqCaXWLA91+0b7lcPPwBHLI0QaEJ8yaGvAlVMch2xi6vLYZOba7/XZYjAbYnDxcfhYsY0B0yohMTl+zbSmaRXjCAsAIfweLlevPwY2fvgs/euKJgu3O7RRtAgZj/u8yNWpBgtbG8bIcj0yOx5QUAZUHJJ0eG9rc+bz2HR4bWpzia3mANjWuT+GbYjgS8ZWRrQ6JiBHpuAk2JwdPy/wntVO1yhEg0admoAhfybbQ2VOeWb1jdT7FDKDvEox6JMfxyoKk//35cQBAOv78rNvvefRZ9F21DUbz7wAAFHUx+q7ehrt/cLjxjSVoCpdVFFT+dvHBFZk0687qkON4ZGgO0+Ni9EkWHRuWeQq2u0D1Wo7ITY1aNZWCMZFVTR93im1crZoelzEaKCyXhLwioMYsugs6JGk2H6lflh+wuG2F6WiWe22wmsRrVR0p1UrEN0mLOXynlcGX+Exc2+6cse2aNvF8ysehW6tDllgdFBJSJzJ5WhRPHavoGaUjSyF7d0IjVnAcSYvVDOJKLsy5S0cW0y6NwIMjZNDSDOIZFndctxG7r9mAsSHxYThy/Huzroz2BNpgdTjB5USLgyBsgNXugsnp191UKaE2eqUp9HzE16K7iG+SZhENmcGxBnhaxEU2FEVhTVuh6OhpcSjT57IwmRoza+ZZIwiC5JssnD7uVmVxUCO/7++QBi1Bs2aEYLmkGA6hokj9ysDM4zUYKKxqKRT6zzzynxid0MZaoEQ2BzpjQDxshsnMw9sqXlOlBi09reJ1qbap6G3AApCIbwFyxFc26M+Vy06N1S7A2yLWjI8GzaBZskhqIWFYHllpdbNsN+kqM+Ir+4Blf5MeR696JpbNYc+jz2LjldsAbAQAmCxH0Xf1tllTCCajYVx27ZWwOWkAPoQnDeAFQXcPTkJtdHvFflqulhWZ1J/VIUVzeZuD9ADucFtn2ANMRgNWShkCZFE5PW5BPKMN0SEWkREKxITFaECbq3R5sk6PdO7a1TYVbRxLueStDnnxJFtSipHTuMkCORq04+Ajs+f8XUgSNIvIpHg+Ap05GAyA22aG3zHz3LW5LLCajMrxTo/rL+LL8wJSWQ7hicJBmsdW/wLDmhe+vJBPgl9OEudi1PkkARL1XUjU3uxJVT7McmjrJuetmcQzLDyBNhhMywC0AYiCZU7MmULwE3c9iI985i4s6xHP1ZU33guA+LOXGm0uM8xGgyKewpM6FE8MN8PfO5t4kqOJBYvCNHK8SZpFKmYEnTGCMiTBshPo8FhhMJSeMm1zW2E0UPlo/aR2pv7LRUyJJYqnQEcOFCh0emwlt+322bHnuj4cfPBm6Z11+O+nn4DNZmt6sSvRoiK2W762un2lj4OiKCzz2WB38XB6WORoAyYnKF1Vq00zHGLTRrA5A5xeFjaHGKRc3+Ge55OVo3nhm2J4Zap0oooydurULACJHC4kam92Nm2E3cXNmYBbTesyBhQlIDxuAZsjwnehkavthSfETqerh8Pl23bMm0IQyKd+CpF7bkliNFDo8Fhhc/JwuMVFUlMhSilkowdSNItoMC+egNlFh2wPaFX5K7WSgjFJ56eOBf5dHPrBt9AxiwgExHPX4rTAp+OIb5rhlHL3/vYcAk4LLKbSUsfnMOOL33sOGy5bJb2zFmaLFTt23NT0YlcJmkV4Qlq0plqUOBtytF6uMBgOmnRVOCbFsMqARfaiOy0mdHisc32sKjQvfBPSQ1MdNSxO5EyBwuW9LbhibUtB+UVAnUuSZAdYaGRfbkjy6bZ1z/Rmt7msuP7iLmWltIzZKsDXngPPUwhP6M8jqHfkqdq+q/4RANC9zoAbP33XvCkEgcIV0gDxZy9F5AibbHfQ30OYQyQkPoR97WLUcDbB6HOY4bSYEJAe1uFJCziNVM7qW7sM3/iHz0mvTuLlpx/H1ecvmzOa2ea2FthU9HTegMJBi789h3ZPaVuHzPqelXD5aQBpAO3IMTbYnc0vdpXMsghPiNecbGGYS/h2SJkefFKBkqjOFpUmaQ5R1T0HACtKeLPrgeaFrxwtik2ZkE0b4fDMLNu3aZUP/at86Fvhw2U9/oL/U7yiYyT6tNDIUcNgiTyEAGA2GvChCzuxIuDA75/bjoCzsIOS7Q7BEQtolgOjoYo6ix3ZpqLk/1wxc6GBgaLgtMz0XymLfIhNZcnSUewVnbQgraOHcIpmMT0m/m6xheaMGgJAp9cKiy2/piQW0kZKs+/+/GV0rb5GejUEs9WG7R/dMWc0s9VlgV8WTyEzEml9PTNDYQHZtBFmKw+Hh0O7a+6IYYfHilRsGnb3FADgvMtuxdh48xe4JWkO4XEp4rtM9Gb7HeZZt293F3nrQ2Zd6Z0Uw+aFb5t47yybQ+jXgm6Er7KwbWVh1NBmNmLTyvzodWO3Fw7Vw1hdPQggEd+FRIn4ns1HfNVc3O2FSzKuGwwUfm910aBFElDTY0RALTTyuZs8K/7ti+1FFqMBO/qX45OXr1QqPsm0LZOtDtIsi44ED6E+tLlKLJLSUeQwzXCYPCO2/c3fHEC7e+6ooRwNbtGYz9fqaUEu1w0AMBjPgGVoBPzeOaOZLU4LzFYBLj8LnqMwPrFQra0dnhcwPiLKmkAHA4oCWucRvm0uCz5x14PoOV98/lx4xa24/+FHG93UeUmoIr6tyxm0ua0zZrTVWM1GuG1mJeIr3nPNvwbLJc1wiE5JwrdVPIa5Ity1oH3hK3WWcj7X9qIH8IYud8FI3GQ04LzO/IO4pTMHyiAgEjSDZShdXQh6R54uVyrPqMp+UhSFC4tyYq5pdRYMWhSv0qQ0aNHRg1PPpJm8P1G571YU3nfv6Qmg1SUukrn6nFYlHyYgrsalKAHhSbPkzybnbanhtplgNRnhlR5gsSmTbuxKPp8PN1++DkzWD4DH4PPfxof6Vs5pD5CjbQUL3DQQ8U1mWaTjLQCAG267CVtv/BgmJ+eOZrZIM2/yoGXkrEE3KQnTOQ7Tk+IzRG5/6ywZLGSUQVqHnNmh+ecum+OQTgtIhC0wmgT4WnMFRStmo81lUY47GtSXTSWlsjp423LzRrhrQfPCV/b4KkmclxVOuZZa8af2i5os4kUj8BSiUyYks/q5EPROQrE6zIz4dvtsSrRXxmCgsE6VnDs/TSpe/CRavzDEpaIjmZQB8WkzTBZeOReAOMuyYVn+vrOajbhA9dpkEeDvEO+58KQFSQ0IAMLC0+qywCsVGopPm3VjdTh69CguvPzPABgBjMNsNeKPbvzonPaAdlfh82l6XBt5VJM0C5P5XADAOZs6sfuLX8Hjjz8+52dsZiOcFlO+AEnQhKxOqrelGQ7RoHgufO05uK2mOS0qgFi+2GY25gVjyNr0c6cuOhLoYGAwAq3OuQU8ALS48gsTT7wxgbOj4w1tZz0Rz500aGnLoXWeCHctaF74ytGi4oTUAOB3WNBSYjTX6rLCa8+PFPJpdSwk4rtAZHMcGI5HjqEQmTTDYBAKElKvaXOV/JyciBtAfrGIlNePRA4XBrnoiOLv7aZhUKUvXd/hUpL2y5zfVRi9VyJfI+SeW6oEnBZ4pKT7sWn9LG5raesAJ6wQX1BnwTI0WuaxB1jNRnhs5gKrQ7OtWTwvIBLnEQ+blahh8TqK2fA7zcqgJTatn8hhimbzGR06cvCXebwtTovijY2Gmn/uknQ+w4FfmvkspXWKCTjyEd8c3Y5//9b/bVwj60yKZhWrg7ctN2+kvhY0LXw5XkBaGmkWlyAEgJ7WmRVMZOSk4kBRInWG0820jZ6RxdPUqAWCQCHQxcCkmrXoKVF9BhDN7Baj7NHKD1gEgXhFFwpF+Er+3vaVxbMsMwctPoe5wEuXX+FuBscLyOjkwUmoHwGHGd4WyeqgI+GbZjjEp0Vv4Tl93Xj/h/8E01OheT/X4rIUrClp9nR5muEQCYkRNG9rDgZj3sYwH36HBR4lWm/STbQ+zXCIhvJRw3KFfovTojxvYiGrBoRvYUo2iqIQKGPa/9JzluOLO84FkATgxbM/+pkmchKXQzTOIx03wWgS4PJxZUW4q0XTwjdJsxAEAWxONGpTlICWzvyU64pZEooX/58ifINmCKSS1IKQT2UmXrxtqkh9wGGZYXOQMRoodEn5Mu0uHnYXhxxtQCpmbPr001JBKRFewt/rspoUP2MxvaqBqLpcLUAWJi5F/E61eDLrxmaWZjhceMVOAEBXjw23/dPd89oDgELxFAmakWjy8SZpFvGpvPAFUJZ4AgCfPT9oiYdNunlmikUQxGP0tLBle0QDTpU3NtT8qmdJdeS6PQevzQSTcX659vbb76Dv6m0AdRYAYDKfg5tuan5O4vkQBAHj4+LxeVvFKnXlDtKqQdPCV44QRCYtEHgKvrYcTBYxWms0UHOmuljmtYGC6A8JyHXHFa+oPm5iPRMv8veqxdNsFZBklqvqyMvnbt+d92J0TEfLi3VMvCj/svrcrQw4ZvVdrfDPnGUJy/ecTh6chPrht5thtghweMTsAF+89VOYmND+PZxm8nlgfW25kiViS9HqssDmzA/WYxGqqTMdSfXUcSsLh8UEq9k4z6dE/A6zMmiJTenH6iBG6/Ni32cvT/j6HWY4PBwsNh502oRwWGhqwZVC4cuUHbnu7l4Ot9sDCGcAAGyuDW63u+k5iecjk+MRkfy9claKco+5GjQtfGVHQnHpSEBcRTvXCMhuMcLnyI+YAFFAA2TKfCFIqKwOQGHEd/ksFZBkulSJ4mUBNT6UxRP75y+eQKgd+dxNj4n3j9petGqOhOKdHqvi/Q10kIWJSx2n1QiTwaB4RYePhnDPPfc0uVXzI06X5xPp+8qMGsrRRXXVs2bOdCRpDjFF+OYqWiHvsZvy0fqwCZmcPoRvks4LX0+gsogvRUFZ0Dc9aWpqnyUK33x2jUpEYCYeRlu3GLTovfCPMD7R/JzE85FmWMWi4mvLwVXGosRa0LTwlSnl7y0nsXGntyihszSC0svoVc/I4ik4IkYHba68R65znnPX5raAoijsua4Pb730r9K7q/HCT3+gG7+SXhEEAYksC56Hsqq4pUu8fyhQBdH4YgwGSimbKacGku85EvFdelAUhds/vBHjw4eld5Zh3759mr+HU+qSt23liye/Q+y3/JoRvixistWhgml/APDYzPC25j2+epklDYZ4cKwBdhcHp5OC01raUleMw2KE1WRUrQdqrt0hmVUtbuuobNBy99cP4KIrNgAA1l58PR5+5N8b0sZ6UjDYbGPLHmxWiz6E7/jMiO984gnI51b0toq5fGPTJnAsiT4tBPJ0+cQpsfN4++X9AAC3zQzXPJ2R2WhAq9OCPY8+i+61cpaHHpitNnz0j+euOkSojTTDgeV5xKdNYHMGuPwsbA5xganfYYbdMvdUqZxw3OXjYLKIixWyaQO555Yo3/rpf6OlS75mlsNut2veczgj4lvmdLnRQMFrU6cBa27lrCTNKn5Xb2v5kWtAPJZWrxE2Jwc2Z0BwSh8LwsfGxECLJ5AryOxUDn6HWTODlkiCRzJqgsHIVxS5BkSLkUdnGTnUxSsqsahUS3nDoSajRHxV6bA6ykjmLG9jMovTHrEps+hXWqn9C0Hv3LJlA1jGALH+OYPB57+Jwee/AbPFik/GY/N+vs1tRSjQBqf3FQAARfWCZWjYnC7N+5X0jGJRGZt5z3XNY1EB8oNNcdowh9CIVUykTu65JUnPiuWwOU8BACjDKmSzNDwej6bv4akwj2xKLHnr8vBwz7IQtxR+Z2EBgSQ9s9T3QqGO+PpaWfjsznk+UYjXboInwCKbMmJstDH5VOtNcEKM5XlaWHjtlckbn8NclAGqOcKXznEITYiDRU+LmMO3EiHosZvhCYjrMsSMHPQ8n2g+KYZDNCQ+X/ztOfgc8+u7WtBHxLfI4+u2msqawmhxWpSFOAHVYhsy7dpYGJbHnkd+gXMv/aT0zimYrRb0Xb0NP3r+1bL20SaVCOW44wAAb9tluOzaHZjQgV9Jz8QVf69kc1DZi2Qbw1yoMz6oF7jpZaqUUF88NjMEQVxhft7v/Ql2/Nkn560c1mzkqKG3NQev3VRREn2fXTtRw1SRx7fSCKjbZlayQUxOaF/45jge0yFZMLIVH+/Mc9ecPitJc4rNwdfGwG42lr0oEQA8NhM8qowceoj4ZhgOYekaM1mCDY/4al74ciylpDKTxWs5pfsAsXyx3y77ZPKZHci0a2NJZFl4Am3g+dUAAIo6JUZrHS6c17uirH3IZST//Au7xH2Gnbj+trtw3zcfaUibCSLxoohvS8Esy/wRX6fVpFhZ1NlUyILSpYnXbsIffnw7ACBHt2DXF+avHNZsJqXEEx4/C2+FXkOferp8snnCVxAExFIsklETKIMAd4CrWAjKEV8ACE5qv2xxRp3RoSUHj63yc6delNssnZCk81lFvG1MFefNDG9AFLtxneTPzuQ4RCbF6+u1w9+u+JgrRfNWh9jUzFRm5QpfQEwqHk4zqgVuFqSZVEPaShCRU5nFp8VCBxe9fw0c7h1IRKaU0p7zIefws9gEOD0sUnETEhH95JPUK/KDWl7YJi8oNRsNCDjL64xaXWICeL+qAElaKhzTqBKUBG3iUUUN49P6yA4QConxIHeAhbdC8eRXT5cHLU2b6cjkeEQkm4Mo4E0wGiq799yqBW6xabFs8Xwe/2ZSnMPXY5t/oK7GYzPDV+DPbo7wTTH5VGa+Nroify8g5cLvFEBRApJRE5IZbd9zPp8P2SwAZAAwGPjlfixr2Q+bzYZoNNqQ79R8xFeuuy1HjwBUVMpOFlD5ssWkelujSUgdxpoLbwIArFjvxI2fvguf3vvNsqdsLCYD3LZ8Lk0AiE+ZSOSwwciDFmVBqRTxbXVZyhat8v0ZUEW+eEFARqrCSFg6uG0m+FrEB68eFtrQOQ7RqXw6LE+FPlGv3Qynl4PZyiOTNGIq3JxrPkWziIXU+Wwrj3F5bCZ4AvoZtKRz6ogvW5E3GwB8dhNcPg4GI490woRIvDnnTl21zdtaecQXAHxuM5w+DoJAYVzjqbOPHj2Kje/7U+nVBMxWW8MXwGpe+EaChSnJAFRUyi5QJHyjUvU28hBuHEoe2HHxhpWr7VVae1setMhRh6iOEqnrlUSWhSDM9PhWMssin+fiNILEYrT0MBootLYCJrO4YGw6qu1+N53jkIiIgskdYCueLndZTTAbDcpgPThuBN0EwajO6OBpZeGpQjy5raaCynta73sLile0sPBUKHytZiPsFiPcktgfG6t7E8siRRdGfKsRvh6bCV75OMa1PcvW1dUFAV3iC6dsqdgAACAASURBVGoSLNP4BbCaF77RkPjADUjiyWoyVnQTy9Oz8nRbTLox9FJ7XI8ownei0CdaaQlCeYpHNurHpvThV9IziSyLRMQIJivmwnS4RaHSVs0sizRLE54g+bOXMupiCFov3JZheMTD+YhvpVFDQIz6NnuRVIrhVBkdKve7AmIBEp+cy1cHRSwyDIdYWDzOjk6hrBK/xfjsZnhaJLtDyNSUyntJmivy+FZ+DbpUg5ZJjQtfQRAQnhJngnsvWI4/3P6nDV8Aq33hGyxMiF+uz1DGZzeL+RVV5RcFgTyEG0kiy4Ln8oKnVuErd76xKbIwsZFkGE5cGT02s2BMi7P8iK/XboaBouDycTCaBKQTJjBZitxzSxS3zQSXT14kpe2HcCbHIRFWR3wrFx0+h6npmR3EVGb5csXViCeKotApBeJiU80RgZUQT3NIRY2gDAKWVRks9NhN8ATEfi82ZWrKuUtkWMQlAe8JMFUNWsTMDlLp+aBR09bObI7Hpi23AgDaV9hw+5e+2vAFsDoQvrLHt7r6zRRFwWs3w+bkYbWLNdSzKQN5CDeQRJaVioWIBRAsNvGmq1T4BhSrgxzx1f50m56RvdnFkXqKotBSwYDTaKDgc5hhMKii9dMks8NSxWMzwe2XHsIhbQvfFMMqEd+WVg62CtJIyXhshRHfZlz3KZpTZjeryXAgs3wZlEVScY0vkhobBwSBgtvHwuuqbt2+OhVYbKo5qU8ngwI4lhKrzzmoqhYUFqQ0mzYiq2FrZz0Gm5WifeErWx0k4VupeALUU+b5aiYkO0Bj4HgBaYZDWBZPnXnxVGkZQjmXn3plcY7jwbDavYn1jGxRCUs5JGV7kc9urnja0O+QBi2KR5Bk5FiquKx54RuZMoLntRt9yjD5h3DXsur24bGb4FUW5DbH6lAc8a1WTARcJjjc4iKpyaB2zxsAjEtT+p4qFrbJeFRWh2bMMHK8oBTh8Lbm4LJWl0XDaTXBG8j3vVq2qaQZLm8v8hPhCzoLJMIWGAwCPFLUL1BFDefihzDxijaOJM1CgFBQZxzIW04qwS7VT5cfInJHTs5dY5CFb2RSXhAq24uqH2zmo/XknluquGziankASESMmn4IR+ICsmkjTGYenW3VPR69NjN80nUfbdJ0ubp4RWsHV3UaMqfFCJc0aNF67aAJWfgGWLjLKHBVCq/NpAjfZpy7FM0iKkXqPS0s3FUKX7fK6hALa3umtDji61rqwnd0VGyerz0Ho3T+ZRFbCbK/yVMwZU6mXRuBXABBXpWat6hUN9Xms5vhVS1uI/7sxiFbHYoHLdWcu7zwzfuzyXlbmqgjvomItqNPY+PiT3cNkSeP3VRw3Tcjl6/oExXbv7zKyDUgnTtp0BIMalouYFrOv+yvPuLrUgnfZlgdUkyhRaXaiK/ZaEBruzgzqvWIb4bJZ1Lx+KsftFSCpq/kkTNi8+QHsMVoqGo0IKcDUSK+OqlmokeS2ULxJCcEr2bAAojVdKx2ATYnBzZnQDpuJIOWBjHD6lAXe1E+m0qaLEzUDP39/Zcs1He51cI3qu1FUnLWCbefVSoQVopHXfhhyoR4ZmGve5bjMTVFgecoOD0sWrzVCwmnNb8wcSqoXX+2IAgIT4t6weVjqxaMHlthoGWhBy0pmkV8Kq9XXDUUDFkmLUyMh7V9z6VzeatDW0d12TgqRdvC96zYPLl4RbXiSRG+qgpCRPg2BiVqGCzMn1xp9RkZOVqvTkdHzl1jSGRz4DggFpIivjWcO3kxjZKRQwd5QJcK/f39WwE8sVDfZ7cYlRKqyYhR0znUgxOiuHMHqhe+RgMFv0dMB8ixhgX3xqYYrqB4RS2eSZdK+E5PaVf4ZnI8EhFRJLp8LJw1nLu2Ng6UQVzQF0su7KAlqVqU6GnNwW2tXqJ1dVCgKAHpuAkJDS9MTGY4JGMmUJSArsal7i1A48JXvNEUn2iV4sllNcFkMBSkNCMP4caQmMXqUL3wnTllThZJNYZEVlwQw/MUPC1iiXCKopRFhpUg+7PVK6QZjkeO067oWSoMDAw8B2BoIb+zo138qXWrg1yu2BOoPmoIyHYH8dqfnDCAXcDrXszokO83a/FMulUZOaJh7S5MzOY4JKOyT5SDs4ZIqddhhMfPQhAojE0srNhPMfnCI96WGq9BhxEOj3ivTQTr0ryGMBEEBJ6Cw8PBX2U2jkqpi/Bt1LSZbHUI1Ch8AakjUk1h0DkenEZvYj2TyObA80A0VFhuuNZovXqxiJYfnHolx/HI5LgZ/l6PzVT11JPPbs4nwJ+SC8eQc7cU6ZKmXRPRhZ8+LheeFxAJiULDHag+agiIMx6+NtWU+QJe92JGh/pEfO1mA9x+7S9MTDMcEpLwbW8Tyi6vXgqX1ZgftIwZFlTsp2gOEWkRodEcqkn4qr31Wi4cMyHPstRgL6qUmoVvI6fNiq0O1USeZLx2MzyqaVcBgmZvYj2TyLJIhE3gWApOr5jD12ExwWKq7lKTO22ySKqxzPD3ttc+2PQ6TEr5z0TEBI4jwnep0uo3wGLjwTIGhCPajPpnWR4xVdW2Wh7Cblt+gdvP9v0Ap84uXP3bFFOYyqyWxUIURaGtTRR+iahJs/lgMzkOSWmBVEdHbftyWQzKuYsucDaaFMMiLAnfN369D5Ya/K5qm0pQw/5s2V7kqXGwWQk1f8vAwMBz/f39DZk2O1u0uK2miK/NBLcvBYNBQCpmAsuIlaQWaoSxVEjQLCJBG4D6ROqdsk1FnZFDoxEjPTMjlVmNvnpAjHqZzKLnLhk1IRkh/my9EQxWPkcajUZnvJdLJ+H0MmCyNhw/kURw5cIIqFJtmY1ImkU0FAAAOFwpRMPVX6tsOi1NM/sxPc5j7//5Iu6+a0/V+6uE0ckEpiZE9WdzJ5FNpBGkZ0YOy/3bOF1pAEB8msLZiUlwnur7hNmo5DyVYmwyjUTUBwBwOeKo4rJV4Jk0HL40AA9CYzxOjU2gvYKS7dWybt060DQFIAOAwavP/itWrtwHq9WK48ePV7w/OknD5vIDcGFkJFvVvVxMreepFCNnxfvM7smCScUQDDLzfKL2tmhW9aXTwPSUAQYTr+SjqzXiazCK4fTYtBlx8hCuO2mGA8cLir9XXhxVy3kDxEFLYQ5mkh2g3siLEoszOlTrzQbU0fqcuFCELEzUHe3t7XX5XDdthaeFR2QSSGfcaG9vqUfzqmrLbNCRDDIJUeCsWmmr+tgBYN06L2j6TwA8AmA5fvrEP+GnT/wANputIeJBjXlKQDomBh86lpuwann7rFP/5Rzj2t4QACCdsMDp8aG93VW/xlbYltk4Nh0GkzHCaOKxpteH9vbWqvfVGaXR2in+vTJxJ+xuCu1tzqr3Vy7Hjh3Dtpv24p3/AYAxWKxWXHvNH+Kf//mfq/rbmJwM/K2iiEwlHGhv99alnbWcp2J4XkA6EQcABNqBFV3taPfbG94WzS5uO31avPB8rQwMBtQ0XQ5AyevnUaWZ0XKKDz2SmJHKrPaILwC47fnyi4mICWliUak7yrmbkAYtnbUPWjzFaQRJ/mxN0N/fv1380b99ob7TaTXCLU27hia1Oe2azYmrywGgo6u2ff3P4JtYc9Fy6VU3rDY7rr/+ehw7dqy2HZeBWLxCPI5ly/ia/K4A0CWJwKSG/dnj0nS5y8dVncNXxmUxKGtKFrJ6W0tbBwDpmqHGkWNouFwudHZWl+rAZTUpxUci00YIgvbWNGVyHBJRyVdfQxq6SlnQiG8lofY33rABaEMq/jomRiJYu2JZTaF6Jp1DKpWC05sFYEdwlMfo5BRajJmy99HokXolaLEtp8JZpFIpBEfF0bbTn0QqlQKXNpc9fVEKIZuC0Sp+Ph42Ip5I4uzYBKyzDIS0+LfRAnO1ZWQyhlQqg+kJsUuwuWNIpWjkUlEEc4mqvo+hOaRSKTh8GQBuhMZ4jIfCCDpZTf1dAG2dp0YzMDBwEMDBhfxOp0WVD3ZKm/GWNM0hJQnfZTUGtdau6obLl5ZerQBDZ2sSMZWQzLKISh7fFd2178/vNsLm5JBNGREM8cCK2vdZb+Sqcm4/W1NGB0CsVudVLaZeqIWJSZpFPCzOOKzbuAIXnPcJTE1VvyrNYjLAFxAtRcmomEbQUePfpt5kc7ySjcPlY+G06MTjWwmVhKW3bwee+tnt+MmPfoKXftyHq+++v6YQuzfHwTmcRUuHOOphUk5YXXzFU271DPPXitbaMkpH4XTmkIw4AACd3RScTid6uzvQ6rJWve8VWQtOxadBGQSk42ZYLU64fIE5/ada+9tohdnaYhhlYbMaEA9bQFEClq00w2azYPXyzqojRq28ANfJDFqlxSYv/fRX2P7H56O9fe2cbWkWWmvPYsJhMcIdoAEAUSn6VGskst6Ewjw4loLVzsHvqe3RaDIawDEnxN/NPbjyQx/D1NRUPZo5L6FpASxjgNXOoS1Qu9BxWkxw+1hkU0ZMaDRaLy/eqiWHr4zVZEBLh7ry3sJEfFM0i4s/sBMjx4HOVXZ88d6vwU+l5//gHLS2isJXXJjIaU74pnMckjFphjjA1zSrXwn1yOpQ92kzn88Hl8uGn/zofgDv4uWn/x97bxomyVVeCZ/YMiP3paqyKqtXtdTd2pBo0SCxWCwWgyUhgcXSjW3Q2J/9fX5ssMcPlkE28swAg20GI4uZAVsMtgAh0wY+M4BYZLELS0KFNpBKa6vXqso9cl9imx83bmbk0l1LxlZUnj9SdVVlROXNiDj3fc97zhG88cBOxOPxDb+mXyC+oubozEnb1VoMSR0MnWhsTI1vROSJPtuoGNUkHs2ON6eLNyuqTRnlvABdYxBNKuB9OmIBYSxywrIMwn7eFBXO4q5/uM2qU55gE4FIHUjlrFLiPOkOQEldKKYiZEHL9aaP3QoxqEKReVz7H/8Lbr/99rFfczV0FA35LDn32PTGo3vNCPq4bss8kxn75WxBPteTOoxb8QWA7fOGk0WRR8Wh8Id6pydRiU5ZE91LHS5qHrWiI/7LZL1mZpw77tjEd2Fh4csLCwsJo31mCRYXF3Ho0CGIASJyFvwirnnzW8fWR0VEHpGk4Stammh8rUatrUDX0TfcFvLxEMaMIKRDUpNNiz3QNB21jtodbOu5qIx/433PG16Ef/n47xhfzeG7//oFiKKIvXv3jv3aE2weCByLpFF9qnk0xCKbI2THqpZr2N8LsVhacqZSWmsr3eTF2LRsiWuRedPyqb/6H1jxoClswZDPROLjSx0AIB4h0hxNY7C07Iw2ttZWUSn01s6KTQtV1tQ8GhXelHvyonFt6NYDT4qt0uk0otEo2q02eMEHpdNGMh4dWx9lTqEh5Ml7VYfNjGpLQb3MQW6TuM5ASEM0YM0DBDAR3+JkwM1KkA2LjqXnSSs6kiCa3nEr9QDwmW/ch/0H9xlfzUHwizh8+DB+8pOfjP3aE2wueN0Ptls1jFlTNQybvHyLWWf+5npbQcWIvI1Py5YMCwV9vSGppeNVfOQjHxn7Na1GqUAT91T4BQvWzt8LvFpecoYmkdS2nm+9FZ/BeTqYWOZRb3vvmqs1VdQr5O+cm3FORuNJ4guQQbjfvPG38bt/9Y+44ppDkArj66Mi/n7i68Wqw2ZGpaUMOTpYQZ6CPg4cy3Sr9dVJtd5SUCuzh7//CwBAMUNIqRVrt3PHNoRibeOrOSidNkLhyERPuwVBiW+97M22az5vnU4UIM8b85CUE5v1WlvtDrZFp61JwppPTeHfv36r8VUKt99+O0RRHEt6aCXasopihtzDgoGyJa8Z9nNdG9VcxpnIaRI1TdYrPaeDZccngvEwGUzUVAa5vPeIby5vxBVHFMTCzumPPUt8jxw5gg/+9d9ibvc+HP7j/4wvf+lfxn7NsDhMfL1o8bEZIasaWrI65OE7TlwmBcMwffGLleLED9ZKXLBrDjddfRFOP1cHAJx+7m7cdPVFeOUF449vR/w82o3jAKMCmMHLfu03sOzBVukE9oO2Mr1KfItG1TAct2YIiEgdTENSDnQYSWpbzz/binb54uIidu2n3dZZBAIBHD582BFrtrWgKWs49SwpjD310FcseU2zTKVSdMbZodpUulKHbdtW+eE1Iujr2QgueVCfTeVFoZjqmKMD4GHia0bUgsoTQB7C1FKnXuagKjqaHmy5bUZ0B9uyNACB2I9ZUTUEiEwlmpxU6+3AkX97EAdecy0YZg8AgBOWcOC11+Jnjz859mtHRB7/8S9vRTRBrrOr3vGX+NQ/fn7s151g82EmyYHjdbSbHMo1712/5SJ5HMYSGjgLqm0RkUd8xpQ46cA9i1QNjXvwjArRgrZ/Op1GxOjaMMwc2u02otHxpYdWIB6PYy4ZQdVYu0d+8gVLqtERke9WfCsFwRH/4kwW0FRS/UzGrKl+hkxevl4cTDS7cTjpOLEpiG/MAp0oQD7MHA+Eogp0jUG9wk1a5hZhkPjGuxVfa4hv2D+oz56sm1XwRafgD4ag6zsBAKr8LALBCM7dOT/2aw/ps0vO+WJO4C2E/BxCUeMhPH56qqVom4z0qQXUuDBXDaWcMzMltXav4ptOW3c8tXMaADC3++V412//DjIeYVGLi4u49s1vAUCkUz5/2ZJqNNH49sKuajZbmum6jmVjADI6ZY1EBSCexHQwMZfznhVdzqSrnxDfAUQsJE8A+rSikyEpa0B1oqUM8dbtSh0s2rQMSh0mGxbrUG3JqBQrALYDjI7Lr74CzUrBEp/VyAhHjsnabU2EfByCUbL22ZzLJzOApslIf9oiW6WQj0N82rBwKwiOEF+S2kael9stDJr4o//8pwAAuRPGf/2rj+HIkSPWvfgYSKfT8AdiAJIANHTay5ZUo8P+XlpoucCjbrOLUFPWIOV7NnRWJZgFfRxCRpe7kPMe3SsVqIWgYomF4FrhaIDFRhGzQKcEkBsRAwaRhIKVY5OHsJUYljrI4FnWsp1r2M/3D7dNJCqWodpScN3vfQJPPsAiNiXjrX/059iZDFry2gLHQhS44aHSTXHnmcBKhHwcwjFafXL5ZAZA4opJyM6sRXOXLMtgfp7cp8oODbflJZKwxgsa0inriE56lhCUetl7996l0yR9VQzJuO7X32VJNdrHs5hOkb/TCalDva10JSqxKWts6ABAFDhEE+TcC3nvVXyLxZ6uPuQLOHbcTfH4sUKgD5AbUdDH9dliTbSi1mCQ+CZSsiWDbRRhf/+6tWQViqqBH9MjeKtD13VU2wqkLLnpJCx046AgmxZynRGpgww4d4+bwCMI+DiEYkb1yWMP4aasomb4ic5Z6Cc6M8VC8GtoNznkbU7F1jQdK8uGrdeUNeEVFKkpFgyro1njUG2owPoCT23F77337/DAj4BIHPjgX38M50yHLHndeSPEopy3v+Jb7/QcHYjU4cyppOvF1BQh8FLRW6ltHUVDtWRUuROqY6ltwCaROlilEwUIifYHiE9pbqk90YpahGpbQbPGolXnIPg1BKOqZUOJACFP/oAOf0CFIrNo1VnPVR42I+odFaqm9zYsxlCilZsWs3/2RKaydRH08d2KL3VQ8AoabQ2NMnkIp1PWkfJIoKfzXVm2l3g0ZBVSrufha2XrOBLgEAwbMpW8t5yQcobTaTCmImihM0BqmgHvMzYtRXufNbW2ggp147Cw4gsAyWmyXrUyi47inWemebOZnHL2M+Wtu88ZYOXONeTnsXT0BwCAxQefmlR8LUK1Kfc5OjCMteRppD57QqDGRq1bqe/XZltf8SWvO3Hk2LoICCxCBvGVit6q+GbzGjSNQSCsWuonarY0K2QFqJp9D3izDywJQLDu/hsQuO7aeW0wsVAg/w1FrAkeoYiIvQG3pSXLXnYk+qQO09b+HdPTxjHKpFPqFbRkFfUyjSueEN8+iDxrWQk8Ho/jhoO7cHzx2wCA5RfKuObSHZ4x4t6sUI3I20EPXys3LAEaYmGuHHroIt6sqIyQqADWrh0ZTOxJHSYblq0JhmGQSBqaV4mzlQSuF5TMhWLWJGZRhP1ct+JbLgi2ugMQRwdr44opgiaZitf02QU6IBVVELBw7ULm9LZle6mSWeowO6dZEl5BkaLEt8J5qktqHih1Mq4Y2ATE16rpRoBYn7z+uhvACUUAAMPM4xX/4U2eMeLerGh0SBAIdXSwMrXNjEFLs0nLfHx0iW+GSh2MtbNRnz1Zt62LqeleepuXqk+U+IZjqqXkyWyLVSnaOyRV7yjInSbXrz8gWfrsDPo4hAxHjtz4IaqWgnYPInHdEv9lirCfR5RGTuc4tG38vNbavfAKOhBpFeZmaWyxt665lqx2iS89R6fgeeIbsfDiTafTiEejUOWTAABdT0EQg54w4t7MqBo2PT2dqPVVQ4DswGmIRaU4saKzAtUWWStzxdfPc5bk3VOEhjYs2iQxcYti2lR9anmo+tT1E40rlupE+xLACj5bK771toqjjy8DAI498TVLfVH9PNvVZ9MKq1cglQiNod0EqxDyc92KL7E0s+95UyxraNY4cLyG9Ky1tGx2hqxXs8qh3vbONVdtqGjWODCs3q1KOwXPE18r204AUC7mcfD1LwcAcPwOlAoe69tsQtAqBq0aJi2MKzajr3I4qfhagkpLgaYCUs4eNw6AXMP+gAbBr0Fus2g1GbSUCfHdikilaMWXR0vxzvWbN6qY4ZiKgGDdY7Gv4msj8Y3H43jthfMoGUlYTz/8RQQCActkfAzDIG4Qy5KH9NmapqNSJuuVTFp7TyFevmS9fvJ/foBjJ09b+vpmLBkvbbUbB2AMJkYU6DqDXN47xDdjUK9QVEXY4r95NXie+FrZrgGAz33hn/G2//Qn4HgdqhLG4T/7BBTVOx+GzYiaQUDNqW0+w7/VSoR8g16+3nlwblZUWwoqRR6ayiAcVyD4dctCRyjCfh4MgwE7usk1txUxO21uu3rnM0BdJuJJ1ZLgFgoSYkE/94JtVcPFxUW88g3XA9gOABB8BUsSzMyIJwixlIreoQ0tResOSE1ZbLEW8nF9MpVbP/Y31h7AgKrpyGYMW68p64MczDKVFQ8NJq4YdstOxxUDW5D4hvwcWJa82QBQm4QhjI1qu5/4JlKyZWl7ZoQGtKKTIanxoOs6Ki15aLDNSvtAgJjB+3muf9PiIVudCZxDKtVru9Za3rl+SwaZS1hMnliWwayh2awU7av4ptNpcHwMwCwABXLnuCUJZmZQfXa55B3a0JJVNKpG1LTFa7dtdhp3/tUh+hW+dOcdEEXR8mH4eltBuZvaZu1QIkCIb9CDwTH0XEIOxxUDm4H4+qw9RYEzHsKJiS2WVai1VXRaDOplHhyvI5JULK8aAsPDbV4S6m9GUA9faYj4Wr92IR+HaN/aTYjvVkQs2Gu7ZnPekLvouo5ykdoqWf+5nJ9lwHI6GhUBxbJ996zsCjn3cLyD6w79piUJZmZQYlkrc57xg23JGhoVsnYpixL3KBYXF3HJK843vtoGvyhaXkUHqKMDuQdHp6x1FQEI8Q0bjhxZDw0mdnX1sUnFdwhWV3wB8hDu2mKVvDXpuBlR72hdjWhsWgbLWj/YBhCpg3m4baLxHQ80ba844MZhy9r5uUnk9AR9frBZj1Sf2qZ2uR1+opEAh6gxJGWXH2xH0fC6wx8CACRngfd/6KM4cuSIpceYmSH/bVS888xsyirqBvGlMhqrkE6nkTQGE4E5tFuy5VV0gDo69MIrQhZXfP08i1CU3G+LHhpMLOZ7A6UBi2WRq8HTxNfPs/DZEEk7/BD2xkW8GaENevhSRweLL16ArFsoqoJhdDQqPKoeapWuFy1ZxdF83VaLnNUw7OFLU9vskKkMVOs9UjGawFkETHrDrEf0hk1ZRU0yqoYz1r++OcRieZmxxdGk38NXsbxdDgBzRkW1VvbOxrXeIs4AADCXsp4rNKtZ8L4yAB6vuvr3La+iA0bwCF27KevXzjyY6CXiS+VF0YTmaFwx4HHia0flCRh+CE+kDhtHra1A151plwd9HFgOCBoPzkrJWxGMa0W1peCLD53C3T9fwV0PnUKlKa/+S7acR7+VWdImGzqgv8tSLXprsGkC5xAUekEIeY88hNsmI/30nPXnZLbFknL22GKR5C+a2iZb3i4HiGQDMIIQPPLMzBV06DpJ3IsErP+bP/iJzyC1nXTEXnX9TZZX0QFj02Ks3VRKtYUEUqs3Lw0m0nOx2oZuLfDOuzACdu0CQj4Okbh5wtwbF/F60ZZV/GKpgkyl5do5lM+Y/GV91ZDqs7uDieXNuWn5t8UsqsaQS62t4DtPZl3xtS03yTnQTUt8VoYocLZcd+bNZqUkTIbbtigCPq7rB5v3iN6w1lLRMKqG1PPUSpgrvuWCgLoNA241c9XQpopvPMqC92lQOixKFW9cv7k8uW+S1DYb7lu+3tqtrLDQbEgbrHd64RXpeXueA93BRMkblK8tq6gam83paeeffd54FxxG0McjnDCRp01IfNuKhi8/vITvP53Dl362hKczNVfOYzgAgbTLbavW+ziE42S9atLmk6kcLzRwWmr2/dtKpeXK+lVbMnS9f9NiR6UeGNDVT+zMtiw4lkEs4a3qU66gQ9eMqmHQhpkSU4hFOc+jZkN622BcsdWWWACpXNNNy0rWG4OJVCcejKgI2qATDfu57lxJOW9PoaXSVFEpkvvu/LzlLw+gN5hYlbwxmNhStK42e9rh8ApgixJf8wVcl0iS1GbDwskaig1CMnXo+P7TOUgN51vmVCeaNwy4fYE8eJa1bUqTrB05Zl3iNh3xffRUeeS/LxyXHK/6lpsK6mUOcpuFGFIRCGm2bViCvl7Fty5xE43vFkbcY23XrFE1tIs8mf1gy3nBFkszc7t8ZlaDYMNsTEDgEIoa7gAeGUykKXLhmGpp2iRFyMchatq01DvWr93yig5NZRCMKkhG7XlumgcT2x6497ZkFQ0bB0pXgzfuPA4jqQOZ3AAAIABJREFU6DO1yyXvTKiuFaVGB8/k+quGsqrh348WHT+XitEuXzlBbg5P/Ps/2kaeAKNa3127zeXsUG0pOFlsjvxeqdHBiTN8zw5omo5qW3FEogL0bzZrZR4ND0VnTuAsklM9P1gvRFfnDMlFMKoiYMOGPTxU8bVX6pCet+faEgWuO1+R88hgYtFIkYvE7fmbQ6bkvXJBsKVav7xM/oaYDVZmFLMG8a2VvVEsasoa6ob/cspiN461YEsS35CP738IbyLyBACPnapAx/AD42iujkKt4+i5vOWKvfgvb305Os0EAA2P/ODv8buv3mu5yTdFyM8hYkgdqptM6vBMtjZy3Sh+sVRx7FzoUCKNmaZuHHZKHXifDjGkQlMZVCoMVBv0chN4H7TCUyt7o/pEiW8oYo+tko9nMTNL7lN2VXyrjZ4l1rZtlr88APJ30HtvvmDPMdYLGp9MU+WsRn/ktPWblkZHRSlHU9ustzKjmDOCY+plb8w0md04Ujbo6lfDliS+QR+HQEQFw+po1jhUG+5/ENYKRdXw9Ep15Pd06GdspduF/3rnvdh38DcA8ABWIPhZvOaaN1tu8k0xWK3fTJuWo7n6Wb9/rNBw7KZUbjqrzeYHBhPrFWFTVesnsA607Vove2PjSquGoZh9tkrzafLfaolHuW7937yU0aFpDEIxBYmwfZ6oVJ9d8IhMRSoZxDdpD/H18SymUoaLUIG3fDCxbtJmR6ftq/hOJVjwgga5zaLogcHEvFlXb4Mbx2rwxqfXYfh4Fn6B7RqpSyUWiur+h2EtOFZooHOWc30mU3NMvC6rGnyRKQC7yD8wJ6B02ohHY5abfFOEfDxCJqnDZhmSqrcVZCrts/6Mput4bhVybBXO6MZhU8UBMAYTqbZ+kw6VTjA+UsYwS73iDVu7oqEQi8XsO5dYmEMoLkPTGCwtW0vSZFVDdpk8yuPT9lUNASBhEMySB6zodF1HxfBfnrKJ+AJAOk0+F3ZIHfrDK+xx4wDIvbfnn+1+py1j1tU7nNoGbFHiC/TH+NUkzjOG3KthNWKkaBqeyznjEFBpKtCho1oMAAD2XbYNV1xzCJWSfT5FQbOrwyYiTydLzbPKHCicIr6VbsWXeFTa6b9MEfSbKr5lwRMttwmcx9xsr+3qhaq/VDJIo43kKeTnEU2SrsrSkrUhFrW2Ailviry1wdGBguqzvTCY2FY01CvkPOx0BkjNkGppq84hL1lNfHtxxXZKHUSBQ9AoOrz3/3svVlZWbDnOWkE9vINR1fHUNmCrE19T5XAztMw1TV/TANQzDlljlQ0rs/Nf9i4AwPw5Adzw7lvw6c/eadsxhzYsm2DdAEJ814LTpaYjaW6jKr4+jrVlMprCfM3VN6G2fgJrkIhyEPwaVIVBUXK/+pRbJj7ofp99GvuwifhKFtti1dpqr2o4LSPks2/zOm0Q34oH/GBbsoZGxXCysNELNixyiBo631OnrH1tYkPXq/jaVf00PzefXczgIx/5iC3HWSvyff7LE+LrGEL+/gG3zVB9Wqm00VZWP89TUssRUkEdHco5PwB744opBn18N8O6AYTQrgWaruOYA+4OZcP6TjJFTdvl6EBhHiqtlwVP6DsncB4BgUUwYrRd8+532p5/4jgAYPHhb9h2jJCfQ3SKEF+rB9zMOtG4TeEVFLSyWpU414dTW7La9YK10xmAhFiQ+yV1YLAKdZPUYWZOA8fa83ekppN49pGvG19N4fbbb4coirYNoa+G4qTi6w76q0/esPhYDSdLjTX9nK7reCFvf8ucVnylXK9dzjCMrTdev8AhFAJ4QYXcZnHimEd8dc6CSlPuJrWtBccLa1vncVBpKWjWWTRrpPoWiqm22tABA9fcZLhty6LPD9bF9LZ4PA5RFFFYJtfbow981TYyEPLxiCTtCbGothRIRtUwalN4BUUqRf5bK7tvA2oOQZhN2Uh8TSEWJYur9dW20pU6bNtm30ZicXERqe1h46tpBAIBHD582LYh9NVQMoYSozH7yP7ZsGWJr3nQZrMkgJ2S1h5NfDRvP3mizgDlfI/4hn0cWJs/yEE/B5YnEynf/vwRR6QB42B5laG2QRwvNmz1N212VLQVtU/mwDD26nuBfn02meh3v9o3gfMImPxg3YwtXlxcxFve+naAIWVMwVe3jQyE/Xxfxbfasq7iWxuo+NrlDAAAKVMQgtuDiY22iobhBTs3Yx+VMccWW21pli+qaNU58D4Nszb+Del0GsEw4Q8sm0a73UY0GrVtCH01lA3iG7PJf3k12Puk8zDIQ5gQN6IVddb/dr1QVA2ZytqJ76lSE7JqT4IPRblBIm8p8Y2nZIRFn23HA0iVptVqAfgpgBk88v2fIhYJQRRFSJJk67E3ipXy2tcNIC28bLWD2ajflvOhGxbJ8PCNG4NtYZuJb8jP9w23bYbN5gTWQxRYhKLkfltw0R0gnU4jEA4DOslzlTvLiEYvsYUM9EsdeFTb1nXkqqZ2eXpeA8PY957OTpPnSaNGrCSnbDvS6sgVNegaAzGoIha27zlHNi1EfkaJbypizb15aYmcd2xKQVi0t+Wvq2Sg7cWvOoyXXnDM1QE3qUT+Vjos6TS2bMU3OGCL5XV3gGy1sy5NlaJpOL2OCvF6oes6Ki0Seat0OATCKsSgfZG3FIuLi/iVN1wPhiWlIo7fjje95W2utWzWgpV1bFgoThTtq9hLQx6+9muzASAomOKmPTLRP4Hz4DkW4Sip9JRK7p7LykoWDEvKmG/9jauRyWRsOQ6JLTY6ZAVrK77Vptp1dZift+xlRyIW4uAPqNA1BvmiuxrfnBGbbLdONDgYOW3R2jU7KgpZI7xiWra1Ug8Ah373MABA0aZw22234ciRI7Ye70xQVA01w40jmXTlFLYu8Q0I3MBwm7fbrsseI0/VlgLNnPxFAxBsJk/pdBqRaBS6RnarqpKAGAy71rJZDaqmI7+BNL21ukBsBMPhFfZbmQEDUofKpOK7lUGDEEou22J9+NY7oGs+8D4NH/34X9tGBhiGQaqb3saj2rSO+GbzGpQOCzGoYjph7/spCmxXppLJufvMpPrwUFSF36bgEYBU63ubFuukDoOODnb6LwPAVHcwkXV1MJG4cRj+yy61DLYs8Q0NJIB5vfq0HpkDxVqszzaKIfJEHR1sdgYAgGqpgPk95IrZuf8qZLP2VGmsQKlJNgjrxUqlBdmmUJVys9/KLDlLiLndUgfS4tbAsDpaNR5VGxKsJtgcoBGzVOvnFrLUVimiIuCz93GYjAGBsApFZrFiEWnsKBqyGaNdbnN4BWDosw1HjpyL+mwAKBixyZGYaqu8Q+BYzMwaNm4WhliYB9ucqPjOGs4XRJ/t3r23pZjcOGbcOYctS3yDPg4RI6nHK9GZZ8NqqV+jUGp0ukEFVqM0QHzjM5T42i8b/9t/uAOX/erLAAC7Lng9PvSJz9h+zI0iX9/Y+69q+rq1wWtFeSC8Ip6SwbGM7TdehmEQFrluYmJpEyUmTmAtkklv+MFmHWqXA0TqEzUqh5llaz771ZaCcs4UXmHzNUw2rwbxLdh6qFVB9eHRhP33kPQ8OUalyHcLB+Oi1jJVfKdlW92QgJ7zRb3qboe7aar4ztgYPHI2bFniy7IMojGAFzR0WiyksncfwLW2suH2ygmbWuaDVUOndKKA4Qdr8vL1chBCobHxm+R6XDzWg5Lh4duTqZCbrp1VE4rBABKvS4wmsAdThravVuFc3fxQV4mgze1yAAj52K47gJTn12VxeCZUWnLXyiw+Y6+VGWBsXo2CkZuDiQAgFcnx43H72/bxCI9QVIGmMlhaseZ4ZjeO2LT9Uoc5kyOHm4W+VkdFo8ob5+TOZ2jLEl+AaHcogSqV3NW9nA256vqrvRSnbCK+UmN05K0TFV8zeap6nDwVGxuvuNuxdg3DyqzTYlCTeHC8jmhScWTdADJUGt5EQ6UT2AMahNCounv90nZ5NGavGwJAKr7xaZOXrwVDUtV2r+Ibn7E3vIKCWlCVirYf6qygXrAJG6OmKcK+XnrbskWR00TqQNYrMaMgINhLxyLGYKKmujuYWJB0qAoDwa8hHnE+vALY4sQ36OMQ2gTxt7kNDEdRnCo1bfGElRrknMwVXz/PwWdz1QQwBhM3AXnSdR2lNVR8K8UcPnnTjagUc33/nq22Ldf50g1LMWNsWGY7YDlnNiwAEPCxvWr9xNlhy2LG0BvWKxxaa0ijtAtFaqTvgJ9o0Md2yVM5L6BiAfGttRRIuZ7czM64Ygqqzy65rM+mMhknLLFCfr434JbnULdA51tt9Sq+c3O67Rsvs3/2Sta9zWY2Z+jqYwpEF1LbgC1OfAmBMrXMPUqg8rUzV3zPRJoomrKKQt1aj2JV01FuKdB1U7t8Vnawasj1kaeWR8mT1JShjOgiNGssvvYPs/hf792Nb/zvFL792Ttw7Imf4d67PtX3c5puvc63ZGxYiivGYNuccxIVgMYWmzabHr3mfplx8ODBtx48ePCqgwcP/plb5zBrtF2lXAcnTy+7dRq9dnnCfvIU9PVXfK0gvpWW0rUyi9mc2kZB9dluDibKqoZq2XAGcMASK+TjEDVkKuWCYIlMpVRVUJN4sKyO+fTYL7cqzPpsNxMTu24cEdX2KveZsKWJb7/ekHc9gvFMyFXPTFzvvetTI0mTGadK1pKnclOGruto1li06hx8oupI5C2Fj2e77bZ62Zrdtx0ojhhsazcZ/P37duHHX53CsSeD+OFXpvHQPe+Brgdx/91HcNPVF+Hm6w90f95qnS/V9xZXSMWXOjpEHXDjAAYszTZJYuIvEw4ePHgZACwsLNwLQKJfO43pJAtAg66F8bd/8zE3TgEAINF2uQMDUiFzxbcgWGKLRYbbyH13elZ1pIIWT5D/ujmY2DYNSDnhDEAqvnTtxt+0aJqO5WXy/kWSCiJB+9eNRIW7n5h47Cgx7/YFWpOKrxsImizN6mXOk0NSHUUbaXb+ocNX4aarv4n77/4gdP3buP/u54ZIE8VpyVqtKCVPhWXaLm+DYZyrGgJANERCM3SNQd7l6eIzYVSl/Vv/NIulowFMb2vjLX/0BPyBJQCXAfgbCH4RB157LW6+457uzy9ZXvGla0eI7lTamdQ2in6ZinflRb/EOASARhweBXCV0ycQj8fxol1TAMiF++W7vg5RFBGPx50+FZS77XL7jxUUWMRmrK34lpu9iu/8Nmfa19OGtKBSdo8+mC2xnHAGMAeQVPL82AEktbYCKWeEV0zZb0MHkIH+iFEwyufdq9Z/51+/CwCoFhcdkUaOwoT4mlvmHhySKtQ70NHfhlNVYPu+5wD8KYB9AP4DgB9j72Xv7SNNFKellqU638GqYWKWSDGcqvgC/ZuWaolFR/Hm2pkh5Xjc/80EGFbHO//8FK64msH+l3wcgALg9yG390IMhhFN9koYmUrL0qn3ntTBqPjO0YqvkzIVg/iWvSsv+iVGHIB5LMlxC/nFxUX8+lveBjDkNATfNhw+fNjx9EVd11Ez2uXTDrwLAsciNUuu5XJeQHVMq0lF1ZDPAUqHRSCsYirhTPVsxniv6hXOtfuu2RIrnbKfxJHYYpPUoTXe2lVaClaOk4JUMFp3RJsNADGD7xRdGEyMx+MQRRE/f2gRAJBfeti1Da9zTMWDCAgcQjHy4Cd6Q2u1sFZgVNXwp9+J4/iTM+B9ZSidt4Jh3wVdeydOPPWnCIRKwABRbisq8rUOZizKF6fkqbBC9b3uEd/cKT8hUB3Vtd3jmVAcWLv7vpaEpjK49Moy5veQ90zTHsPcru9i5fgbkJz936iW/qLvd1RNR6baxrZ4YOzzUTS9W2Wiazc1J4MB48g0OEDlRT1dvRc3mxP0kM1m1/07kiSd9fscx0H0+wA9D2A/5E4YPM+DZdkNHW+j59KSNVQlwuL8vgqy2bqlxx51LqJfAe/T0GpwOHG6huWVDDh2Y8RNaipYOmlo9JNtqK36ut6/1dbpTPALCoAp1CssTi0vI2yBrni953K60EK9EgUACHwJ2Wx17HM427loug5/iByjlGNxOltEdgxd+PFcEz+95yEAF6KY+Sk69b3IZoef9RtdozMhECT3+ZVl2ZZr+2z48Y9/jA9/+MP4+jdmoakAy5Vx/XVvxgc+8AHHz2VLE9/Bh7AX9YaDxLfTYvBvd5KKYHr3J7B97zRe+oYwPv0XJ9Cs7sT3/oXDG945POh2SmpaR3wN7WqRSh1SBvF1UOoQGCJQKgBndKprgabpXQcFAJA7DB78FhHHXXlDT5tx4y23oVLk8JEbdZRyL8Xv/80nAfRXE5akliXEt9xUoOs6dN1U8U13EPJzG374rhfBTZaY+EsICQAdB4qD6g3OgFQqtaGDrPZ7jXoNiZSIUha47BU3oFr9/oaPtdFzKTU6aNXJdXDenihSqZAtxzcj3VQRTbRQzARRWlEQiCYRD27svtUsNNCuEdKZnFWxLZVEKrW+0vVG3vO9exvk+FUe4XgSKYueK+s5l+eLVWgqC5+o4dxdM4gGrL33jzqX3XuMIlnJD8YX3PDnNR6Po9VqAfg4ACB74gd43WVvhCiKI8mclddFer4GAGg2RKRSG6u0jnNPSKVS0FRyXE3NIpVK4eKLL97Q641zLt4qkTmMfk9Rbz6EB6uGD90TR7UkYNt5Nbzn796CG959C3bs3Y/f/kty7vd9NYlWfXhZlywckirSdnnG/YovQNbOay1zqSn3RRU/+3DIGAR8CvGZk30/G02quORVFegag/vvTgy9llUa7XKLvEfVEge5zSIYURAIaY5VewHAz7OIJnryIq8OJv4S4wiAPcb/7wFwrysnceQI9h04DwBw+Wt/C0eOHHH8HNqyhkaVDkg58ygM+zgo8gsAgB98+R5UxmiZl1uyycPXGSszAJhJsmBYHe0m51rsOI2aDkads8RKTTPgeA3NGoe8tPG/e3FxEa+++k1g2J0AAI7P4oa3vt0RqU/PkcMd6pfNZjE1T+aQLr38fGQyGVfOY0sT34DAmobbvFnxHXQGWLiX7JZefv0KzLZ/51zcxJ6L62g1OPz0nuGdnFU632pL6XrLmofbOJZB0Oa4TDOCA1Z0Xtu0lAaCKx6/j7TlOq3Pj3TgePkbiejqZ9+LQRvo/i+X25aEq5SMtD3q4UutzKIB54gvwzCIR1nwPhVym0WlOpE6OImFhYWHAeDgwYNXAZDo124gZiSAueUHW2sREsMwOlJT9p/D3r178cYDO1EpPgIAWHzwWeybT25Y41hpmjx8pxVLJAdrQdDPIhimtljuBCHkjKZmOOqcxC0S6Ol8i3kW9Q26cqTTafBiCLpGPMxU5RgS8Rjm5uYsO9czgQbHVF0aTPzsnXchOXsJAODtv/02Vza8wBYnvjzHdlNfamUeDY9Vn1qyikand3Etv+DHqWcDCIRV7D843BK58i2ka/mTryUxyHGpzndcUOmFqgJSVgDD6IjNtB2LvKUI9A1JcWh6TCtKddAA8P7rXoaffZcuyJdH2pbtvrCJRKqDSkHAsSeCfa+laNpY6X3dc6LEd5l6+JJzdFKiAvQHxxQLrKuRtVsRCwsLty8sLNy7sLBwu5vnETM0ktRL12lkc+RzFwirCIn2k8b77rsPb7j+BrDcCgCA5XbjV6/99Q1X+vo8fGeccQYA+oMQMqPt420HteOKxJy7dxBnh14AyTjODoVcDj5xHwDgJa87gHzOWm37mTBtCo5xYzCxZRpKnHF8rLaHLU18ASAeYeEPqFAVBkVrdeRjY7Bq+OgPSdXw0ivLEHzDO+0LXlpDdEpGccWH44vDmtDTFsgdKPEt5wRoGoPolALBpzvmCkBhtsWqlrxXrTfre3/r5vsAJABmEcAzI23LGAZ48asrAIBHfxQder3TFtia0RS5gqHvnaLhFQ55+FIEfBxCMXLsyYDb1kW37eqSHywlbcGoM0b6s7OziEej0NTjAABNnQMfCG240ldu9ksdnJIs+fleEELOpYpv0dgsRR3wX6YI+XnEjACSSoFHeYPEV9V0/Mb7b4MiE+b3u+9/j2OVzznDMKhR4VzJLWjJKuoV8jm1SdK/Jmx54isKHELGkFQxz1jSUrYK5qohADzxgAgA2POiUyN/nuWAA68tAwAe/l5s6PtWaEULRtWYyhymjKqhUz6wFObBxHrZe+Ej5k3L0gvz5H/074L3+aF02kO2ZQBw6ZWE+P7iJ5EhucO4a9dRNNSMjkbmOHnxYJTIK5zUZgOkchKK9qr1XtNnT+AMpqbd9YOlpC0UUeF3SCdaLuWx7yW7AQCJ1MuQG8PFotKUIRnhFYkZxTGpGcMwCBuV1oJL1fqCMZIZcyBqmiLs5/oszSobtKOrtGRUJA6ayiAUVZCIOvf5n52hFV8ejY4LFV9F6/ovz8245yW85Ynv0JS5hx7CZvJUKfLIHI8AaOD5x/77GX/nstcR4vvYj6JQB/4UK3S+tOLbjbw1AhCcSv6iCA5IHbwWPmJeu+cfI9Pi+17SxntuvQtXXHMI1dLwMP38uS3EpmVUSwKWjop931uWWtDG2JSZ/aCP/qJo/PdfADjn4UsxWPH1mj57AmdAvXMbLrVdKWmLOEie7vrnI/i1d/06AEAMX4QbP3Dbhl6n0VHR6mgoF8h9d25ed8yZBQCiBvEtuhQe1Aseca5QFfLx3RCLcp5HeYODiZWmglKWDoY7J1EBgHiEg0/USIe77Px9V6qS2Q6O15GMu5PaBmxxOzNgVIiF6uiU+9lAydPN1x+AIv8GgH8C8D08+O078eC37wQv+PBXX3uk73fmz2ljZlsbudN+HH8yiD0vanS/11ZU5GqdDdvPqJredZkoDAQgOK0T7XMH8Bh5askq2go5H7nN4PhiAAyj4zffdy2CEQ03vPuWkb/HMMD5L63hwW8l8NRDYWw/rydv6KgacrU2ZqPiyN9dDflaBx9+x6ugyB0AJwAAiw/+E266+hZ8QBRRttgv8mwg/tm9qHAvbTYncA40cate4dCSZcd9uGnVMOog8Q0ILKbmyGe/lBHQUTXU28q6yU+5KaMq8aRqGFMQDzv73tFKa9Glii+Nmk4mV/lBCxH284hNE6/ncl5ApbkxqUO5JUMyHJHiKeckKgAgCiyCUQWdlg/LGeDCnY4dGgBAGxzBiIKgz72665av+BI/WNND2EMEqmSQzJvvuAeJ1I3Gv34bgl/Ei37l10amtAHABZcTr74nHwwPfe9UaeMt82K907XoKnYDEAzi63DVkGEYJOIMWFZHq86h1vDOupn1vaeeDUNVWKTPaSEYWf0Be/5LiUn6Uw8Nr93J0sZ1vrlaG3/8ya/i0l95M4BtAFTwviwO/up1eNrhxKxgX8XXW12WCZxDymh1NqocWi5UfEsGaYuNEUSwXjAMg9kZBj5RQ6vOoVljUd4AgZIaA/peh++/cUOf7YYjh67rqEikWjjlZMXXzyGeIvetUlZAeYNSh3JTQclYu0RKRshBNyRR4FzVZ2cLhrwopjpmQzcKW5749tliecgdQNX0rng+mpxBvXIRAIDj74fSacMfCA1pRCkuvJyQp3//eg2VYv/Y7akxtKJmVwiq8aVSB6eJLwCExF7lMOti9vggJNMN8fSzROaw68K1ve97X1wHx+s48VQAK8dL+ORNN3bXcJy1y1XbiCSmAWYXyGV/EqpcRzQSccRGx4yhiq+HNpsTOIfUdI/4uvEZoKQt4eCAFEBssRKz5F5aygqQmut325FM+l4nB9soeoOJzt9320rPGcCJqGkKUeAwM0c+p6WsgHpb7Vp7rgflptyTOjjoxgEAIs8iFDGs6Fxw5CgYbhzBiIrAhPi6h4DJWqle9k71qdyUu3pcKcej05oBx9fxnlv/Aldccwg16cziqt0XNcAJNcid3fj67V/t+96S1NrwAF/WZKllrvgyjHORt2YETDKVStG93PhBmPW9p58jxHfnvrWRVn9Ax54X1aHrDP7P3z+MY0/8rOv7u7zBtVM1vbtpkbLkfHbsD+KKaw6hXnZepBfwcQhFjYqvhzabEziLWIiDP6BCUxnki85Xn7o6UQfb5QAZkkrOGumXGaGvQ7RWSE25a2UWn1YcrRoCwJTxnrkxmNiSewNSs6NrP7Zhbk4Hx2uol3m0W9iQ3KHckCFl3ZE6sCzT1bQXXNBn541jhmOao5r0QXhDzOoiAgKHcIyQkprEo9EZ3+vWCpjbKNTXde+LNWw773zc8O5bUK+PzpUneuAOgDsB/CYe/SGLR394UVcPLKsalsstbE+sPwKXEt9mjUWjykPwawjHVTAK68qHOGiuHBqbFqd1gqNgXrtTzxLJws7z116tff6xDwL4KJ57NAxAx/13H8H9dx8BL/hw3YnMutcuX+tJVC658j04/hSwfS+PG/7wFhzYsTHz/HEQFHquDl4NjpnAfogCi2BERbvJIZvXgL3OHr9qEN+ppLOkO+zney3zjA+lxvo9uqWGjFKGbGKdJk9Ar9JaK7PQdd1RD/eWrKJRIXMqsylnnzvRAI/4jILCsg9SToDUlDEV9q3593VdR7llqvimZIQcCh6hiHaJr/PP7EKRXHNRB/2XR8F9luAyiN7Qe7ZY5nb5sScJ8d190erk6eY77sGB11wLjif6X4a5esgz9uQGdL6aqWqYP21YmaU7YBgSw+kGAgODiV5pmVPiW87zqBZ9EIMqpretfUP1Ox86ZPzf6wCgz/d3I2uXqfS0wblTZO1mjPNx2tEB6Hd1yJ6sYXl5xfFzmMB9mIMQcnlnj61qOmpGtZKmWTmFsJ/vVnxLG6j46roOqUH82gEyYOxUahvFtGkw0elOW9NU8U07THxDfg6JlKlav06db6WlQNX0bsU3Oet8tT6epI4czhNfqdh/Dm5hyxNfcxBCzVNSh14L5cTTpMK364LGmX68i2hyBv5gCKrybQCArr8CPn+iTw98orj66wwiV+tAMcz6cJyvAAAgAElEQVRlcwbxTW03PHwdvulSkGp9z4rOK36wdO1OPkPWbfveFth1XGl7XxwAJ9QA7AIn7O/z/d3I2mVMEpXcaVIpocTX6aEYAOBYBnFDV6kqcXzp059w/BwmcB9+k97QaeLbklXUq0aClAvEl2p8KXlaj1VhtaVA0TQUMzSB0VmdKNDzg21UnB9MlCoqFJkF79OQiDn77An7eSRSPX32oNf+apAaMpp1Fq0GB8GvITUNR6vlQE+fLbkwmCgZXZa4gwOlo7Dlia8osF3NS63Me8YPVjIuKFUhUcUAsO28tU3116QiXn7t6zC9TQIQRObkbN/3c9XOunPG+6uG5HymtxFC5RbxDfpYz4VYNDs9K7Mlum5711elZVkgGHkUAPC6t3+hz/c3V+2su7K9ZErsyxsVX7p2blR84/E4/vLtLwEgAeDxo69/C6IoIh53XnYxgXtg2V4QgtO2WObo1FmHjfTDfh5Jo2ooZQVour6uymGpIUPX0av4znYclzrMdgcTne+0rRiWWKGo8wNSYT+P+CxdO9+6q/VSs1/m4EbhoTuYWHKe/lVK7siLBrHliS/DMJiaIjffRplDo+2NQRvJqBquHPdDkVlMz7cRCK3t3G685Tbc8O5bcMHLyA3p3Be9r+/7OnScKK6PjC2ZInNpxXeGVnxd8uPrq9ZLnCtJNIMw63tXXiCeu+nd69fwve7t5wEACsvn4oZ334IbbyFG9zp0HF9H1bfWVlAxjNbbTRblggCO15GYdSd4BAAWFxfx0tdeDTBkrJgXduDQoUN4ymFbtQnch1t+sE1ZRaNKSEc65ez9K+znutdfMUPupYPx9GdDod5Bvcyh02IhhlQkEozjsw2JKKlYqgqDguTsfbebuBd1fqaDVHx7MpX1rBtALEH7PHxdIL5UplJ1eDDRLC+acrjLMogtT3wBIBLiIIZUaBqDggvTxYNQNR01oyJ7+jnSLl9rtdeMvS8mA3DPPhoa+t4LhfW1zM1Vw0GdqHsVXw6huLeCEMz57SvHSMV3bvf61+7cS8jaPf94CINhe8fWsXanTetWXCHnM5XugOOIPY8bw4DpdBrhcBjQMwAARY4jEHLeVm0C90E9dEsOE99CiZA2wa8h6nD4Q9DHIRLXIPg1NGscmnW2Gwy0FhTrnZ7MwYVqLwD4jcFEAMjknH1m5vI0cc/5+33YpPEtZQUyaLeOinepIfd5+LqxdnQwsVrmxkoDXS+IvIjvOwe3MCG+6I+/LeZZRz8Mo1Bp9azMTj1HqoYbIb7nXNwAy+k49UwArXr/Up8sNqCs0YOw1Oig3iHvj6aZdKLb3ZU6iAJnkjpwnpA60Pz2TotBfskHhtUxu2P9TiGzu9oIRRWU8wIKy/1V2eOFxpptzcyBJYVl8lmi6+aGzIGiJhWRmCWfo32XvRnLK5MBt60IOuRSdlhvmDVVDZ1ulzMMg4jI9xGowjqIb6HeQdHkox4Wnb//BgSuS3yd1md3E/dccAYImQcTDcnCejctZg9fN56dKZf02W1FQ71s6OodtqEbxNjE9+DBg289ePDgVQcPHvwzK07IDQRMBMoLA27mwbbTBvHdvgHiKwY17NzfhKYxOPrzYN/3Oqq2ZocA889VCjzkNotQVEEwooEB456rQ99gojf02VTqcPQXDeg6g+RsDbxvmKSyDIPpsP+Mgw0sC5x7CansPv9Yf8W+o2prHnIzr11hiXyWqMNExAWZA8UHPvo/sO8yIue46OWH8YlPf861c5nAPVA/2LLDbdeMYd4fjKjwu9D1CIs8kjTEIiP0hQOdDZqmo1iXTRVfd6qGHMsg7FICGHUjcDJxjyLk4xCfUcCwOipFHooMFNc44NboqGjKas/D16W1m0uR/9YrzgbHEHkR4QpzDuvqBzHWFX/w4MHLAGBhYeFeABL9erPBHGLhhQhVSp50HcgcJ1Wx9Dkbi6s97yxyh2ezo72AB3HSpAemg21U3xvyc64ZUft4tjsd6pUEMCp1+MGXfgoA0PXHhn7Gz3M4dHAb3vHS7XjbZdvg50dvHPYYcofnHh9eu+dyq69dsd5BtdXToFHiSyUqsYB7FV8/z3rSTWUCZ0EHbagfrFOgCVLhmArWhftXxM/3dL4rZEhqLSlgUlOGomlYOU6ul2CkiIgL5AmAKQjB2fevm7jngiUWwzCIBTlE4m3oGoPTz1dRrK9N50ur+t2hxJQ7MpW4oc9WZBalsnPvYa1JYroZVsdMchMTXwCHQEazAeAogKvGfD1XEBC85QdLia+UFdBukqpmeISeiWEY7EgEEPKd+eI5q843X1/1ZqsMVIZ7g22kXe5GVLEZ8TgDjtfQabGoVN0fbrvx1RfgpqsvwvOPk41KceWbuOnqi3Dz9Qe6P/PqfdOYDhsG7FE/fvX80X2f8wzie/Tx4JDO92iuvqp/5vMD5HiQ+Lq5dgGeNVnReaNaP4HzmDH5wbYdbLvSBClq5u80wn4OU/OGL/qSDzp0FNZQ9aWV4aM/J8z92JNfcYU8AaYghKKzx5WK7iTuUYT8PHT9GADge1/8DnLVtQ0v0/XNLxk++PMdVzYtAZM+e9lBfXY2Rz4vgbCKkAvyHDPGJb5xAOaPvcuS5Y2hzw/WAxGqlVbP0QEges9BcCyDN12axptfPI/funwHdiSDQz8DADvPb0Dwa8gcF1GT+j9sHVXDC/mzt8xPlJp95Hg4AMG9djlAWk90U5B1WGs2CEXV8Of/RAJEGOZSAADLP9UXIDIbEbF/Ntz3e+fOhLBranj9Ujs7CMUUVIpC92ZJ0VG1IWI7CHNVWNeB3GkyKEk3LTEX104U2D599qTiuzVBiW+jwqHl4H2XukjE3CK+Io+Z+f5AoOwaCNTBvdtw09UXoZwn94unHvosDpwz44oVYDzujh8sjUmemnJe6hCPx/HOV56LaukhAMCTDx7HO1957pre/3ytjUaVRbPGwSdqiCY1BF2QCYp8Lzkz6yDxzRjP51DEeV39IBzdbmSz2XX/jiRJq//QmGhWWxAC5OYj5XWczuQRZ4YJoRPnAgCnskXUmwpOPhcBAEzN14YiivfHAb9cRTZbBQC8ZIbB8ZXWSAKxY38VRx+P4YmHOFz8ikrf9+5/+hTizJm3zg89J6Fe78ksVo6TD2xkpoJ6vQ6txUCS1p9XbhXkVg2BSAflvIDMkoxTyytoVCur/6INKLcUsP4AWJ8fun4RAEBTHgYnHATnD6Jer2NXWhh5HZwb1vDkiWEiu+uCCp58IInFhzgE4/3f/8lTpzDFjd5rFuoyjmd6e1Ip50OnySEYlcH6KqjXgU5dQlatjfMnbxidRhWcaCTcFRgs54rIRtwjv05d2xP0Y3aGkJhGlQ6nOrMZo+1yN3SiAJE6TG/vrwBma6sT31u/8kN8+mP/DY/+cBcAgPet4Po3vR0f/+8fte9kz4CEC0EIbibuAcSK8f/5wz/BD75zDJoKsNwFuPTKa3HHJ/9u1d/NVjsoLPVST8N+zhWZjSiw3cTEbM6549IhyGBMhSi466swLvGVAFDWFAdQONsPp1KpDR1ko7+3Vsi+JpIpQgDadRFiOIpUajQZtPtcAEDn6wiFNBSXCPHdfq6GUKgnVQj5eFx+rjh0Lq9ngvjuU8Of5P0vaeHo4zGceiqJy1/f306rqAAbjHVb72Y0OyoKcq3v2NmTpF2enCkhFApjZ3oaca7lyPsyCrMFIJrUsfICoLbDCMeS8HGsK+fTLDQQCjVRL+kAZiH4ZVzy6oNo1coIhUIICBxeum/HyJtdKgU8WQJWKv1a7n2XtfHkA8CpZxK48s39w4hNAB0hgu2JwNDrPfF0rm/dTjxBqszz53QQCoXAgME52+bAc+7cgFqyhpk5smFqVv3wByOufYYo3D7+VsR0ggHL6Wg3OVQaGuZizhyXkrWEW8TXGG5jWB2lrAClwyBTPjvx1TQdij8Ght0BskFYgiqXkYzFXLECTNDBRMm5e0hTVrvBIykXiG86nUY8FoOmEs9xTd0NMRgGgomz/p6iaig2Osgv02KWO/peAOA5FhEjMj7vYJe0q6uPquA539l/2GaM+4k9AmCP8f97ANw75uu5ArOrwzMPP4ul5WXXzqXWVrrRwJkThg/srn4ydMn2GPgR5On82chI6cF5lxpDUo8N63wBYOH46GrXL5YqfbZZzRqLSkEE0MQj378VgPtSh4DPO5HTZWOQ7FcP/y0AYHanguv+3/d1wyf2psJn3eFfPB8d+rdzX0Q6D0dH+PkCwP1HhwV2tZaCxZVq378NegqH/JxrpBcA/DzTlajUy85OF0/gHQR8nMkP1jnZQUVyr10OkIovLxAvV11jUFghYQhns2TM1zvoqBpKGXIfnz/Xh1dd9w7kcuvvpFqBaeO9qzhIfNuy1o2aTrnkDFAp5fGiV+4GAAQjL0W1VFhVppKrdaDrOgqGDd10uuPqjEVXn33WUqW1oEOQbtjQDWKsT+zCwsLDAHDw4MGrAEj0680GcxBCqx7A5z51q2vnUm31/HIp8TVrfBmGwQVz4ZG/y7IMLtk+TJ62ndeCP6Aif9oPKTd8sT2XrfcFVADEbPrRU+Xu1zdffwB/+bbfM756Ag9884u46eqLcNFud0MHAn1evu6GWNC1y3Vjgfur63tmRm88KM6dCYFn+y/J1M42glEF5cKwny9AKsSPny73/duPnssP+fwuU+JrfJaiAXc3LAzDIJnUwTA6GlUO1daE+G5FmP1gnWy7Vl1slwOAX+Dg49juPYIOuJkTMgexYnzvwGv/BACw/TwOv/dnH8KRI0fsP+ERoCEEtYpzQQhNWUW9TJ5hcyl3iO9nPncX3vrH7wQAyJ0deNcHbuuuzZlAO3n5PqmDe8Q3lnA+MZHKi6IudVnMGHurtrCwcPvCwsK9CwsLt1txQm5gbmYKt/7BK42vZvCdL98JURRdGRigjg6ljAC5zSKalBGM9HZI2xMBhM5ywVwwFxmyF+M4YI9ROXx+hDWWDh33LGZRN9LidF3H95/J91Ufbr7jHmzf+w7jq59D8Iu47LVvxOKiuzGzQZ9pMFFyt3JIhxIHAz4AYmG2LSae9fd9PIs9M/1DbizbW7ujPx9NnH/0bAGPnJRQrHfwvadzI4feVo6TY8/tpoNt7rpxAEA4QEiPrjPdNtgEWwt+ge1aSToVhNBRNNSNdvmMi9GpEVHAzDZyPdIBt1Nn8Van7jrmjXXERUvC1LTzQQhSVYXcZsHxGqbi7nSsIiKPYERDMKpAbrOoFHnkau2zBkItGzIWGjwyPe9uxTfRTUx07j2UDJKddMGGbhCT5DYQwfplr7nC+Goagi+Iw4cP46mnnCd1qzk6nLdK1VAUOOyZHv4Z6ud7JrlDtSXjyMJp/OT5Ar7yyBKey/YPPUWTM+i0SeAAyy1C6bQRiUQwP59ew19lH/pCLFyOLa42+yu+KVPFd3sisKZBhvNmhqv5VO7w/M9HO3fouo77nivgCz89iSeWhgf7VBXIniDnNOuRii9gdFqMar1UZFe1Z5vglw8CxyIcdbbt2jYRX0re3EBE5DFtODvQzbLZM90MTdO78ePmjbVbHr4AkDJcGHuDifaDBo+EoioCLgUnBX3Eu37a5MqhajoyZ5E7LElkXfNL5L7rpsYXAJJTzg8mUi24W7p6MybEF0SwHo0FQWbzWMidEKLRqCsDAxVDJ9qVOew0yRzA4JwRtleD2Dc7gjwZnrCP/KCJcmF0T7HeUfDwCQnLZ2jb1ErzAIA3/8F1uOKaQ2hUHDZwHAFR6JGneplz1Q+Wrl23IrO9R3x3jBhAG4WdycCQ3GFP1893tM53NeRP+6AqLOKpNsQgIRkxDxBf0bRpmViabV04rTc0D0jNupggFRX5PqkDQFLApMZwIMLpcgttxUhKoxvr7e5WDVNT5D7VqrOot5zZtNKo6WBUhehC4h5FxN/btFDd7pmSUAu1DpqyinaTQbUkgBc0xKYVVyPjqcSn6qA+m2rBkx4wvZ0QXwN1qQgxRAjfpVfeiJWVjCvnUTGqhnl6c9vRI0/TYd9ZZQ4Uu5LBoTSw9Dlt8EIVqpzG3Z/5yrrPS9cBTb0AAHDxy1O44d234EOf+My6X8dqBH39scVukaeOoqEpq9DU3kOMtjEBYHvi7DIHCoFjsSPZT5LndrURjCiQcgJKmfUTVto9SO3o3Zi9IHUIDlbrJwNuWxKU+BYdartW6yraTQ4sq2NmykXyJPJdP3QqdQBGpzJS+ZLSYVDKCGBZHck5d4lvKMAiECZSpWzBGeKbM2o24ZjmihUYhblaT9fuTNX6FwqkY1egiW1zMljW3QAhOtRZKTtTNfeKvIhiQnwN/MXHb0f6HKLpffm1f4h//NwXXDmPiqHxzS8RskIvLoBUA9cCjmWw21QZvvn6A3jftRdBkb8DAHjk+42hNLHVIGUFtBok2jmSIATFzR0rhSiwvdQ9F8kTHWwrZQWoCovYlAx/wKhO+Hgkgmu3b9mVHNb5nnMx1WivXvEfxMoLhHSndpqIrwcqvgFff2JiY1Lx3ZKgrU+n2q4Zw7Q/EFERcNFPNCryiKdkcLyOcl5Ap0X+/sXlSl98s6JqeCZDpGf5ZR90nUFyrgNecPceLPIcxBB5Pj31tDOTibQrEI25e6+IiEK3Wp8zCh2ZShu11rCn/Qt5smkxe/gKHAvRxRAH2uloVDhHJGZNWUWjOiG+nkPApDd0SyuqaTpqBnEbjAYGMNKv9Uw4Z9pEfO8gaWIsfx8AgGFf35cmthZQV4D0OT0ZhBfIk8CxiBsTqrUyh3rbHZ1oT+ZgbFhMMof0KkNtg9g9Qs7S0/meXeM9CqeeJcdPn0NuwD6OdU0fZ0ZA4LqDTbWJpdmWBQ1CcMoPlhLfUFR1lXxERB4cB0wbnSEqb5OaMp7N9qq+P1+qdGUOVKs/s9395ExRYCG3TwMAPvsP/+LIMU+eIBuAQODsiaN2IyLymE4bUgeD0OrQ8fTAbIzUkLuODtkTVJvtbqUeAGYNfXa94ow+uyVrqJfd818exIT4GhiOLXb+IVxtK9B1Hc06i5rEg/dpiE6Rc2IYBnPRtROoXckgWMawD0nOwB8MQVMI0dW1K+EPhBFNzqz59ZaOUvLUI+JxDxBfAIhHWQh+DUqHhVRxh/h2rcxOD8sc5qLD4SBnQ0TkkRyoEPd0vuur+Oo6cPIZsmGaP4+8hhc2LADJjA+7vNmcwH1MGZq/quRMxbebIBVR4XNRJ0pJa9pwWqFe2wDwo2fzyNfaWCm38OALpe6/Lx/rubP4ec6184/H4wgEAqhJz5DzvfcBR5yQ7v/eAgBg6dgDth5nNQzqsw3rffzidKXP2s1sNblimttxm/jGoyx4QYPcZlFy4JnZaKto1Kj/svu00/0z8AjMfrA1iXdlSIo6OlDN0PR8B3TOaSbsW9dNzsezmI/3iHJNKuKKay5FMNoCkEZ+aX2Vw5NPE/K0Yx9plzNgEHXRSscMs843l2P62oROoTLg4ZsyVXzXs2Gh2D4ga0mf00YgrKKU9XUng9cCKSugXuERjCqIz5BzigU9QnzN+mzJ3cHECdzDlFHxrVa4If9pO5A32uWRuLsuIgEf8fKl3tqU1AKkNfzPD53Clx4+Ddlkk7X8gtF5291yVeawuLiIQ4cOgWVJ+BEvzNnqhBSPxyGKIl5YJBXmF57+gWuWowApTgTCGqJJmZBHY/ai0pLxiOF/X6x38AuTy07meI/4uh38FPRx3djilaz910G+pEPXGIhBFbGQ+7TT/TPwCAIC2zdh7sZDuNqi+t7hAIS5dbbLgf6W+Y233Ia3vOcW7LuM3GQvfsWH1/w6ug6cMIjvzv2E+Ib8HAQXk7/MEPs2LRzainvEl074ThnabIZhMBNZfzzjzgFZC8sC+y4jbbSnfzY6wGQUTj5DPjc79rVgNAA8U6n3UvjIBO4hZdIbOtF29VKCVEQUMGfIx8wV3zNhxVTxddOSMJ1OIxqNQtNI+VyRY7Y6IVGizbAkVpwXKq5ZjgK9av1sd9PSW7v7jxbxzV+s4P9/ZKm7kVNVIHvSsJT0QMXXL7AIUeLrgDx7JWty43BRXkThDebiAXhB40vJ06lnyX+jyV6U8EaqhjsTw23xbnzxo2uv+EpZATWJVA2Tc4Sce6VdDgABH9sfW+yCHywdaqA7f/o+xQP8hjYI2+KBrlSFYv9LCPH9zuefQ6W4trsVlTns2NsbbPMU8Y1PNL5bHf16Q/uvXZogRWcD3EQswHelDuaK7yi0mwyKKwI4XsfM9jZiLnfcstkszr1wGwBg9/4rkcnY54SUTqcRiUSgawkAgCKvuGY5CgAhHweWYbrSv4xp7XRdx/O5eh+HKC4blpIzMsSQ5vpgeEDoVXxzOfsLRVnjGMGIuzZ0FO6fgUcQGPQUdaXiS47/5AMnAQCZ49/sfm82sj6dKABMhX0I+vovMFo1fPaREJTO2jR1J4yq4c59zV7V0CPtcmBAny3xjjw8B1FpydA04uoAAIkUqfhOBzd2g/PxLFIDa77vJWTT0qxehns+/+k1vc6xJ8nmZ+f5JuLrkbUTBbbrEELkRe4TkQmcx4wRItGscai37b/vloqU+LpvpB8NCIinZPgDKmolHjXpzNWwzHERus5gZnsbvOCuHRYAHDlyBFdd9zoAwK79r7E9Onklk0UothcA8No3XGEr0V4NLMsg7Ocxt4tU65dXqdYPBlK5LXXw872KrxOJidSNIxRT4Z9UfL2DPo2vS9ZKN1y+FzddfVHXyuz5x+8ktmNvumzDZGXQPzaRUpA+p4V2kztjEtggji+Sn9ux31uODhSBgRCLpsPEV1aJh29N4qHILEJRpWtllhyDZJpdPG6+/gA+/Fv7ATwGIIQHv728qiVdp8Xg5DMBMIyO3Rf2pqC9UvFlGAaJOAOW09GqcyjXJhXfrYhIgIMYUqFrDHIF+8lo2Riio24SbiIW4MGywJxROfz79//PM3Zzlo5SfS+NHXf/Ok52/WDtpxKf/uyd8Ik7AADv/Yt32060V0MswHcj4CmxPROojIUGUrldrWcYpiv1yRfsHyr1krwImBDfLkSBRZhWn1yq+H74C/fixa++FgDZ1fK+kzjw2mvxP//1Rxt+zVFyhwsvrwIAFn+6Nq3o84+R19hzcc9ixyvkCRgIsZB4x4kvrdQXDZlDYraXvDS1wYovAGw3DSd2Lem4ewEALPfGVS3pTjwVgKowmN/TQiBM3hM/z3nCyowi5OcQihrvX9GdwcQJ3IVfYBGMkPttxoG2K02QmvZAghQlr3R2InN8Cvfe9amRP3tiYMDYC8UH+h5WHSC+LVnrJu7NuZi4RxERBczubINhdORO+c/aQT31HFm7bec24ec5T+hcYwbfKToQwFqk/ssuD5RSTIivAYZhMJXQwTA6GhUetaazxFfVdHCRKXD8HIAEgDKUzkmIwTD27t6+4dcd5f17wcuI3OHJByOrRuA2qiyWXxDB8Rp2XdBrlyc80i4HaPRtr2XecljjWx3U95qIb2IM4jsXE4ct6dRvAAA09dpVLemo529V+tduFclL6wZQfTZZu2rJ+Wr9BO4jIHDdtmvW5rarpumoGX6iUx4gvtEAj5uvP4Af/+sfGP/yMtx/95GR3ZwTT5F7+a4LmqRi54EAIUp86w4EIZRNiXvTSfepSyzAwyfqmN7WgaYyXceNUTj5NBlsS8wuuS5RoaBSn5IDiYmlEmsc0xv3d/c/PR5CKMB1Kw95h3LjKWqGh28pS25uqR06Xn7tIVRLBUyH16/vpYiI/FBlYMf+JkIxBaWMr2uaPgqVYg7/608/A11nsOuCJgQ/uVAYhvFEtYFiMAjBaY1vZYD4JmaJvjfi5+EfQ8gvcCxmTR7ANamIK66eRyDcArAP+dPRs/7+848R4lspfKlbRfKKvpdiMuA2gZ9nu4M2+by9Fd+WonkqQSoqCvjzO/4NF11BO3OXg/eJQ92cZo1F5gQpQMzvaSHi512N7KVITTvnyNEdkIqqCPjcpy70GUir9bQiP4hygUe15AdQxsPfu9V1mQNF0ti0SEX7P0dUXpT0gLwIALyxAh4B1YrWKzzKRbKDdcognJKnK655H47+HJjdyeKGd98CgHj4joPtiQDKzV4VkmWBC19WxUP/lsDjP45ibtdoTdm9d30K2ROvAtBzgwAIoeM8cNOlCA5E3zrt6lBrU6kD3dWT93pqjA0LxbZ4AMtloq2+8ZbbAAA6mnjwWyJ2X/jfAIxeu/dfdxVU5SSADoDv4v67q7j/7iPw+f2olMsjf8cNmPXZtTKPRkfBFMb7vE+wucAwDCLGZ6Bgc8GhJauoV8jna27t+T22gWMZbN+WRjjRALACYA5KZwfEYH83h7qzbDuvBd6ne8aLe5a4i6FRJd2a6NrDRdeNTJb8NxRVEPCAVIAOqO08v4mffTeO408F8MrrS30/c/P1B6DIbwDwNQALeOCbX8QN3/wiRFGEJEnDL+ogpqg+24HExLJR8U1MeYP4ur9t8hDMhvr1Mod6Zzh32y5QO6ziCrkpJ+dI1ZBjmbH1tNviw3ejF7+GGGs/8v3okNzh5usP4KarL8L9dx8BcD0A4J47r++23hKh/8vemQdIVpbn/jl77Wt39TIbPcPA4ADSQ4O4BRgwiqMoKEoSI0lcspHlxstVriG5WVwSg0quVxKiBhPFjE5Qk6AEETVGURl2YYZt9qW7a9+Xs90/vnNOnapeq/ucqtM93++foaqrTh3qq++c93u/531eb1x0TXwCa7k6VAv977pntivuljoMrXLBAgAbYnMtji58LRm7J34wd+xM9vzGQwA4MMz3AZQhSD5c8No34IEfP77qc3ISv2h35OBQo1KHM5Koof3LuZx9qjU11Mve6SAFkMxhtZhDYuQYAGDrBbegnO9cARx+hmSEtxjuLHGP7Lglohw4XofcZFEouXvdTWfMVtP9S0gtRszI3JqOOcfnyfjeevcDGCP/32EAACAASURBVNnyy8aj/RAkH97wlrcNzH/YjilTKS3iJOIEsqqhamizvaCrB2jg20F397Z6H+2VzOAp1+UDmwiIq97S2jhP8HT2y6sIx2VkTklWkwMTs5CKE14NYDOAk7joimFr6y0e8FZGTuBYq0K7UuRRb/V3Vdmt8TUzvk7oaUcjPjBdfr7bLqwimiRjd2gBZ46Xnh4HAOj6N8CLEpRWE5I/iHNWoRd3A79Nn10t8FTqcIbS1hu6G/jOZjVoGgPJryLigQ5SAJEf3XTbHbjiBuJYEAi/09rdMTn4CClE3j5Ztd7jBfwii0CYXP9mXM7Wm+4D4Zg3rhGSQIrUxs5qQJA0ZE5JqBY7g8hIYhi10ssBACy/H0qriUQsOjD/YTvDln82C0V1L9Zpyu3A17QuHDTemPkewS+wCNr1hn3MHFrOAKc7M75OZA2DEj8na8xy7azvT++Pd/zNLKRS5Tcbz3wd/mDQ2nrzWoEUAISCLHwBFZrKIF/o7+QqN5R5PXyTwdWPncizczycOQ645BfJNtlPvx2f855KgcPBR8IAVOy6Cvi9T92Dy974TlSLWU9pswGjY6I949vHXRaKdzAXroW8u7ek2TS5wQejKnz84LfLgXb2dseUzWO9rUzDqUMFnHjBD15ULcmZV+axvRGC+d26hSmD8YolFkDcjTi+rfN98cnOxlCNGotyYTvAqPitj/8qLnvjO1HM9sE4dxmYGvdqyV3v+7qsWm4cIx6QFwE08O2AbLva9YZ9DHybnZZYVsbXgeAJmN/d4ZVvJHqkx74bnWOcXsqVIEjvAwC87LJcx9ZbwmMZXwAI2Iqkinn3K4xNNE1HtTXXw5cB49gCYXyejP2lry+AYXU8+cMIsqcFlHJpfPaWm1DKpfHT++NQFQYvu6yGX/qfN2N86w5cf/NteP9tn/SUNhswOiZai83B+GdTBo9Z9OK2H+x0mvz+A2EVPsEbtz/zOhEfkTGyhXisH3mmvZPzb3//FAAgFHvSKjD2SvJB5FkEwuRa63YjBKvxSMJDga8xDucai5Zui9BDTwcAnceWHU1M7Dwb7/j9P8W+rw3Wf9hkKM6C43W0GiwKLnqo11ukbgoARjxgQwfQwLeD7u5t/cw+leoKVJW0BwaAeMrZwHc+ne/wxhbOu7QMRWbx3/+W6Pjbha/9HORmAiNbGvi1P3l7x9ZbwmMaX6Cz5XS11L+W06YbR7eHb9jHg19Bq+L5GIvODXzjIzJ2XVmEpjL4zpeH8eA9d+LIM4/igX/+HB6+j2SBX/WmToPGmEeqie3YpQ79XmyeyUxNTe0a9DnYSRiXn3KRddXLOWs0yAhFVcfm52qxS8de9goSQD3+/ahVa/HSU1sBAIXZu6yGRl6wMjMxCxMzrge+RoHU3E2ugWHupJoWoQf3h6DZ4vLnHyMZYFOiEvULc6Rrg8IvtGUqp2fdm3OZvA5VYSBIGmIRb8w5b5yFR+iwxepjIwRd11FpKiimBWgag2hShiCSH6IT2+XA/EVSAHDlDeRq9YN9SWROkUmsqsD3v0ZU6L9wXQ72eRoQeU+Yb3fjF1jbokXom1a028rMLGxzasECABuiPjCYe7F83a+kAch49LsRPHxfBrqu46f3n4NiRgDwtHWxNYl7NfC1SR2oxtd9pqamrgbwtUGfhx1z25XYYrl33c14zEgfAEI+HqIRhE+9jkiYHv9+FL9/x/ew45LfAvCLAOrgxW9i8so9+Ou93/NM8AS0CxOzLuuzLUuspHfGzsz4jmxuIj7SQrXI49O/dztKuTQ0Ffj5j4jlpClj8Yo2GyD+92ayyM3GMTMZU16keEZeRANfG/bitqphrdQPqk0Vmq4jN90pcxA41jGz66DEz1uUNnF+Hbt2F6DILP7l9g2oV1ncf3cK00d8iKdamLyy0/oq6cFsL9CV8S3yqPYpgDKtzLo9fJ3cipQEDsl5tN7JMRmvve4kyDT+BoB7AdwGQMO7/1gF2zW7vRj4+gQWYePGWaUZ376wf//+BwEcGvR52EkZ2r9ayd3aipxRIBX1iJG+iXltThm7cHKTxbfvvgAnXvhDAADDfhGqPANfIISJzRsGeapzMKUHOReL23Rdt9wHvNB4xMS8zjMMcMGrSUfU04dejQfvuRPPPxZCMSsgMdqynB+8IlEBSOBrdUx0MePbtqHzjrzIG2fhEeZYK/U5a2j5wI46HzwBC2d93/TeGUSHZBx9NoA/efsOfH/fEFhWxw1/eNrKPJs4mcl0Ep8tc1gr9S+AKnePnYOODnbm0/kCwJveU8PQhu8DCAG4DoCGiZ1344JXzx2n2Cq6yLkFwzCIRgBe0NBqsMiXvBWQUPrDSIoEpNUS56qbjqUTjXvDT9QkEWpfL970vhmIPg0HfhZGpXAuBCmP931kEy57I2lolPBQ8AS0CxPd7ADWUjTUSuT4XnEGAIjUgQGDW6+dxH/d+yoAGoB34eH7Hsfn/+QUAOCya/LWrqmX7p8Sz1odE91s2JUxrOaJrt4bGV/v3QkHiI9vZ5/6qTe0rMym3dsuB4jO9+enSnOe1/VphOMfQiRxN44/H0UkIeOtvzM9Z6sccE564TT24rZqSeibxnchqYPTW1obY348dWJu4wmWA0Y2fwSpTf+F0S3XInv6y1CVJwBc1vE6jmUQ9XnjotNNQCI630KaRblAOkB55QJ5pjM7O9vze1ZizK+1muDFOJQWi0NH0hDk1Td/me9cZo3OmH5/DbOzFUc+Y6XnYodpVlGtkuttMF7Fr952EN/buwG8oOPqd51AalMK49v/iLy4WcHs7Op3I51qoODzMQASyKbVFf1elnMupYaCcnEMACCJBczOzr03OUWv3wurNPAHn/0G/vOLd+CZH98DXXsXgOcAAMFoAxdeeQLVKokr1FoRs7M1186lV6QASagcP1Zf1nxYyfkcO07+9YdayGbyi7+4B1bz3dDA1wbLMogldDCsjnqFQ6XW3+3yrNG8ItnnjO+D99yJky9+E5e98Tfxmx//UwiSPmeb3MQJezU3INl60uGsWhRQa9X78rnlOVIHI/B12G5o3ND56pibqfq1P7F7fv76vO9PBkWwHtIF2gmIRFtfSAuoGjstNPBdOVNTU++f5+lDhsShJ1Kp1IrOodf3Mf4mgmEVxSyLlhpHKhVa+k0rOJdGlVxbN2zwOfoZKzkXO2dzNTyba2e6d+wCduw6aTziALRtsrZvGnUsKbLS8bWzeRMJmGpVaVXf6WLnohUbaFTINfWcs+NIpeb3L3eKXr6XzaMaVF5CKBKFrv0ewJwL6JeA46t494fTSA6TxRYDBts3j0HosajSiTFaiOQwGbt6I4BUanlt93o9n0aDfEZyiHX8/2Wlx6OBbxchiUMwQuyp8nkWsqr1/EPtFUvq0KXxdTp4Cko8EgERuRq5+JN2ii3r7w/ftxcP37cXvCDiY/82t8MXwzCe2qqxQzyY264O/dJnl+vyHA9fiecQlJydWn6RQyIoIlttruj982mEvYKvy9mh2lKRCC7xJsqC7N+//65Bn0Ov+Aw/2GJWwKxL7gBEJ0qu5V7SiQLLTyjwLOv4fWG1DJmNEIrERtKNrmrluop6hQPD6hhOemsBnwyKOJypolLI4ZV73oBL33Ac3/vq1yA3n8PWCz5mvS7q512PJXrFlPzksu6dlymB8ZINHQ18u/AZbYsrBd7S+Ub97v5Yu3WiCSvj63ywsjHubwe+dz+A//iHT+DnD38XcrMBQfLh/FddhTe995Z53xv3C56buCadVnQ8qk33s/W6rqPcVOZ4+EZdKiLbEPetOPAdDkkAVvZetwkIg9HWn6lMTU29nfwz9fb9+/fvG/T5AKTI0WyEYLamdZqGrKFmtSt25SNWTFDi4ReWLuxLBlffydNpzEC0WuLQkFuuBL6zhutAIKQiKHlrN8hMBtktP3/1f++Y87pkyBn5jpNYjWNcdOQo5Mmx4x7S1XszihkgfpvFR790vuWGglaDQSXPg+M1RBIKGDCuBFD2RhZmhzal1bTa2voCIatDWzfDYe9NXBN785HsqQZOT0+7/pm1lgpVm+vh65ZlzcZ5vJiXS8rjYxfsaBVOA1832b9//779+/fHvRL0AsTBJuRyoY29g1TKQwVSJsu5vg6HvbdzM2rsNhNHDneyetNm4BtRIXnEGcBkaJm7oF50REok3W8cY+6ymJ/lBbz1C/IA9haq1T5ln8oNpcMVgOWIt6MbBusbYp2esJVCDpe98Z1WW1t7h7ZuvHjRNRE41vKT1NQovvGFz7j+md1WZmZhm1vtRDfG5vfzXQqGYTyrzQZIti9k795G2xafkZjeuqblmNPUW5rVQcprGV9geYtTLy5gh5IsWFZHo8ahVHXnfmnKX4IRFX6P6f/jAWFZHTG9mDgaMiQ/Lz59FNMuJItaioZKkYzX0JDjh18xVOrQRb/bFtdaKhRNm+Po4FbW0CdwGA6LmC2TbW/79sz1N9+26Hu9eNE1icViaDQaADIAkvjxfd+Bz+eDz+dzrTK23Jjfw9ctDZ4kcEiFJcyUGz29Lx7wrkQFMB05yP9TpUilDmcq0ZipN3Qn8M0WVKuDVDzireAJAEYiazPwDUpEplIp8JhOa9g+5vxnpA1LrFBUBcd6a+xYlsFQcOnr8rAHpQ7mArBZ9+Ev/vIj+H+f+b+OHr8ht9sVD9GMr3cJCByCMbve0N3sU9myMuvS97pYwLA50XtFLAPGkxPX5MCBA3jl664FGJIa4IWNuP6Gd+DgwYOufeYc/2WXM74AsCnRu9xhNDK/m4dX6FhsFvrXfITiLWJGUwmzQ5fTTBsm/cGoAskFHepqWWqeChzrSTtJH99uhDCbduczshmj8YiHCqTspJZYtPgFzrFmVE4Ri8XwrjecYzwawuc/9w/w+XyIxWKOfUZd1lA1Mr4jw96RF3lv9g8YX1f3Nrdvwgs6OrhoUr4p3nvwlAiKrhQtOMXY2BhC4TCgzwAAFDkGfyCE0dFR1z6z28rMbakDAGxeQeC7nEzSILG3La7SjO8ZSzxB/i0W3MnomUFZMOy97XKA2Pottls0GpE8V9gGAJLAImjMX7cCX3MXIOahVtN2UkvIAEc8mHw4cOAA3vDmqwEoACKQfBHceOONjiaL6i0VtbIhL0p557fr3UhmQPhFzqY3dP8m3HZ0MANfkvF1M3gai/qs3vDLZTTq7eAJACqFLBKj5Dy3T74V0zMzrn7eXKmDDJFjERDdu6mORXofuzEPXnTt+O12ZgXatvhMJWlshZaLLDTN+W1RUyeam34S+czKGi24zfgiBayL/W2QCByLUJQEpGmXrOjypjOAh7bL7SyVrR/1YPJhbGwM0WgYAKnraTZCiEQijiaLMnkNqsJA9GmIh70TbnrnTDyCX2A7XR1ctsWyAl9T6mBlDd3bFuFYpuctc68HTwDwl//38zhn13YAwM5XvgMf+8wXXP28ckOZ4+Hr5oIFIHqyTT1IVSSeQ8KD1cR2SHGbGfhyqDVVVwIfircZNopfaiUODcX5zF7WqNutVw/j4x//2OIvHhAbF9mNW8lOXb+IGIFv1iVHjmLecAZIePO6kAiKizbdWajl/KAp5DLwh0jM8fo3/QZmHE4WzcyabhwKJN47uyw08O2iww+2HxrfpgJd75Q6MAyDiM/dYOWsZG86X69OXDu+rsyh2zKVcmOuh2+kD+byEz2M3YaYD4xHO7aZMAyDaJiF6NOgKizqNQa1PrWcpngHs8Kc+ME6O/6xWAy3f/jPjUcZ3HXXXY7rGZ1gc9w/73yVeA4jHixsM4ka+uycC36wqqajbFhiDQ15M/AFgA0LZOR5lvXs2P3jP9+D0bPiAIBrb/wj7N2719Hjd7pxeCfc9M6ZeAS/wCFsy/i6rvGtK6hXWDTrHCS/ikBYRVjil2WPshrOSgaXbY0VlnjXM5lOELAtWioFDlUXFy0tRUNTUed4+LqZqTc5KxlYdjC7WAbJS9gtzajO98xk1NAA1spLN3LolQMHDmDDxKuMR1n4/X7H9YxO4Bc5jEfnJhkmhgKe1PeaxI2is0LO+ZCiLquoFA1nAA9ZYnWzUP3FWMznijWpE/gF0qkWcEemYg98vdSG3pujMUBYlkEkCrCcjkaVQ6sJx7MPdsoNGXmbKwDDAFEXC9tMAiKHsWVmcTeuoKBqENjdAaouW9GZEpVTh+oAgHCc9CPvxwJhoZvjfHh5e9ROQLR3b6M63zORZIIBw5LrrtN+sGNjY9B0ktliuSKazabjekanOGckNPe51NznvETCyNabXbqcpN5SUS15zxmgm7MSgXmTSb3s0PUbiWetwDfjQuCbtQW+XiqO986ZeIiQj0MwYmSfSpxr7W8bsoqWqrWzhikja9gn25Ozh4PLet2WFdifDQJ/RyME98YNAEqGDd2j33kCAFDI/ARAH8cutfTYhX2C1U7T63R0TCzwqDZpE4szjaDEIRAiv4EZF9oW18pkLrzrd34N73vf+xzXMzrFOalQhx4y5hdW5ObST5KG9rZc4KDrzo5dw9ZxbyTl6KEdJeTj5zjoMGCwbWh599lBwLIMwmbjmJzzx88abhymFMYr0MB3Hoizg73K3J2bsJk1LMx2b5f3R1awbTi45JY5yzDYvEayhnZbLLezhi/fNo5brtmJY8+VAACnD/0nbrlmJ86fcMG9fR62D4fALjF2E0NrY8ECdLqpVIsc9fI9A/EJpBECAMymnQ2eFFXD8KYpAMCO8zfgjjvucFzP6BQiz2JqS1t7/MqtCc/r9IeNRghEn+1skFMoa2jWOXC8jqG4t0OWHaPhjscbE36EPObf203MxY6J5g5ALO4tbba3f0UDwm/zJay4eBMudVmZWRnfPgW+IYlfMpu7Ie6H5CFtzmL4xXbWsFrkUG+55w7wpf98GJNX7AHDbAUAcPwp7LryTThwoD+aQb/ILVmguNyMvheglmYUH8+1A1+Ht13rcrtd8bCHdaImk5uieOXWBHafO4yzPS5zADoDX6f12dPGIigYUeB30SrSCXaMhhAU24HuxZuiAzyb5RE3svXFgvPhoKn5jnus8QgNfOeh+ybsVuBr+cDOdra8jfRxhbhzPLzo37evoeBJ4FhEYzoYVketzENRdNcCKCGchBQIQtc3AwBU5QWEwmGMj/cn4wsAF26ILPi3iE9Ytg7YC9jdVIhMhUodzjR8Aotg2B29Yd22XW62afUyDMNgakscO8cXnuNeYtTQ3tbcCHwtSywVPg85A8yHwLHYvWMYPMviwo3RnqwnB4XpjWxaxjmFpukoFczA19FDrxpv/4oGhF/kEDZuwuW8e3pDUyeaH1DGFyDC+4Ws0wSOxfY1kG2wE/BxCIRNfTbvmrNDua6gnM+BYUnG99LXT6FedEEktQibEgEMLdBG+vwNEc9vj9qxF7eROUczvmcaDMMgHHVHb9iQ10aB1FplKMGAZXU0ahxKFWfnruk2EIx6s+NeN2clA/jN156Fy7evga0FAMNGYWK5yEF1cIe0Lquolcl4ec2Gjga+8xAQOITjtpuw6xnfdvMKn8D1tfqRYRhcvGV+L8sdo2FPVWIuh4DAIRAhCwg3ZSrlpoLrb/4MdE1EIKLghj+8Bf/njs+58lmLcdlEfM5zEs/h/LHFM/lewy9wCMfJWJE5586CxckLO8V5zCKYvMO2WNmiCqXFghc0JCJr65q2FgiIbZnKaYf12RmjDXLIY84Ai+Fl67luhmyNY5zM1tdti02vyYvWxq+oz/jFduBbKbi37VpqKGhUWdQrHARJQzCqIupy44r5eNloeE71v8CxmNrsLXP35UB0vsbYuZQ5lFUNtZZi2dD1o9veQkwMBbFtuDMr/8qtiTWjyzbxiyzCic7FptPV4UB7sUnxJjEr8HU2cJixdKIqfB7Xia5FJIG1nJBm084eO2MUXUU85gywXkgZQWm1ROpinKLeUlEtch2f4RVo4DsPfoHrvAm7tO1abii2drfEwzcygOCJZRn84nkpCDaT7VdvS3q+GnU+/CKLUIwEouWCO5lDK1PfJVEJD2DRAgBX7xjG1qEgeJbF1JY4LlhE++tVAgKHSMIYtzwPXXdHn12m2mFPY2oBSwXW0YXPjE0nuha2y9cafoFzzZEjnzWdAWjg6wZDSbbtn11z7ppbbWmolUkMMZLyVgZ87UU2fcA/R+rg/M2yKatoKiryM8QqLDYAfa+d4bCEt02O4+enShiP+XHuPCbqawHSvc0IoHI8qk3Z8c8wA99clw1dP4sS7Yg8iz0XeM+IvxckgUMwrIPjyQVYbjKotlQEJWe/03JDBrA27PnORJJGoU2lxKGpaI51e0pnyI03GFEhrZHt8rUEaYRAirOd7gBWMIquzCIsirMEJRaBkIpqicf0rIZtI84cN5vXoCoMGLaGVi0NwDvyO3oFmAe71KGc46FquuPd20pdjg4Jw9EhPMAs63BYwpXnDq/ZoBcAfAKHcLydOay4kOErdWV8EwNw41iPBOzzzoUmFi1FQ91hj1GKs5i2WJWCs22rM1nyr9c6SK0XGIZBxPCDzWacy+6pNmeApMcssdYLPlvzICdlKqYbh67N4q6//RvnDuwA9AowD36BRSiigWF1VEs8VAWOyx3aHr5Gu+I+d21brwTEdsa3lHNHplI23DhytlbTHMsgQLWDq6J7wel0YSLV93qfkRS5WVaLvKONEExzfjM4oziPpc92sG2xvV1x0mM60fWCT2ARCJsdE505ZiwWw6+8/k3Goyy+dPcX4PP5EIt5o26IBr7zwDAMAj62owuY05lD08qsu2vboHSi6wW/XergkkxlbsZXRsQnrCn7MC9iD3xLOffmHMW7mFrA3EwLx06ecuy4ZrGc11qnrieiRncuJwsTa7JqNR7xWoHUeoFkfMm1Nu1Qq/ADBw5g5649xqMM/H4/brzxRhw82J8GT0tBA98F6Mg+uRBAzde1jQEzUKnDesAvcgjF2xrfhqxCUZ292ZUbCnS9LVOJpWQ6bg7QbSPodOBLM77eJ5VkwDAKdC2ET3/ik44d12ydanapojiPqc8uFTjHOmZ2OAOkHDkkpQsf3874ZhySqYyNjUEDGTCGzaPZbCISiWB01Bu1KDTwXYCAyCGcaPuKVpyWOtS7u7bJCEocuDXk/+dFAl0ZXwCOb5kX6zLKeQ5Ki0UgosAX0Ki+1wH8YqebiuNzjga+niYWi+GCLUPQdSI0/Ma/fMuR7VFZ1VAxgqcELZByDTPwrZVYx/xga7bAlzYecQeeYxE2JEC5rHPHLRbIeO26/FK8733vw8zMjHMHXyU08F2AOc4OLmy7thoMqkUeHK8hHFcQGZCjw3rCL3LwhxSwnI56hYPcYhzNHMqqhrqszvHwpRKV1RPo3mVxfM7RwNfLHDhwAG992w0AQ4SGgrjJke3RDp2ox1qnrieGLD9Y3rHAN53XoCosBElDLEzDFbeIxgyZioNti3e+4u0AgI1nRXHHHXdg7969jh17tdBf0gJ0F9o4nX3q9vBlWeoK4AQcy8AnsF1b5s6N3cIevnTsVou/a9ycLkwsU42vpxkbG0MsGgF0khmSW1FHtkfrsm27fHjVp0lZgOEhkuFzshGC6b8ciirUf9lFEoZjRsmhwkRZ1VDMG7ssHmtXDNDAd0Hc1BtWmwpkVcPJF+sAgHC8Rv6lwZMjdARQDhdJFeuGo4NHPHzXEwGRt6QOLz15FOnZabQU5/TZxTrN+HqdXCaD5HgAALBz6q2ObI/WWioqRTI/Rx3yKKXMZdTQ4NZKzrWKnzVcBoJR1TFPZ8pcTO17ucg5cs21a7OHh71XUEoD3wXoLm5zMngyt1x/8u2fAgCqpccB0ODJKXw8i0jCHXeAhTx86aJl9fgFDhFjzjXrYTx4z52OjV1L0dBU3OnASHGOr351L3ZesgMAcP5lb3dke7TWUlEpkJswDXzdYyjBgmV1NGocSlWHAl/DV5Z03KPhilsM2doWO+GfTeYcuScOeXCXhd6tF6Bb40vcAZxJ2Z+7eQStZhPAxwAAs8cfwi3XfASSJKFYLDryGWcyc7fMW44de47/8ogMlmEQpB6+q+acTSk0mwyAOoBRPHzfXown98Ln86FQKKzq2GamnuJ9Ykb2KZ91JtDJGDpR0achFqbz1C38IotAhAQ8p2d1YNvqj5k1Mr6hiApJoHUUbpFMkn9rZQ61loxYYHXfdbWlolIgHTJHPOjGQZdQCxDoqDAnF8uqQ4L9vd/5KSav2AOG3QoA4PhTmLxyDx554hlHjn+m063xddLGygyg7B6+YR9PPXwd4JlnnsXkFVcBKACQwItjuOYtb3PE+5EWtq0dEkmyNVrIs45YEZ6aNnSiMYVmDV2EeKiTeeZUAX82Q8aL+i+7ixmcVos8ag5Yt9Zlb++y0KvAAnRnfAGg2nJm8gnhBKRAELq2CQCgKi/CFwhj25YNjhz/TMcvsO1Fi8OFicW6PMfDl0pUnGHjxg0IhMIApgEASisO0R90xPuRZnzXDslhs3sb50iL6ZlZ8i/VibqLT+AQMlrfmt/5atB13WqGkRiiga+bDCcZMIyORpVFqb76+2W+rKJZ58ByOlJJ74WZ3jsjjxAQOfgCGnhBQ7POoVlnHKsyLzYUVAo5CBLRsu3afSHqxSz18HUIH9+Z8a23VKgOGaqXG8ocD98QDXwdo1bMImJUGL/sFb+E2VkH7qCgGd+1xLBRBV4p8o7oDWdnyXWVOgO4i/26m06v/l5WlzUr6ZT0YIHUeiLk4+APqdB1BrMOdG+bNjL+oagCvwdlgDTwXQCRZyFwXbZYDlWqluoyfvmDfwu5GQfL6XjHH/0O/udf/50jx6Z0ZnxLOR46dEfkDrWWClnV5nj4RqiHr2N84K/+DhPnk52Piy7/NfzBRz/ryHFpxnftMGw0KqgWOUe2XbNGN6pQTIXI01ueW7Asg0ic3COzDnQAq7UUVI3t8iEPWmKtJ3wCh2DEIlIo1QAAIABJREFUyNannQh8yTGCMW8WJXrvjDzE3LbFqw98ZVVDtamiYGyVR4dkcBwNnpzEL7RdHcyMQdkBd4Bufa/p4UulDs5hb2JRclCfTQPftcNoypQ6OJPxNYOweIJmDd0mbuiz81kWur66AKrabNvQebFAaj3hF0hhItB20lgN5kZdKKp4Ul5EA99FCAhdLVQd0PiW6gp06JZGtN35iwZPTmEvbqsUeGgaUHEggFrIwzck0bFzCruXbznPO7Jg0TSS8S/l0vjdX3krpqenV31MinsMDxFbrHqFQ7GyusBX1XQUjW5UMRr4uo7ZtrhcWL0tlt0SK+XBAqn1hN+W8c1kVn88U+oSiqqelBc5EvhOTU3tcuI4XsOe8f3WF76C09OrL1UtdGUNYzRr6Dh+gYUg6vCHVKgKg3qZcyXja3r40rFzDr/AWl6+5RwPVdNXfQMtNxVouo4H77kTT+7/KT760Y86caoUlwhKnJV9mp5dXdawLms2P1G6Xe425ndcKaw+W19pqpaj0jgNfF3FJ7TnXC67+uPlDDeOcFzxpLxo1Wc0NTV1NYCvOXAunoNsuxqapdMt3Pelv1/1Ma3giWZ8XUPkWHAs0yFTKTnQrtbs/GX38GUYhmZ8HcQvdu6yAKtvNbx5dAi3XLMTD9+3F7qu46677oLP50MsFlv1+VKcxyewli3W6VXWNtZlDRWjg1SS6kRdZzjlXOCbyalQFRaSX0Us4r2s4XqCYxlEYmRHJJdnViVTacoqSgUSWprSF6+x6sB3//79DwI45MC5eI7rXnE2vvPl24xHo/jZf/7rqm+Y1na5pROlnb/cICDyba1ojndU6mD38A2JHFjqxuEYwa5xA1bvyPD17z+CySv2QJB8AAC/348bb7zREX9givP4BQ5BwxYrvUq9YV1u60RTHuwgtd4wtbgVB6QOp40N1mDUmwVS642Y4ZVcKXBorMJGsGprEZ5IenOxSX9Ni/D17z2CreePG482gBclvPVtN6zqhmkGT4VZW9YQDMI0a+go3fpsJ+ys8rW5Hr7UysxZ/ALXbjedJd/zasdOCCchBYKQm+8HwzyNRuNXEIlEHPEHpjgP8YN1xharLmuoGhnfkZQ3b8LribERMl6V4ur9001lYTjuzQKp9UZyyAx8V5ett7cIT3o08O3rXXslnpyrbVW6GoJ+H6RQyXi0EYrcgs7yYFl2xf6ix2fzqLZUZKfJVy+FS1BbCnLZ3hTlg/xeuvHSuQDkfJSGDl8oACCKzLSG6WwRMzMzK+6w1lQ05IollPM8lBYLf1iGqpehNf2L/ha89N2shXOpNRSAL4HjNdQrHPK5Oo4HM9jkW3nb6WPTeRQysxje9BtIHz8fl1zyCzh27J6OcfPSd3Om4xdYK+O7WlusaktD1cg+jY7QnRm3SURZCJIGuckiW1xd4Gtm+0nHPRr4uo2pzyYOVjKSEFd0nGpLRbUoAWhLX7zGkoHv1NTU++d5+pAhceiJVGplniQrfd9qaQl16MpRAIAUOBcXvPZ65AuFFZ+PrGpghAok6CjnBTCsjrFNPMaToRUdc1Dfy3x46VwAYERjkDRudI1yAP5AAIFocsWSkplSA8FgFZljpP94clRBMBjEppE4UqnEou/10nfj9XOJKRpCL9URHVKQmxah1KNgJH5V560fbuI9f/b/8LnbNiN9HPjQh27Enj3vWNb5UPoPwzBWi9pygUVL0VZcIJPJA6rCQPKriIZo8OQ2fpFFKKogPyvi1GkdePnKj5VJkzEnzgDU7tNtRkZNRw4e1WZjxcepNhUr4+tVG7olo4D9+/ff1Y8T8SIBkcOv/9mf4X+/RUezJuL17/4QXn3+yrdHi3UZOnQUMwJ0jUF0SAYvUH2vGwREDrEhIispZswtc3nF33W+Nr+HLy1scxaRZ8GzLKJJEvgWMzxKq/Dg1TR9jjZ7yxZvZiEobRJGUQzZMleQ4FeWfZo1pBJUJ9ofAiKHUExFfhaYWYUJUkvRUMyR8YrEVfAcHTu3GTWcM0jGd+XyMrvGdzTlzV0WJ1wd3k7+mXq7A+fjKQIiB5YFIgly4yzlxFXdhAs16ujQLwIih6gV+BpFUvWVT+bCAh6+dOycp2PssgJKDWXFVcbFhgxN1zu02Zs308DX65gODNUit6rGQekMyTyFYlQn2g98Amc5cqxGn11pKu0CqSFvOgOsN0x9drXIoVRb+ZzLFlTITRa8qCEe82bgu+q79v79+/cB2OfAuXgOiWfBMgwiQ2TrppwTV1VoY2YNc9MkexGjjg6uERA5RIfJWBWNIqmCA4uW3GmjXfEo9fB1i4DIIZI0xi7DQ9N1VJrqiuaJOW6VAge5ySIc1RCJOHq6FBcYHrLbYtVXfJxstr1dHqCBr+v4BQ6hKNkmz6xCn121F0hRG7q+EAlwCEQU1Eo8pmc14LyVHcd04whFVQRFb845etdeBIZhSACVNDO+AkqNFjRNX5GFlZU1nCaBWHLMyBrS7XLHCYiclakv53ioKlbl5WsGUFlz7Eap1MEt7BnfUqa9aFlR4FvvXGyOb1p9C9y1jq1uY9v+/fs/ONCTWYBhw3qsWuRQXWHzGV3Xkc+RG28wqsAn+Jw6PcoCiDxred+X8hwasrqiTHu1qaCSJ/UUw7TxSF8wG3bVSjxOT6980WI2xvSyGwcVziwBCXzNTlIidF1fcdY3XzWDJ3ITTlpZQyrcd5qAyIEXyBanpjGo5PkVSx10XZ+TrU+MtiDxnCe70qx1OqUOJNgtrjBbn6t2LjY3nOGBr9Fw6EGjdmOr8dhzjBmlFERvuLIxq7VUlAuGNClJdaL9wmxa8OP/+C4OHTu5omNUmiqKOfLfftGBVmKUJfELLMIxo2PiKrq6zxpBczihIODRjC+9EixBQOStbddSjlxEV3wTrpFAN3fa0PiOUZ2oW5jbmlFbgZsZvPZKuaFA0TSoKlCwaXypzMEd7ItNszCxsMKxyxtzLkszviZbAZjB7iHjsedIDbHgeA2NGodsYWVjVmmqqBiBb2KY6kT7hendWsrq+MRffXxFx6i2FBTTZMwe+o/POnZulIXxC5zVPCidZqFpvWfaG7KKgrHL4uWML71zL4E9+1TOkpvnSgLfckOBrJKJnDV0oskxmjV0C0ngiDvAkIKTL5LMYVOpr2jrzQyYi2kBmsYgmpQhiDpdsLhEQOQdz/iac45kfM/ccety6dkFYO9irx+U93q9UkcoFkUxI+H5FyuY3dz7MY7nGyhm4gCAYKiK2dnmqs9rNXjNK9qN89m+fTuazV8A8ACAEXzln76Ar/zTFyBJEl544YVlnQs5hgpABqDigW/cCZ/vs0sewym8NE79PBdZ1SCFJABRZGdUHDs1PSdju9T55GoystNGh8xQHeV8GZUVeucvxWq+mzP3DrBMSPaJXDBLOXLzzK/gJmxmnhpVFtUSD17UEI4rCPsk506W0oG/w9mhnTkcjfYW+FqZemO7PGHqe2ng6wpEn62AYXSU80SfvZKMb7WpoKmQbKG5y0IzvoSpqaldAB7bv3//Y4u9blDe6w2+hkhSQzEDlMsr8zmflouol8lNeMtZfqRS8VWdkxN4zSva6fM5ePAg3vauT+GxHwHAKCSfH9e99S34+Mc/vuRnmX8/ePAg3vbuP8djPwSAWfh8Prx1mcdwCi+NUz/PZWikCABoVgPwR3ikInN18YudTy1bQ6NC/ntkA4uRkRFXznM557IY9M69BEF7oU1u5duu2aoRPM20rcxYlroCuEnQ5uVbMCzNCnUZo9HeilxyXdps6ujgLkGRA8cDwZiKSp5HOcdD4OWei0pztnnaljqsvnW111lm06GrvVrYBhBbLFKc6sfsDANd13vuuli1SR3GRqnUoR+MjY0hmTTn2DhazUbP7cHHxsagYYw8YE6j2WzSFuN9wmxbXM5zqDRV9Bq2VpoKynlyf/VyUSK9cy+B3VqpkhegaViRVtQMfO0yB4Dqe92EyFQ6taIrGbucuWgxA19z7KijgyuY22uxIZkUJWYFxIYVFBsy4oHlNzIwx01uMShlebCsjtTY+g+Almo6NDU19f79+/f/tfHfV6+kC6fbBAQO4bix05bnUGupCPY43yoNBeW8Gfh60090PdKoHQHDKND1OHa/5dcw02MnC1XTkUuTa8DEzjG85pJf7/kYlJUxnDIDXx7lRq3n95cbCko5Mk9TI9691lJx6RIERB6CqCMQUaCpLCoFDpWGAkXtbVBzlW4rM+ro4DZBiW9b0RkZX1Nyslx0XbcFvt2NR+jYuYEZ+JoLzoI1dr0tWszFZn5WgK4ziKVk8Gf4WsVwcfirqampl6ampvKDPp+FINZKhi1Wjkel2btEZSarQZVZSH4ViRi91fWLT991N6KG9+7rbvwT7N27qIx8DtWmgqmrfxsAkNrow+2f/HTPx6CsjJSR4q0USMfEXqk2FZTz5CI7NubkmTnLGX4bWBrzJhxNEn+7UlZAJNFAviZjOLw8fa6u63MyvqZOlGZ83YNkfIn5fcHM+FZ7C55KDQUtsyixy4aOjp078BwLibf5ZxsNSHJVGVuHln+cbMV0UencZTmTMbK7gxe7LgHHMkgMkWC3nCc34RH0Vg9x6hQJvrxsq7Qe8QkcwgkFhbSA6dO9Z9orTRUlo6g1nFDgF6n/cr8YGyFzppzjUV5B4FusKaiYga+HlSl0GbwE1rbrsKEVTZObsBnILod8TYai0eCp3wS72hbrOtH4qj3YtGQq7XG229DxLEtvpi4SFDlrzpmthnM9zDld162xy3YVJVLWBkOGBVk5t7Ls0/SM4SfqYVul9Yi9eVA2w6Ip95atLzcVlIzgKT6kQqD+y31jJMWAYXTUyhwKld7n3OkZHZrGIBBREAl6d87RX9QSiDwLkWMRN7a38zO9a0XtQbK1XW54+NICKfcIiBxEnw5/SIWqsKgWOWi63lNxojl2jRpx42CYBqCfpgsWlwlInDXnCisIfO2LzVzXYpOyNhhKGdmnPI9yj02Dai0VhWzbT9RPA9++YfeDLWV7zxyWG7KV8TU1p5T+EPJzCERU6DqD09O9Fafpuo7TRuOLSMLbc44GvssgIHKIpwyNrhH42jOBSzFbJkUamgrkZwypw0gLIsfSTISLBERy8VxNtj5tjJ057rp+GN/9lztp4OsyAbEd+Jrffa7WWrapeto2P7NWpp4GvmuJ8fGVb7tWbFrDCJU69BV7QXg5L/Tc6bTcUFA2CqRGqBtHXwmI7UXL7CzbUy1TtWux6eU5RwPfZRCUeMRH2oUyAJCtLN8M3QyeilkeqsIgkpAh+nTqA+syQYlMPDOA+son/g6lXLq3wLfSwq3XTuJTv/MB45lDePi+vXjnKyYQi8WcPmWKQVDkrcWmucuiavqyd1rMOQfYM75U6rCWGDUKbcoFHsXqyoMnkvGlt7p+IfIs4klbYWKPgW+poaBoWIeOerhAaj3iF9qLlmKW62nB2THnaMZ37RMQOSS6bsLlpoLGMrVLs+X5C9uoo4O7BAQODBgkjEXL7HEND95zJ9LLXLTUWyrKDRm33v0ANmzbYzx7CILkw+vefD0OHjzo0plTghLJPHC8hmqJR7NO9JrLHbsZI/DVdXvjEZrxXUtEghz8IRWaymA63du2a6khWzrRWFIDT3WifWXIkCiUcnzPGd9CRUG1wIFhdIx7uEBqPeK3ed8XM0JPEqNSo73LQjO+6wCS8TULbdo+ovas0kLka61296guH1iq73UXlmXwobdM4r+/+RfGM2fh4fv24h2XLi9bawZPkcQwFGUTOSZ3HEqriVg0Sg3VXSQg8mBZzJl3s8uYc5qmW3OzWuTQrJMAKhCm26ZriaCtSGp2BlbL9+Vgzz5RnWj/GRklC5VStrfAV9d1nJzWoesMgjEVIb93g6f1SIf3fY9jZ/fw9bq8iAa+yyAocghGVfCiinqFQ71KvraZ8tIZpJlS+0Ztag2T1Mqsb/zN3u9hYqfpgXUWBMmHySv34NGnnlnyvfYgq1pMAAD2vOc6XPbGd6KUTbtxuhSDoHHR7JY7LCfwzVZbVpCU6+q2R1k7dHv59pJ9KtuyT6mUdztIrVdGR9v67FJ9+RKjaktFwWheEU3Ing6e1iN+gUM02W761MvYFesyyoZEJZZUIfLeDS+9e2YeIiByYBgglurU+c6WG0u+93Sx/ZrMSeJDOTRuZBJp4Os6GzaMIxQvkQfMViitJnyBEOBfOuN7yjZ2ou8iAMC5Fw/j+ptvw5e/8i+unC+FYOqzEyOdlmbpcmtJOzr7uGVOdS42KWsHuztAOd9b9qlYb2d8x8Zo4NtvRkcYMKyOaolHrtzDdnm9bWVG/Zf7D8cyGBohi81ihkexJ6mDbC02TStCr0ID32VgtsqMDZOA1cw+nS4unX2y34RnT5Ds0/BG6uHbLwIiB6X1HACAF87BK655J8r5LGZKiy9aNE3HjDF2SotBflYAw+pIjspgGcYKzCjuEDQcObqdHRRNW1Ji1DnnyGJzeOPyi1Ep3iAgkkYIgBH49pB9yldkYj/I6hhJ0XbF/Sbs5xCOkbHLZphle/mWGrLVsCaS9HaB1Hpl1Gjr3nvGV0HRsKHzugqQBr7LIGg1sTAyvsZNuNZSFvWErbVUq1OYpgKZk0bgu4G2K+4XQYnDb/zZx+APqVBaPF7/q/8HN912R0dwNB/pSqvdse20AF1jkBiRwYs6wj4eDENvpm5i+mebhYnmnAOw6Njpuo6T+br1ON212KSsHeyNEErZ5Wefai0VuQy5tQUjMtWJDgC7pVkpK6BQX97YFepyWyfq8QKp9YppI1jK8iguc9xUTUeppqBotJffuMnbuyw08F0Goe6Mr63A7UShPu97AOB4vg4d5AdQSAtQZBaRhAxfUIPAsfDTSe06ZubQDKDMzOFMqbloscxxe/BkSFSGN1CJSj8JSjxilsa3PefsY9NNutJC3ZZdShsZ35SR8fV5WHdG6UTiWcRT5MZbSAsoLjP7VKzLVovyyFCLBk8DoKOJRY5HoYexswLfpGIlnSj9Y3iIgSBpaNQ4FIs6aq2ls/XFuoxqiYWqsAiEFcTC3r7OevvsPILIsxA41tL45mzZp2O5hW/Cx3I167+pzGEwhEwvX0PjaRY7abreob/u5ni+PXbpk91jRzP1/SAocZbG1z7nThUaCxqrH8m2x03T5o4dXbSsHRiGweg4GedCWkBxmR7OxbpsZZ4iCRkBul3ed0i2vh34LnfRUqjJljY7mlQg0bHrO0GJQzRpWJpl+WV1Os3XZKtBVHTI+5l6Gvguk5DEI2pkfM0WqgBwPFebt9hG03QcztiCpy6tIZU59AdTn53oajkNLLxoacoqThXaQbG5XT60gQZP/SQo8ggnFHC8jmqRR6tB5CWKpuHYAlnfQ+mq9d+FtAClxSIcJ7ssAF20rDXGN9gC34YMXV96CzVfk1E0M77JltXBkdI/AiKHqK1j5nLbxBfrihVAjY15u0BqvRIQeZulmYBCfWmZWKEuo5Bpd0r1eqaeBr7LJChxltTBbEQBAC1Vm3fr9Vi+bvn3AnO1hhE/vRj3A3MCdksdAOBQpjrvew5na9BsN9iMJXWg2fp+EpI44uVr7bS0592Ls3PHLldtdTS4MOdcahN5PwOGjt0aY2wUYDkdlQKPZgPLcnYo1NvZp0iyBb9Ib3P9JiByiFuBL498bengqdJU0FRUa+w2bPS2TnS9EhQ5RMyMb4ZfVrfMQk1G0cr4et+Gjl4RlklQ5BGKKZD8xMu3WmoP7IHp8pzXP3u68zkr40uzhn0lIHJgGcbmDtAOnop1eV53h4MzlY7HbZkKzdb3EzNbPzRO5oxZHAoAL6Yrczon/vxUqeNx9y5LSOLAsbQocS0R8rdvwoXM8rZdCzWb1CHZsnT+lP7hEzgkRtr67OUET/majEaVRb3CgRc1pKgbx0AIiBxiRsa3lBGQqy49drlqy9pliQ0pnt9loYHvMglJxMt3vpvwoXS1Q8OUr7XmZBPTNHgaCAzDIChySBoNDLKnxI6/P32yM1jKVVs4YZNA1MosqkUegqRZVco0W98fzKJSU2KSts05VdPx2LGC9bjSUOYsNmePd2mz/XTOrTWCIm/dhIsZAbklMoe6rhuBr9Gmelj2tJH+embM1GfPCpBVDZUlsvX5assqSowPy9QyckCQ7m3txWauunS2PldtWVIHmvFdR3TfhDO2AErTdfzwxSx0XYem6fj+85kOLVqzzqCYFcDxmqU1pRnf/hGUeCTHZDCMjvyMAMW2gH1uptIxsX9yOGc5cQDtcR7e0ATLEoNvr+uX1gtmYaI15052LloeO17EkWwNTVnF/c/OzHHp6N5lidI5t+YIiBxiw+0mJktln4p1BYqmIT9Lbm0BP+2wOCjGx8m/xawATSUdFRcjW21Z9TOxlPeDp/VKtxVduaGgpSysty43FLRUzZI6xIa9P3Y08F0mwUWyTwBwOFPFvsdO4auPnsSJLs1v2urY1gJr/B5o4Ns/Qj4evKgjPiJD05gOjbam6/j2MzM4XWzg4UM5vJTuzNTPHje3y42soUQ9fPuFuUU9vEDgq+s6/v2p0/iHHx2d49BRyqVx+BkiWUltIrssVN+79rAHvsW0sGT2KVttQVOBktE69dHv3On6OVLmJx5hEYop0FQG5TyPzBJjl6m0UEjbC6TofB0EZM6RwPfFJ0+imJtddNFi1lWY2fqhEW+3KwZo4LtszOzTQjdhAJguNTqKa6znj5DgqVL4IUq5NCSeozYtfcTM1pvBa/eiJVdtYd9jJ7H/aH7Oe82xG9liSFTodnnfIK3CGWvOdY+byXyV/vd/8W6oyggYtoW44QUcpWO35rAHvoUMj2y1taizw+TZ4/jgm14H6ByAGfzo/q/A5/MhFlu6RTnFWTqy9WkBmXnujSa6rpPtcitr6H1LrPUKwzCWTKXVGMKD99w5b1xjki43oamw5EVj494vSqSB7zJZrNBmKU4f9gEAqqUf4sF77kSUakT7SnvRQiavuQW+HKaPkrEbNQJfGjz1D9aQlUSHZfCChnJeQKO6+CXr1msnccs1O/HIAy8AAHTtKXzwTTtx67WTNOO7BgmKfIctlqxqizZD+Ow3f4hzJn/JeHQCouTDjTfeiIMHD/bhbCl2AiJnNaAppgWkywtnDctNFS1VQ962XR6iGt+BEIvFcOsNZwOoAxjCw/d9C5duG1lw8ZiptFDK8VAVBuG4jHjU++NGA99lEjTcAczitMwpEcuwlMSt107iv+7dbzx6Cg/ftxc3veZsmoHoI3MyvieWv2gxM75jE2QrnQZP/SUk8WBZIDk+V1s/H7fe/QAmr9gDlt8FAGDYZzF55R7cevcDiNKC0jWHfdvVzAZmKgsHUKoUg6ZtBgAwzFHIrSYikQhGR0fdP1lKB0GJR2yorc8u1OQFtaKzFWNxY9P4UqnDYDhw4ABe8/prAeYIAIAXduCy11274OJxutS05IOJUe97+AI08F02DMMgILIIhDUEowqadc6yzFmMW+9+ALw4ZTx6GoLkw1V7rqMZiD4Sljq1osvN+NYrLIoZAYLULkqkBVL9JWR83ylj0TJzbPGxiySGIQWC0JTzAAC69iR8gRDiQylaJb4GEXkWwyPEts7cSp0pzb/tWm+pKDVklPJhAMCu3efjzW//JczMzPTnZCkdBEXOspHMzwrQoWOmPP/YZaqdDYYSVOowMMbGxhCJRAD9EABAkTeAEQOIJ4fnvLZYl1FrKZY/fmJEtnbHvQwNfHsgaOhyR88ik9eUMCwGx49CaSUBVMAJp6C0mojHojQD0UfaGV8ybrMnlpetnz5q6Hs3N9tFiVTq0FfMRcuokXE/fWTpRUulkEM4fiUA4LxXJFDOZxHxCbQocY2SGgZ4UUO9wqFRYxdsNT5teHKfdd5bAQCbd4Txodv+DHv37u3buVLaBEQeCdNG0sgInirM33FxuixDkUmBFMPqGNuogKWe2wOjnM9iZAu59m457zqU8xmcnGfenTTGMzdtZnxbNOO73jAzRmNnkR/A1z/7TZRyi9vlmFvlodg0fv/TX8Zlb3wnirmMuydK6SAokSKp6JACf0hFrcSjnF96VTp9hCxsRja3sxQ049tfzEXLmLHYNMdkMd79x3dAbm0HANzwB2/HTbfdQbXZa5iQj28XuKUFpCvNOdZ1AKyA2J598gv0FjcoghKHoTEybtnTZExOFuYGTw1ZRb6mID8jQtcYxIZkREP0OjtIPnXXF3HJ614OANi4fQ9uuu0OHM/N06HWeM6ac6M047vuCBmtL81Cp/xMHA/es7hdzokXyY1652VJjG/dgetvvg1fvucr7p4opQOGYRCSeDBMe9Fy+vDSmUNz7DZsI++hbhz9x9RUm7ssLz5RXXKxmZ8R0KhyCMUUhONkm5w2HVm7BCUOiVGj8+JpAaqmz5v1tW7CtuwT3S4fHEGRQ3LMlDCI0FSyOGl2dVw8mqtDh25lhZPjLWvBSxkMQdE254z5dDhb63BUUTXdmnN5mvFdvwRFDrdeO4l9f3u98cz5ePi+vbjlGlI1Ph8nnvcDADadQ34grBGEUfqLWSE8NrF8mcrJF4zAdzu5ycZo1rDvmHMlOdoCyzahKiO4/4v/tOh7jhtzbuP2doaCjt3aJSjylpvON/9+H0q5NI5kax2vqTYVZCrEw9csgkuOKvDxdLt8UDAMg3iERSQhQ1UYFDICNF3H4a6xM73Tzaxwcox2bRs0QYm36lpy02Rcyg3ZKkIEQJoHKWrHa0jG1/tjRwPfHghKHG69+wFc+JpNxjPngRdDVtX4fJwwgqeNRuAb8fFUuzQAzBbRpjvDqUOLZ3zlFoPpoz4wjI4N24yxo1nDvhP28bj12kl88E07oWlPAQAeeeD5RRebx54zFpvntgNf2jBm7UK2zEngm5v24cF77sRL6WpH9unFdBU6dBQzAjSVQSQhIxZdae4HAAAgAElEQVTmqK57wISMrplAu128vbV4raXicIYEvqZjS3KMZnwHTUjikTDn3IwIzVAWPX263eDp8eOkZbzcIp1pWVZHdEheE2NHA98eCIksIolhBCIsgEMAJCitCfgCIUQScysea2UWmVMSeFFr+8AGaOZpEJhb5mNbTanD4hnf6cMSVIXB8MYWJD+5wVKdaP8JiBz++IvfweQVe8CwzwAAWO7SxRebz5Ox3bS9vR1Ox27tcvn5W/Bvd73XeLQND9+3F7+9+1xEDUtIXdfx81MlAG2tYXyN2Cqtd4K2AKqt863jeI5kfR85kodmLGAsqcPY2tguX8/4BRbBkI5QTIHcZC0Hq2OFJh4+lMMPns9YciOzsVByrIWAxEHgvB9Wev8MPYS5XV4p5JAcmwYAbL3gZpTz2Xlff+IFknka39oAZyyC6A14MFha0S1NMIyO2eMS5NbC2aATL9Ltcq+wccM4pEAQuvYwAEBTdy242NTU9tiZ8iKGYei8W8P818+exHmXbDQebYcg+TB55R589J6HUGupePx40WplbHp0D421LCs8yuAISRySY53ODgDwrZ/P4N+fmsZTJ4vWcx2B7xrIGq5nGIZBUOKRMp2Qjrd3SPcfzXeMW9r4W7X0MJRKrr8nukJo4NsDIsdC4jncdNsdeNWbtwIAhsZvwE233THv648e7LwBAzR4GhSmLZbo0zGypQlNZXDyxYWzvkcPmGNHs4aDJuzjUSnkcOFrSYZPCly94GJz+piEVoNFfKSFUIzoz0ISD47Ki9YsWzdvQDhRBqAB2AK5qcEXCEEPxPCFHx3Fj15q/xZmDY/u1OYmzRp6gJDU1mfbW463VA1Hsu1tc1Vt60STY7J1vaYMjpDEIbV5buDbzcxxMq618k/x7X/+bF/ObbXQwLdHzMzh5h0kmDWD2/l46akgAGDrBW0xPw18B4O949qW88jYHXk2sODrDz1N/jZxfvviTFtND4aIj8dNt92BX/7gTRB9Gpq1Ebzt9z4z72tfepLMuYnz7XOOjttaJijxqJVnIfmzADhcdPnvWAsfHZ2G3GnjJpzaSHWiXiAk8ZYd5GLNZ/LTPigyi1iqBV9Ao+2KPUBI4pHaRBYts8fn75h567WT+M6XHjIePYvv3Psl+Hw+z3empYFvj5hFMhu2NcDxOmaPSWhU536NcovB0WdJULzNFvjSrOFgiPh4MCBZv7NeRsbjyLPzL1qOPVdCflaELyBb/rEix9LttwERNgoTOa69e3JsgQXni0bge/bL2wuWWGD5Laop3oNjGfzWn38Gm84lYz555f9YcJfNyvhuatLA1wOEJB7DG1tgWWJXJjfn33mZPU7GdnRLk+ysUtvIgRP28UhtMjK+Cyxabr37AfiDlxqPDkLy+XHjjTd6vjMtvTL0iNm5SxB1jG9r4Phzfhx5NoAdl1Q6XnfsoB+KzGJsooFglGy5sgxDq8sHBM+xCIgcqi3FCnyPHghA14Huwu9v/+OjAC6DP/QkWI5kfumCZXDYM+1bzqvjpaeCeOnpIF52WeecU1Xg0FNkvDoCXzp2jlAqlZDP5yHL8qKv0zQNpVLJ0c++NKHirk8fQaPGwh9S4QvMbWChA/jSF14EAMRTMvRiAwXojp/LSnHje1kugiAgHo+TVrR9JCRxEEQdyfEW0ickzB6XsOHsuR7M9sCXLli8QVjikdpErrHmgrKbUHQYjTrR33PCYbSaTUQiEc93pqW/sB6xB67nTFZw/Dk/Dj4SmhP4vvAEyTxtu9C+VS5QK7MBEvbxqLYUJMdkhGIKKgUe6ROitZ1z67WTUOQWgC8AAPKzX8Et13wSvCDiaz95YYBnfmbTMed2VfDQ3iEcfCSEN79vpuN1J1/wo1HjMDTeRDylWM/HqJOKI6TTaWzYsAGSJC1qEybLMgTB2e+8UJORyzHIzwgIhFWrFW7H57ZY6E0JgqhjZHMDQyEJmqo4fi4rxY3vZTnouo5ms4mTJ0/2PfANSmSnbXRLE+kTEqaPLRD4HiOB78iWJoI0OeQJghKP2LAC0aehUuBRLXJzIsbcjABdEyFIedz8yb/D7M++hZmZmfkP6CGo1KFH7DfhHZeSYPfAIyHYLCWh68BTPyQXGHtATG/Ag8XM2jIMCaAA4JmfhK2/33r3A7joF64FcC0AgBe+Z9lmxenYDQzTgxkgMhVfUMXsccmyRzJ56r/JWJ57cbXjeZrxdQZd1+Hz+QbijcuxgCiSLO9CbiyK8TwvamDAgOYYCAzDwOfzdfge9wuOZRAQOYwYdp4zR+bPHKbNjO9ZTbor6hHCPqPbqWEBemKeYnDTuWrbhTzGt+7A33zqU9i7d29fz3Ml0MC3R+xb3pvPqSMYUZCbFjvE36cPS0ifkBCMKDj7ovZNmAZPg8XegGLnK4mJuj3wjSSG0WxcAiAJ4CAU+QnLNosGT4PDL3IQDW9Ijm8vWg78LGS9RtOAJ34QBQC8/PK21Q7HUnnReoBlGPASCdyeefpJPP3kE3Ne02qQ34gg6mBZrDpALxQKuPfee1d1DAoJoEaNVvHTR+cGvkqLQfa0BIbRMbKpSR0dPIJ53dxkWHqaHTHtHH++7X7EgFkzY0cD3x6xF0mxXDvr+8T3o9ZrzBvwBa8pgbNp9GnwNFjs3/+5F1fACRqOPuvDqUMF6/npoxcBAC59A/DKPe+0qsfpomWw2BecOy8ji5bHbXPuyLMBFDMCYqmW5dphvo/Ki/rP6dOncfXVV2N6enrFx3j88cfxuc99Dt/97nfxz3f/I44fPQxe1BEMRvFv935jzuvlJrmdiT4NrANZ6Vgshn379s17Xueddx7uvfde3Hvvvbj99ttx6NChJY93+PBh3H777cv6bPN4hw4dWvZ7vErYx2PDNhL4HjvoR3fi+cRLPmgqi9TmJgRJ73DgoQwOn0ASDpvPXTjwtZoFnVOHX+TAr4HmFQANfHuG59iOXtSXvI4ETT+5P45Wg0HmVBY/+FcycXddWex4b5xWlw8U+5a55NcRSTwGgMW//u0RAECjyqJWugYA8OprBVx/821W9Tgdu8ESsQW+57+qDF9QxbGDAcvd4YffSAAAdl1ZAmu7qtFxGwwf+9jH8OMf/xgf/ehHV/T+QqGAT3ziE3jve9+Lq666Cu95z3vwsb/4UwiihkgkAVWd+55WgwS7gqQ55tscj8fnPDc5OYmJiQlcf/31uP766/GBD3wAN99885LHmpiYwAc+8IElX3fo0CF8/vOfBwBs3bp1We/xMmEfb9VVVEu81ZrY5Ln9RMKyYVvOej3FG4R8PDYaXvbHn/N1LFq6mwWtpZ21tXOmHiLqF1BpkuKZrRfUsHF7HSde8ONbXxjBC0+chqbGEIo9j7N2Kh3vSwRp1nCQmO4A7SK21wO4H8eeuwq3XHMOGPZ/Qdc+jK3nVzE+0bTeFxR5iDxdIw4Suxev6NPxyj15fO+rQ/jGnaN4xRvy+PmPIuAFDa++trNzEM3U95dYLIZGo128dNddd+Guu+6Cz+dDoVBY5J2d7Nu3D7t377YesyyDWCyORx/7LjaNXYqfP/Uk/vsH38fPn3oSv/Tum/DE/ieRmxHwwouP48Z378HPfvxfGEsNYdOmTTh+/Dj27duHiYkJnHvuubj//vtxzz334Hd/93etoPKhhx5CIpHAxMQEJiYm8PnPfx4XXXQRHnvssWX/f5tZWvuxDh8+bH32xRdfjEcffRQXX3wx9u3bZ53DO97xDrzwAime3b17N5544gkcPnwYjz/+OHK5HB566CF85CMfwb333tvxvvnOPZcjv/8nnnjCMwGzqRU962U1/PzHEXzhT7+M3/7rK6zOi499NwtgG4rZbwC4oiNBQRksEZ+AofEq/CEV5byAUk5AyFCYzZ4gzYJiKdIsKOxbuKeB16B38xVg33ZlGODa908DUPGjf09g9vhuADIqhV/G/3rjTtx67SQAICDy8FFvwoESlHiIHItb734Ak1fsAS/+AMCPAIxA8h8A9P8NAHj9TemO98XpgmXgdBeGXnlDBvFUC8ef92Pf344DAF7/7jQiie7FJs349pMDBw7ghhtugN9PboJ+/8p9PYvF9o4ZywBgAMEocDvnnF14zeVX4PwLX46v/NMX8eTjT+KRR76LV73mCnzsL/4Uu3btwsTEBI4cOYLdu3cjHo/jIx/5CN797ndbx7z++uuxdetW/PEf/zEuvvhiK1D98Ic/jOuuuw5XXXUVJiYmejrn7mPZP/vNb34zCoUCrr/++o5zuPzyy5FIJJBIJPD1r38du3fvxsTEBCYnJ3HVVVdZC4bu98137k888QQeeuihjkXDoIkagawpQcqcHMeD99yJW6+dxC3X7ERuZgsA4KUnP4lbrtmJTaNDAztXSicRY9GyZQexAD34MwafveUmlHJpvGRYR55ljOtayvjSwHcFdN+EJ86v422/fxCi7xTAPAfgOgjSM5YjAEAzT14h6hcQSQxDCgShyk1wwvsAzKBZH4euM3jdr6Sx1db1CwASdLt84HT7KPtDGt77l8cQjh8EcBIbz/kqLn/b3DbGCTrv+srY2BgikQiazSZ8Ph+aK/T13L17Nx599FHrMcMwOH70KK583eVgWLLNqqkMIpEwKsUcrrziOrzlLe/FF//xE5DlFuLxGCYnJ3HFFVcAQEcnqd27d+P222/HxRdfbD0XjUYxOTm54oCxUChg69at8x5rvi5W9nO4/fbbEY1GcdFFF1nHAjCvbnipc7/uuuvwnve8B5/4xCd6yrC7ScTP49ZrJ3Hf599qPPNaPHzfXmPX7VwAIwBmwYsncenVb/Z884MzCbMgfPsuUqT/028VcOSZR/HgPXfi+UdJ6tcsNo6soRqmtROie4j5gtjLrmFw8sX/gZ9++2vgRBFKq2U5AgBAkmaePEEsICBdaaJSyOGyN74T/7+9u49t47zvAP49kkdSL3zVq2XHMjnbTZ04lezrmrTpYptysWYbimgKimUrisGJMwTdGkywk9T2jCwO8uKqXdoA21wrSwc0QBEhDtpNw1DbjeNgcZuz6Tpbsy4x6TgvdRWbOtmSLEsyuT94pEnxVW+858TvByBiHo/UL3cPH/7uuefl9rvvxfFX9+C30TX4yl/1IHDL1Zz3sNXQeDP76t7orpL04f8BO+8GbLIdT/0kDACQIPGC0wBDQ0N44IEHsG3bNvT3989pgFswGMSOHTtw8OBBBAIBnD59Gt/+7j8AABzO6xgd1fDznx3DmdPH8Ed3fxk//emrWLNmI764aTM2he7CD194AV1doXRXh3A4DE3T4PV60dPTg127dqUT0n379qG/vz/d0trb24tDhw6ho6Mj3eWgs7MzHVs4HEY0Gk3P+BCNRvH888/n/azU+zVNw7vvvotoNAoAWTGk9hsZGUEkEsHw8DA0TUtvS71n5vvy/b1U3Fu2bBFm2Vi3U8a3XvwZfvLPfTj9+jCAm2GTb8H6O1dh6IM/xUfvAZJ0GNenrsHj9gi/+EE1STU4DL6wFcDbiP32MwBkvPnv/wFABhDHWn36SDMt8iRVam6/Rx99NPH444/P+n1DQ0Nobm5ehIhmLxVLbGwSP/rlBzmv//CJb8Lla8Ttd9+LE4Mv48rwxfTgqE1rm7B++cJNHi7icRFFsXh+EY3hl+eGZ/V53Z1tWO6dW/8lkY6N2WM5cPwcrk0nRzVdjn2Cf/vBfvz3m0cwdW0CssOJWz8fwh/fvyN9semtkfG121cuSDx79+7F008/vSSmh1AUpUv/51ZVVR8ptN/MOvu9997D6tWrS37+Yi3UcGViCuOT13ElZsPIJRnARQDnANQAuAXANCTpDFasXocmlx2SJBm2aEQ+Rscy8/xVsj548b/ex4vf3oMTg38C4C8A7MXtd/8Gp489gYmxNdj6tdcxGvtHSFc1HB3MnbGjksxeTy6kS6OTeOmtD3A59gm+/eByXB1dDeDPYbE1Ij79HNo/reEb3/kYAPD121dWtNW3nGNTqN5mi+8ceGtkWCQJ8RkXDZnrx3d/Y0/Wa2zxFcNcRvnz3InBVyvjwuVk4pvqrjI9eQ02uwPTk9ey7rAAQEN9/snyq5me9N6rquqDiqI8oijKBlVVyxvBZbDUTA019XGMXAIkyQ/gQyQSyRk9bPI4mm9asyBz+NLC8tTKGNViWPe5c/j1LwDZ8TA+jnwXE2NrUOuexu9/uRZe3x7cubrB6FApg7smOX2r29+EZYEjiLy9GsD3EJ9Ojlf64j3JqSUtkmSqpabNE6lALBYJ3hoZsfHcZTMLaeAAKSHM9tZ3vYODEkXhq5Nx4fKNGQMyu6uk7rBk4ncul6qqhwEc1p8GzZL0AkjPzWuzx2F3xvUFK9oAJBNf2TEKi9W3YFOZ0cLx1Mj4+p7nkEgA3/vmVXz4rg/n//fvAQB3fiUG2Z5I70fikPXpW0evTcNZ9ypqXVsxfiXZl73W9TbWfyH522i2+dKZ+M6Rr85eduLrcspwMHkSgq9WhgQJCZTXxYf9e8Uxc5BhsTssANDIFt+CFEXZCeBBo+OYDVvGD6u7YRoXP7IDSN7qtNquAhgFwMRXRN6M5eK/+rcf48C3VuLKsIzVHaPY1HMJk1PZ+5E4fLXJ6Vv/cm8ffvfxCN4avAiLFdjUI8NiSc6yMnPAv+iY+M6Rv07G2U9K7wcAjfVMnkRhs1rgqbFBuzpV1v5NPHfCmO1FCL93hamq+qyiKC8riqKqqlpw+P/Q0FD63/F4HFNTpb831/OtLrEAEnoMQHJ1NncDMKrJsMlx+FoSsNqWJ1+PS+k4FyuWuTA6lng8nnU+KznrQ/zqBMbGkoOgXE1j+Jvnh6FddKCxbQKTU8DExAQkScK1K8MYGjP2wkWU2TAAQWKZHMfYWHKmI5tjApv/LDnYMgFAP6WAK7uuqIT5HBsmvnPUOIsfYSZPYmmot5ed+LLVUByz+c45bFZTzSu5kBRF2Z5nc0RV1cOKomwAAL2LQwTAdgDPFvqszMEjly9fLntw1mIN4pJtcVyPJ+/WuP3X4fankkl9ol8ADtkGOeMOmyiD2wBjY7FYLDmDgSo1cEqun8SbH2fUuXWA16//Q7e80YtlrS0ViacUUQa3AcbHEph04IOxG93I6urqcvdpa0Jz88IN3i/XXI9Ndf4yLICGWSSzzS4mTyJpqLPj7CdjpXcE0OTiRYso6p02OGzW9MwOxaRG9VcjVVUPFHm5C0CqX68XwFuLH9HCsVmkdOJbCLs6iMfjlGEtce449aCYypkL3WxdArmAxRx5a2TI1vIOXxMTX6GU24prt1rY50ww5V6I8GKzoAMAgqlWYVVVBwyOZ1asluJ1riRJTHwFlBwQXvy7a7bkqVqUc17MNvMRE985kiSprD6ELofNVNN8VINyu540ux1V22ooqqYyL1pamPjmpaqqpqrqAf0h7OC2cDiMO+64A+FwOL0tEomga9OdePvM6YLvs0oSv7OCaqgv3ohgtuSpWtSVmNnI5bDBbjNXKmmuaAVTTqtSs9tZgUhoNtw1cllTlDF5Ek+5Lbmt/N4tKqfTUfDhctUXfT3zUUhnZ2d65baUkZERBFYFsP62joLvs1mZ9Iqqoa74d5eDUcVV7NyYcb50NkXOQ4vbCWCk6D7LPOYrFNWg2eXA+dh40X1aPUyeRFNO4lvvsKG+Sge2LSUejydnmyQll6J+/1wUb7z+Gjw+H1a2t8Pj9uKN11+Dw2bBH36pC9FoFH19fXj44Ydx7NgxPPnkkwb8H1CmYsmTTZ8bn8TUVO/Ah8NX879mwnEwbPGdh1Z36R/hNs/clrqlxVXq3EmQsIythsLx1sqoKdFa3+bleVtsExPXCj6uXBkt+nrmo5Tu7m4cPHgQ4XAYnZ2deh9e4Kkn9mJ9RwdWtrfj/PvvY+WqVfD4fGho8OPQoUMIhULwer3YvHmzGFNCUdGLVn+tuRZAqDbFximV2/1MJPNOfBVF2a4/nlmIgMzEUyOjzl64ZclutXAqM0EtK9Ga66uVUWPnoiMiKnXueLG5dIRCIRw9ejRrm00fVOxxe7H+tg7c+Qd34Z++/xw8bg82btgAIDnHp8/nq3i8VFit3QqXM3+rbhNXWRRasYaichoARTOvxFdf9/2wPn1OUH9eVYq1Li331fAqVlCtbmfRQTAr/EyeRFWqRfcmH8+d2YXDYfT39yMSiWDjxo0IBAIIh8MIh8P4nzO/wmN7HseP/vVf8Max1zAyrOGmVavwwflzOPOr04hEIhgcHEQ0Gk0/MgfJkXEKJUlNJQa+kbEKNfK5nTLqTDh4f74RB/XHASQnQw/OOyKTuclXg3eHRvO+tpLJk7DsNgtaXA5cuDyR93UmT+Ja6asFcCnva54a2XTLZ1Kuzs5OvPTSSwCA3t7e9LZ33nkHk9NxDI9P4rG/ezy9/8pVq+CUrfDUyOju7gYA3HfffZiamsLg4GDl/wcorzavM+/vZauL31nRtXmdGBrOHtO03GfObmXzSnxnTJS+AcCPi+0/lyXtROqflS+Wuvh1jI+NI4HsibklSHAlxjE0VLof20LFYhSRYgHKj8djvYazY7kLWVgtEpxToxgaKm+Ri4WIpRKWUiyJqQmMT+YuZLHKVWf6eoaKk63JKcsSiUTOdhLbCm9ug4Kv1o5adisTXru/Fqej2duSjRDmsyBt1PoymKf0ZTALmuvyckYv2ZcpXyzB38VzWg5b3U4EViyreCxGESkWoLx4NtR68JvhD3O2Bxvr0LZs4ZbOFOnYLJVYPrPKgjMf5c6osnH1cjTPcXCbSMeGCpMkCXarhGvT2YmvvcwFhcg4/jo7vDVy1pLxgcZaAKVXYyRjtTfUZnUPtFoktJv0rnbJxLfYuu8Zz7tUVX1k4cIyl5tbXTmJ77plLoOioXI11jvgr7UjNj6Ztf1TLfUGRUTlWttSn5P4emtkzuhQJew2C65Nx9PPrRYpPeiNxLa6uR7q+8M3njfVAxPFpwUl49XarVjhsSOmX7OsaqiDo4z58EVUMvEtse47FEXZrqrqs/q/u2YkxFXhUy31OBGNYWIqedVaa7cxeTKJW9rcOP7exfTzeocNgcY6AyOicizzONFQ58ClsRtdiW5bkTvvKy1NDpsVo7ie7mLmtJnzB7ga3drmxqnzGuKJBFpcTrS4HRjKP9SCBHPbsjocO59sKNq40mtwNHO3ELM6PKMoyllFUYZLvmGJstss+HzQn35+5+oGtj6YxLplrqzRqkq7D1bOxGEKdwRvTFfldsq4hXdZqobVIsEhJ+tYSZLKnnpQ0zS88sorixkaleBy2vC5gB92qwV3rW00OhyahRaXHaGbm7B1XTNaTDiNWcp8B7cdBsDJEpFsOZStFlik5K0cMge7zYIvrWvGf/56CO3+GtzaxuTJLAKNdbgj2IBzl8awaW0jLzYr7Ps/P5t3+/XrcVjLOBd/vfn3ir4eDodx8uRJBAIBAMDRo0ezVmCrd9gwfT2BGrsl52K1r68PHR0dOHnyJHbu3Jne7vV6MTAwkJ75gYyhtHuhtJu3xbCafXoJNDCYbwI2ga1l9wZTWuGrwbYvtBsdBs0Bf0CXJk3TsH///vSUZgByWmqtFgkNeRYI0jQNsVgMoVAIbrc753UubEFU3Zj4EhGRUAYGBrBx48asbb29vYhEIjh69CgCgQBGRkbg8XjQ19eH3t7edIvwyZMnEY1GceTIEezatQsnTpyApmno7+9HR0cHTp1KTj6U+iy/349AIIBYLJbzWZFIBP39/elYOjo6st7T2dlZ8WNDRPPDe4NERCS8YDCI3bt34/7770coFMLAwABCoRC8Xi9CoVB6LubUSm+hUCidmO7atQv33HMPQqFQuuvE7t270/tGo9G8n9XX14dt27ahu7sbW7ZsyXkPEZkPW3yJiEgoPT09eOihh7K2HTlyBECyK4PX64WmadA0bV5dFzweD4LBYDoZzvdZfn9y4LLX6837HiIyFya+REQkFK/Xix07duDgwYPpbg0dHR3Yt28fBgYG4Pf70d3djWg0img0ikgkgmg0inA4jFgshnA4DE3TEA6HEQ6H0dvbi0OHDqGjoyO93759+9Df348tW7akW3BnflZvby/279+fbuWd+Z5UMkxE5sHEl4iIZq3QrAxTU1OQZXnen9/Z2Zm3D20wGMx6Pjg4mPVfAAiFQgCA48ePp2Pp7e3Neg1A1iwRhT5r5j4znxORubCPLxERERFVBSa+RERERFQVmPgSERERUVVg4ktERCVJkoREImF0GDQHiUQCksSl2IkAJr5ERFQGh8MBTdOY/JpMIpGApmlwOBxGh0IkBM7qQEREJbW2tuLChQuIxWJFk994PA6LRYw2FcaSbKl3OBxobW2t+N8mEhETXyIiKslms2HFihUl9xsaGkJzc3MFIiqNsRDRTGJcChMRERERLTImvkRERERUFSra1WHv3r2V/HNERDQPrLOJaKmROEKXiIiIiKoBuzoQERERUVVg4ktEREREVYGJr4kpirLT6Bgol6IoG2Y871EUpcuI85Unlu364xmjY8nYbkg5znNsNujnqseIeGjpY50tJtbZ5ceTsV2EYzOnOlvYxNfIgpeP0YVvJkVRugBsFSAOYZKFjDKz3cAYugC8nPF8AwCoqnoYgFaoEqlQLF0ADquqegBAUH9uSCwztle8HBeI5zFVVQeQPDYVO09LiUj1NuvsgnGwzs6OgXV2mfHM2F7RsryQdbaQia+RBa9APIYWPsEJkSzofzuil5mIUbGk/n7Gpq8C0PR/RwBUrOzkiSWY8fcj+nOjYjHUzHj0JOAt/bVnVVU9ZVRsZiVSvc06uyjW2RlYZ88qHsMsZJ0tZOILAwteAYYWvpkURdmgFwJDCZgspFp2ggLEkuIFEMt43mBUIKqqHtATAQDYAEA1KhZAnHKs+yyABr01zPDWSpMSqd5mnZ0/DtbZpbHOLkKUsox51NmiJr7CFDxAyMLnN68PIsYAAAHVSURBVPjvpwiTLOiVZkRRlGFklx2aQW9ZOSXAD40o5TjlUuqYiHAb2ISEqbdZZxfEOtuEBKqzAXHKMjDHOlvUxFdIIhQ+ga62UoRIFhRF8SLZ2vQUgB8oimJoC08GDTcqCi+ASwbGktKlquojRgYgYjnGjdtoGpIJApkc6+y8WGcXxzq7AMHK8pzrbFETXxELHiBG4QvqAwK2A/Ab3P9ZpGRhO4CnVFV9FsADAERpsfsxbtxmDQIwtNJQFGW7foxS/SCNIlI5BoAB3DhPXui3g2lWRKy3WWdnY51dGuvswkQqy3Ous0VNfIUqeIA4hU9V1QF9YAKQPNlGEjJZ0I+PVnLHRaC3oCiplpSMlpUuAFolW55mxqLH8IyiKGf124sVk+e4GFqO88QTQXJAVg+AhozYqHxC1duss/NinT0D6+zy4zGyLC9knS3sksX6FUUEyU7vB0rtv8ixpKbRiCHZonGvQM39htLPUwzAZ41uWdH7rEUA+I0uM0TVSJR6m3V2YayzqdoJm/gSERERES0kUbs6EBEREREtKCa+RERERFQVmPgSERERUVVg4ktEREREVYGJLxERERFVBSa+RERERFQVmPgSERERUVVg4ktEREREVeH/AfuFpXilFFYMAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Set into eval mode\n", + "model.train()\n", + "model.eval()\n", + "likelihood.eval()\n", + "\n", + "# Initialize plots\n", + "f, (y1_ax, y2_ax) = plt.subplots(1, 2, figsize=(12, 6))\n", + "\n", + "# Make predictions\n", + "with torch.no_grad(), gpytorch.settings.max_cg_iterations(50):\n", + " test_x = torch.linspace(lb, ub, 500)\n", + " predictions = likelihood(model(test_x))\n", + " mean = predictions.mean\n", + " lower, upper = predictions.confidence_region()\n", + " \n", + "# Plot training data as black stars\n", + "y1_ax.plot(train_x.detach().numpy(), train_y[:, 0].detach().numpy(), 'k*')\n", + "# Predictive mean as blue line\n", + "y1_ax.plot(test_x.numpy(), mean[:, 0].numpy(), 'b')\n", + "# Shade in confidence \n", + "y1_ax.fill_between(test_x.numpy(), lower[:, 0].numpy(), upper[:, 0].numpy(), alpha=0.5)\n", + "y1_ax.legend(['Observed Values', 'Mean', 'Confidence'])\n", + "y1_ax.set_title('Function values')\n", + "\n", + "# Plot training data as black stars\n", + "y2_ax.plot(train_x.detach().numpy(), train_y[:, 1].detach().numpy(), 'k*')\n", + "# Predictive mean as blue line\n", + "y2_ax.plot(test_x.numpy(), mean[:, 1].numpy(), 'b')\n", + "# Shade in confidence \n", + "y2_ax.fill_between(test_x.numpy(), lower[:, 1].numpy(), upper[:, 1].numpy(), alpha=0.5)\n", + "y2_ax.legend(['Observed Derivatives', 'Mean', 'Confidence'])\n", + "y2_ax.set_title('Derivatives')\n", + "\n", + "None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/10_GP_Regression_Derivative_Information/Simple_GP_Regression_Derivative_Information_2d.ipynb b/examples/10_GP_Regression_Derivative_Information/Simple_GP_Regression_Derivative_Information_2d.ipynb new file mode 100644 index 000000000..2a043055c --- /dev/null +++ b/examples/10_GP_Regression_Derivative_Information/Simple_GP_Regression_Derivative_Information_2d.ipynb @@ -0,0 +1,328 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# GPyTorch regression with derivative information in 2d\n", + "\n", + "## Introduction\n", + "In this notebook, we show how to train a GP regression model in GPyTorch of a 2-dimensional function given function values and derivative observations. We consider modeling the Franke function where the values and derivatives are contaminated with independent $\\mathcal{N}(0, 0.5)$ distributed noise." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/gpleiss/miniconda3/envs/gpytorch/lib/python3.7/site-packages/matplotlib/__init__.py:1003: UserWarning: Duplicate key in file \"/home/gpleiss/.config/matplotlib/matplotlibrc\", line #57\n", + " (fname, cnt))\n" + ] + } + ], + "source": [ + "import torch\n", + "import gpytorch\n", + "import math\n", + "from matplotlib import cm\n", + "from matplotlib import pyplot as plt\n", + "import numpy as np\n", + "\n", + "%matplotlib inline\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Franke function\n", + "The following is a vectorized implementation of the 2-dimensional Franke function (https://www.sfu.ca/~ssurjano/franke2d.html)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def franke(X, Y):\n", + " term1 = .75*torch.exp(-((9*X - 2).pow(2) + (9*Y - 2).pow(2))/4)\n", + " term2 = .75*torch.exp(-((9*X + 1).pow(2))/49 - (9*Y + 1)/10)\n", + " term3 = .5*torch.exp(-((9*X - 7).pow(2) + (9*Y - 3).pow(2))/4)\n", + " term4 = .2*torch.exp(-(9*X - 4).pow(2) - (9*Y - 7).pow(2))\n", + " \n", + " f = term1 + term2 + term3 - term4\n", + " dfx = -2*(9*X - 2)*9/4 * term1 - 2*(9*X + 1)*9/49 * term2 + \\\n", + " -2*(9*X - 7)*9/4 * term3 + 2*(9*X - 4)*9 * term4\n", + " dfy = -2*(9*Y - 2)*9/4 * term1 - 9/10 * term2 + \\\n", + " -2*(9*Y - 3)*9/4 * term3 + 2*(9*Y - 7)*9 * term4\n", + " \n", + " return f, dfx, dfy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting up the training data\n", + "We use a grid with 100 points in $[0,1] \\times [0,1]$ with 10 uniformly distributed points per dimension." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "xv, yv = torch.meshgrid([torch.linspace(0, 1, 10), torch.linspace(0, 1, 10)])\n", + "train_x = torch.cat((\n", + " xv.contiguous().view(xv.numel(), 1), \n", + " yv.contiguous().view(yv.numel(), 1)),\n", + " dim=1\n", + ")\n", + "\n", + "f, dfx, dfy = franke(train_x[:, 0], train_x[:, 1])\n", + "train_y = torch.stack([f, dfx, dfy], -1).squeeze(1)\n", + "train_y += 0.05 * torch.randn(train_y.size())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting up the model\n", + "A GP prior on the function values implies a multi-output GP prior on the function values and the partial derivatives, see 9.4 in http://www.gaussianprocess.org/gpml/chapters/RW9.pdf for more details. This allows using a `MultitaskMultivariateNormal` and `MultitaskGaussianLikelihood` to train a GP model from both function values and gradients. The resulting RBF kernel that models the covariance between the values and partial derivatives has been implemented in `RBFKernelGrad` and the extension of a constant mean is implemented in `ConstantMeanGrad`.\n", + "\n", + "The `RBFKernelGrad` is generally worse conditioned than the `RBFKernel`, so we place a lower bound on the noise parameter to keep the smallest eigenvalues of the kernel matrix away from zero." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "class GPModelWithDerivatives(gpytorch.models.ExactGP):\n", + " def __init__(self, train_x, train_y, likelihood):\n", + " super(GPModelWithDerivatives, self).__init__(train_x, train_y, likelihood)\n", + " self.mean_module = gpytorch.means.ConstantMeanGrad()\n", + " self.base_kernel = gpytorch.kernels.RBFKernelGrad()\n", + " self.covar_module = gpytorch.kernels.ScaleKernel(self.base_kernel)\n", + " \n", + " def forward(self, x):\n", + " mean_x = self.mean_module(x)\n", + " covar_x = self.covar_module(x)\n", + " return gpytorch.distributions.MultitaskMultivariateNormal(mean_x, covar_x)\n", + "\n", + "likelihood = gpytorch.likelihoods.MultitaskGaussianLikelihood(num_tasks=3) # Value + x-derivative + y-derivative\n", + "likelihood.initialize(noise=0.01*train_y.std()) # Require 1% noise (approximately)\n", + "likelihood.raw_noise.requires_grad = False\n", + "model = GPModelWithDerivatives(train_x, train_y, likelihood)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training the model\n", + "The model training is similar to training a standard GP regression model" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/gpleiss/workspace/gpytorch/gpytorch/utils/pivoted_cholesky.py:101: UserWarning: torch.potrs is deprecated in favour of torch.cholesky_solve and will be removed in the next release. Please use torch.cholesky instead and note that the :attr:`upper` argument in torch.cholesky_solve defaults to ``False``.\n", + " R = torch.potrs(low_rank_mat, torch.cholesky(shifted_mat, upper=True))\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iter 1/50 - Loss: 110.599 lengthscale: 0.693 noise: 0.009\n", + "Iter 2/50 - Loss: 108.303 lengthscale: 0.644 noise: 0.009\n", + "Iter 3/50 - Loss: 103.957 lengthscale: 0.598 noise: 0.009\n", + "Iter 4/50 - Loss: 99.515 lengthscale: 0.554 noise: 0.009\n", + "Iter 5/50 - Loss: 95.034 lengthscale: 0.513 noise: 0.009\n", + "Iter 6/50 - Loss: 92.725 lengthscale: 0.473 noise: 0.009\n", + "Iter 7/50 - Loss: 89.589 lengthscale: 0.436 noise: 0.009\n", + "Iter 8/50 - Loss: 82.739 lengthscale: 0.402 noise: 0.009\n", + "Iter 9/50 - Loss: 76.709 lengthscale: 0.370 noise: 0.009\n", + "Iter 10/50 - Loss: 74.856 lengthscale: 0.340 noise: 0.009\n", + "Iter 11/50 - Loss: 69.440 lengthscale: 0.313 noise: 0.009\n", + "Iter 12/50 - Loss: 69.494 lengthscale: 0.291 noise: 0.009\n", + "Iter 13/50 - Loss: 70.631 lengthscale: 0.277 noise: 0.009\n", + "Iter 14/50 - Loss: 65.169 lengthscale: 0.272 noise: 0.009\n", + "Iter 15/50 - Loss: 61.315 lengthscale: 0.273 noise: 0.009\n", + "Iter 16/50 - Loss: 54.774 lengthscale: 0.279 noise: 0.009\n", + "Iter 17/50 - Loss: 50.667 lengthscale: 0.287 noise: 0.009\n", + "Iter 18/50 - Loss: 45.541 lengthscale: 0.297 noise: 0.009\n", + "Iter 19/50 - Loss: 42.602 lengthscale: 0.306 noise: 0.009\n", + "Iter 20/50 - Loss: 42.981 lengthscale: 0.313 noise: 0.009\n", + "Iter 21/50 - Loss: 36.291 lengthscale: 0.316 noise: 0.009\n", + "Iter 22/50 - Loss: 35.341 lengthscale: 0.315 noise: 0.009\n", + "Iter 23/50 - Loss: 30.169 lengthscale: 0.309 noise: 0.009\n", + "Iter 24/50 - Loss: 26.163 lengthscale: 0.301 noise: 0.009\n", + "Iter 25/50 - Loss: 21.606 lengthscale: 0.290 noise: 0.009\n", + "Iter 26/50 - Loss: 20.486 lengthscale: 0.279 noise: 0.009\n", + "Iter 27/50 - Loss: 17.299 lengthscale: 0.270 noise: 0.009\n", + "Iter 28/50 - Loss: 14.477 lengthscale: 0.265 noise: 0.009\n", + "Iter 29/50 - Loss: 12.003 lengthscale: 0.265 noise: 0.009\n", + "Iter 30/50 - Loss: 5.255 lengthscale: 0.268 noise: 0.009\n", + "Iter 31/50 - Loss: 3.595 lengthscale: 0.270 noise: 0.009\n", + "Iter 32/50 - Loss: 0.765 lengthscale: 0.270 noise: 0.009\n", + "Iter 33/50 - Loss: -5.474 lengthscale: 0.270 noise: 0.009\n", + "Iter 34/50 - Loss: -8.094 lengthscale: 0.268 noise: 0.009\n", + "Iter 35/50 - Loss: -8.584 lengthscale: 0.264 noise: 0.009\n", + "Iter 36/50 - Loss: -16.166 lengthscale: 0.258 noise: 0.009\n", + "Iter 37/50 - Loss: -16.839 lengthscale: 0.251 noise: 0.009\n", + "Iter 38/50 - Loss: -16.507 lengthscale: 0.245 noise: 0.009\n", + "Iter 39/50 - Loss: -19.224 lengthscale: 0.242 noise: 0.009\n", + "Iter 40/50 - Loss: -21.473 lengthscale: 0.240 noise: 0.009\n", + "Iter 41/50 - Loss: -22.681 lengthscale: 0.238 noise: 0.009\n", + "Iter 42/50 - Loss: -27.049 lengthscale: 0.234 noise: 0.009\n", + "Iter 43/50 - Loss: -25.780 lengthscale: 0.233 noise: 0.009\n", + "Iter 44/50 - Loss: -26.668 lengthscale: 0.233 noise: 0.009\n", + "Iter 45/50 - Loss: -30.996 lengthscale: 0.234 noise: 0.009\n", + "Iter 46/50 - Loss: -34.794 lengthscale: 0.233 noise: 0.009\n", + "Iter 47/50 - Loss: -34.021 lengthscale: 0.228 noise: 0.009\n", + "Iter 48/50 - Loss: -34.319 lengthscale: 0.222 noise: 0.009\n", + "Iter 49/50 - Loss: -34.365 lengthscale: 0.215 noise: 0.009\n", + "Iter 50/50 - Loss: -31.525 lengthscale: 0.209 noise: 0.009\n" + ] + } + ], + "source": [ + "# Find optimal model hyperparameters\n", + "model.train()\n", + "likelihood.train()\n", + "\n", + "# Use the adam optimizer\n", + "optimizer = torch.optim.Adam([\n", + " {'params': model.parameters()}, # Includes GaussianLikelihood parameters\n", + "], lr=0.1)\n", + "\n", + "# \"Loss\" for GPs - the marginal log likelihood\n", + "mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, model)\n", + "\n", + "n_iter = 50\n", + "for i in range(n_iter):\n", + " optimizer.zero_grad()\n", + " output = model(train_x)\n", + " loss = -mll(output, train_y)\n", + " loss.backward()\n", + " print('Iter %d/%d - Loss: %.3f lengthscale: %.3f noise: %.3f' % (\n", + " i + 1, n_iter, loss.item(),\n", + " model.covar_module.base_kernel.lengthscale.item(),\n", + " model.likelihood.noise.item()\n", + " ))\n", + " optimizer.step()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Making predictions with the model\n", + "Model predictions are also similar to GP regression with only function values, but we need more CG iterations to get accurate estimates of the predictive variance" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Set into eval mode\n", + "model.eval()\n", + "likelihood.eval()\n", + "\n", + "# Initialize plots\n", + "fig, ax = plt.subplots(2, 3, figsize=(14, 10))\n", + "\n", + "# Test points\n", + "n1, n2 = 50, 50\n", + "xv, yv = torch.meshgrid([torch.linspace(0, 1, n1), torch.linspace(0, 1, n2)])\n", + "f, dfx, dfy = franke(xv, yv)\n", + "\n", + "# Make predictions\n", + "with torch.no_grad(), gpytorch.settings.max_cg_iterations(50):\n", + " test_x = torch.stack([xv.reshape(n1*n2, 1), yv.reshape(n1*n2, 1)], -1).squeeze(1)\n", + " predictions = likelihood(model(test_x))\n", + " mean = predictions.mean\n", + "\n", + "extent = (xv.min(), xv.max(), yv.max(), yv.min())\n", + "ax[0, 0].imshow(f, extent=extent, cmap=cm.jet)\n", + "ax[0, 0].set_title('True values')\n", + "ax[0, 1].imshow(dfx, extent=extent, cmap=cm.jet)\n", + "ax[0, 1].set_title('True x-derivatives')\n", + "ax[0, 2].imshow(dfy, extent=extent, cmap=cm.jet)\n", + "ax[0, 2].set_title('True y-derivatives')\n", + "\n", + "ax[1, 0].imshow(mean[:, 0].detach().numpy().reshape(n1, n2), extent=extent, cmap=cm.jet)\n", + "ax[1, 0].set_title('Predicted values')\n", + "ax[1, 1].imshow(mean[:, 1].detach().numpy().reshape(n1, n2), extent=extent, cmap=cm.jet)\n", + "ax[1, 1].set_title('Predicted x-derivatives')\n", + "ax[1, 2].imshow(mean[:, 2].detach().numpy().reshape(n1, n2), extent=extent, cmap=cm.jet)\n", + "ax[1, 2].set_title('Predicted y-derivatives')\n", + "\n", + "None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/10_GP_Regression_Derivative_Information/index.rst b/examples/10_GP_Regression_Derivative_Information/index.rst new file mode 100644 index 000000000..c4aa053ef --- /dev/null +++ b/examples/10_GP_Regression_Derivative_Information/index.rst @@ -0,0 +1,9 @@ +.. mdinclude:: README.md + +.. toctree:: + :glob: + :maxdepth: 1 + :hidden: + + Simple_GP_Regression_Derivative_Information_1d.ipynb + Simple_GP_Regression_Derivative_Information_2d.ipynb diff --git a/gpytorch/kernels/__init__.py b/gpytorch/kernels/__init__.py index 1610a9845..7ee10c48e 100644 --- a/gpytorch/kernels/__init__.py +++ b/gpytorch/kernels/__init__.py @@ -15,6 +15,7 @@ from .periodic_kernel import PeriodicKernel from .product_structure_kernel import ProductStructureKernel from .rbf_kernel import RBFKernel +from .rbf_kernel_grad import RBFKernelGrad from .scale_kernel import ScaleKernel from .spectral_mixture_kernel import SpectralMixtureKernel from .white_noise_kernel import WhiteNoiseKernel @@ -38,6 +39,7 @@ "ProductKernel", "ProductStructureKernel", "RBFKernel", + "RBFKernelGrad", "ScaleKernel", "SpectralMixtureKernel", "WhiteNoiseKernel", diff --git a/gpytorch/kernels/additive_structure_kernel.py b/gpytorch/kernels/additive_structure_kernel.py index ddfc55478..34626d8b9 100644 --- a/gpytorch/kernels/additive_structure_kernel.py +++ b/gpytorch/kernels/additive_structure_kernel.py @@ -54,3 +54,6 @@ def forward(self, x1, x2, batch_dims=None, **params): if evaluate: res = res.evaluate() return res + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/cosine_kernel.py b/gpytorch/kernels/cosine_kernel.py index a84b45e41..5c756e3dd 100644 --- a/gpytorch/kernels/cosine_kernel.py +++ b/gpytorch/kernels/cosine_kernel.py @@ -3,7 +3,6 @@ import math import torch from .kernel import Kernel -from ..utils.deprecation import _deprecate_kwarg from torch.nn.functional import softplus @@ -66,9 +65,6 @@ def __init__( inv_param_transform=None, **kwargs ): - period_length_prior = _deprecate_kwarg( - kwargs, "log_period_length_prior", "period_length_prior", period_length_prior - ) super(CosineKernel, self).__init__( active_dims=active_dims, param_transform=param_transform, inv_param_transform=inv_param_transform ) diff --git a/gpytorch/kernels/grid_interpolation_kernel.py b/gpytorch/kernels/grid_interpolation_kernel.py index 58f5fd58c..a56a9fdc7 100644 --- a/gpytorch/kernels/grid_interpolation_kernel.py +++ b/gpytorch/kernels/grid_interpolation_kernel.py @@ -180,3 +180,6 @@ def forward(self, x1, x2, batch_dims=None, **params): ) return res + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/grid_kernel.py b/gpytorch/kernels/grid_kernel.py index e3d884a9c..99615dfe8 100644 --- a/gpytorch/kernels/grid_kernel.py +++ b/gpytorch/kernels/grid_kernel.py @@ -98,3 +98,6 @@ def forward(self, x1, x2, diag=False, batch_dims=None, **params): return covar else: return self.base_kernel.forward(x1, x2, diag=diag, batch_dims=batch_dims, **params) + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/inducing_point_kernel.py b/gpytorch/kernels/inducing_point_kernel.py index 699d347f5..81d0417f4 100644 --- a/gpytorch/kernels/inducing_point_kernel.py +++ b/gpytorch/kernels/inducing_point_kernel.py @@ -98,3 +98,6 @@ def forward(self, x1, x2, **kwargs): self.update_added_loss_term("inducing_point_loss_term", new_added_loss_term) return covar + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/kernel.py b/gpytorch/kernels/kernel.py index f98b5c77e..f4fcd5ab5 100644 --- a/gpytorch/kernels/kernel.py +++ b/gpytorch/kernels/kernel.py @@ -6,11 +6,47 @@ from ..lazy import lazify, LazyEvaluatedKernelTensor, ZeroLazyTensor from ..module import Module from .. import settings -from ..utils.deprecation import _deprecate_kwarg from ..utils.transforms import _get_inv_param_transform from torch.nn.functional import softplus from numpy import triu_indices -import warnings + + +@torch.jit.script +def default_postprocess_script(x): + return x + + +class Distance(torch.jit.ScriptModule): + def __init__(self, postprocess_script=default_postprocess_script): + super().__init__() + self._postprocess = postprocess_script + + @torch.jit.script_method + def _jit_sq_dist(self, x1, x2, diag, x1_eq_x2): + # Compute squared distance matrix using quadratic expansion + x1_norm = x1.pow(2).sum(dim=-1, keepdim=True) + if bool(x1_eq_x2): + x2_norm = x1_norm + else: + x2_norm = x2.pow(2).sum(dim=-1, keepdim=True) + + res = x1.matmul(x2.transpose(-2, -1)) + res = res.mul_(-2).add_(x2_norm.transpose(-2, -1)).add_(x1_norm) + + if bool(x1_eq_x2): + # Ensure zero diagonal + res.diagonal(dim1=-2, dim2=-1).fill_(0) + + # Zero out negative values + res.clamp_min_(0) + + return self._postprocess(res) + + @torch.jit.script_method + def _jit_dist(self, x1, x2, diag, x1_eq_x2): + res = self._jit_sq_dist(x1, x2, diag, x1_eq_x2) + res = res.clamp_min_(1e-30).sqrt_() + return self._postprocess(res) class Kernel(Module): @@ -101,7 +137,6 @@ def __init__( eps=1e-6, **kwargs ): - lengthscale_prior = _deprecate_kwarg(kwargs, "log_lengthscale_prior", "lengthscale_prior", lengthscale_prior) super(Kernel, self).__init__() if active_dims is not None and not torch.is_tensor(active_dims): active_dims = torch.tensor(active_dims, dtype=torch.long) @@ -193,29 +228,20 @@ def __pdist_dist(self, x1): return res - def __slow_sq_dist(self, x1, x2, diag, x1_eq_x2): - # Compute squared distance matrix using quadratic expansion - x1_norm = x1.pow(2).sum(dim=-1, keepdim=True) - x2_norm = x2.pow(2).sum(dim=-1, keepdim=True) + @torch.jit.script_method + def _postprocess(self, dist_mat): + return dist_mat - if diag: - mid = (x1 * x2).sum(dim=-1, keepdim=True) - res = (x1_norm - 2 * mid + x2_norm).squeeze(-1) - else: - mid = x1.matmul(x2.transpose(-2, -1)) - res = x1_norm - 2 * mid + x2_norm.transpose(-2, -1) - - if x1_eq_x2: - # Ensure zero diagonal - diag_inds = torch.arange(x1.shape[-2]) - res[..., diag_inds, diag_inds] = 0 - - # Zero out negative values - res.clamp_min_(0) - - return res - - def _covar_dist(self, x1, x2, diag=False, batch_dims=None, square_dist=False, **params): + def _covar_dist( + self, + x1, + x2, + diag=False, + batch_dims=None, + square_dist=False, + postprocess_func=default_postprocess_script, + **params + ): """ This is a helper method for computing the Euclidean distance between all pairs of points in x1 and x2. @@ -248,6 +274,8 @@ def _covar_dist(self, x1, x2, diag=False, batch_dims=None, square_dist=False, ** res = None + distance_module = Distance(postprocess_func) + if diag: # Special case the diagonal because we can return all zeros most of the time. if x1_eq_x2: @@ -256,41 +284,18 @@ def _covar_dist(self, x1, x2, diag=False, batch_dims=None, square_dist=False, ** res = torch.zeros(x1.shape[0] * x1.shape[-1], x2.shape[-2], dtype=x1.dtype, device=x1.device) else: res = torch.zeros(x1.shape[0], x2.shape[-2], dtype=x1.dtype, device=x1.device) - return res + return postprocess_func(res) else: if square_dist: res = (x1 - x2).pow(2).sum(-1) else: res = torch.norm(x1 - x2, p=2, dim=-1) - # TODO: Remove the size check when pytorch/15511 is fixed. - elif x1.size(-2) < 200 and x1_eq_x2: - # Full distance matrix in the square symmetric case - if x1.dim() == 3 and x1.shape[0] == 1: - # If we aren't in batch mode, we can always use torch.pdist - res = self.__pdist_dist(x1.squeeze(0)).unsqueeze(0) - res = res.pow(2) if square_dist else res - elif self.__pdist_supports_batch: - # torch.pdist still works on the latest pytorch-nightly - # TODO: This else branch is not needed on the next PyTorch release (> 1.0.0). - try: - res = self.__pdist_dist(x1) - res = res.pow(2) if square_dist else res - except RuntimeError as e: - if 'pdist only supports 2D tensors, got:' in str(e): - warnings.warn('You are using a version of PyTorch where torch.pdist does not support batch ' - 'matrices. Falling back on manual distance computation. Updating PyTorch to the ' - 'latest pytorch-nightly build will offer significant memory savings during kernel' - ' computation.') - self.__pdist_supports_batch = False - else: - raise e - - if res is None: - if not square_dist: - res = torch.norm(x1.unsqueeze(-2) - x2.unsqueeze(-3), p=2, dim=-1) - else: - res = self.__slow_sq_dist(x1, x2, diag, x1_eq_x2) + res = postprocess_func(res) + elif not square_dist: + res = distance_module._jit_dist(x1, x2, torch.tensor(diag), torch.tensor(x1_eq_x2)) + else: + res = distance_module._jit_sq_dist(x1, x2, torch.tensor(diag), torch.tensor(x1_eq_x2)) if batch_dims == (0, 2): if diag: @@ -432,6 +437,9 @@ def forward(self, x1, x2, **params): res = res + lazify(next_term) return res + def size(self, x1, x2): + return self.kernels[0].size(x1, x2) + class ProductKernel(Kernel): """ @@ -453,3 +461,6 @@ def forward(self, x1, x2, **params): next_term = kern(x1, x2, **params) res = res * lazify(next_term) return res + + def size(self, x1, x2): + return self.kernels[0].size(x1, x2) diff --git a/gpytorch/kernels/matern_kernel.py b/gpytorch/kernels/matern_kernel.py index 5e2136399..5925c4ae2 100644 --- a/gpytorch/kernels/matern_kernel.py +++ b/gpytorch/kernels/matern_kernel.py @@ -3,7 +3,6 @@ import math import torch from .kernel import Kernel -from ..utils.deprecation import _deprecate_kwarg from torch.nn.functional import softplus @@ -92,7 +91,6 @@ def __init__( eps=1e-6, **kwargs ): - _deprecate_kwarg(kwargs, "log_lengthscale_prior", "lengthscale_prior", lengthscale_prior) if nu not in {0.5, 1.5, 2.5}: raise RuntimeError("nu expected to be 0.5, 1.5, or 2.5") super(MaternKernel, self).__init__( diff --git a/gpytorch/kernels/multi_device_kernel.py b/gpytorch/kernels/multi_device_kernel.py index 96b404056..c692019f4 100644 --- a/gpytorch/kernels/multi_device_kernel.py +++ b/gpytorch/kernels/multi_device_kernel.py @@ -60,3 +60,6 @@ def forward(self, x1, x2, diag=False, **kwargs): def gather(self, outputs, output_device): return CatLazyTensor(*[lazify(o) for o in outputs], dim=self.dim, output_device=self.output_device) + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/periodic_kernel.py b/gpytorch/kernels/periodic_kernel.py index 6bbf6c6ce..723d8ea96 100644 --- a/gpytorch/kernels/periodic_kernel.py +++ b/gpytorch/kernels/periodic_kernel.py @@ -3,7 +3,6 @@ import math import torch from .kernel import Kernel -from ..utils.deprecation import _deprecate_kwarg from torch.nn.functional import softplus @@ -83,10 +82,6 @@ def __init__( eps=1e-6, **kwargs ): - lengthscale_prior = _deprecate_kwarg(kwargs, "log_lengthscale_prior", "lengthscale_prior", lengthscale_prior) - period_length_prior = _deprecate_kwarg( - kwargs, "log_period_length_prior", "period_length_prior", period_length_prior - ) super(PeriodicKernel, self).__init__( has_lengthscale=True, active_dims=active_dims, diff --git a/gpytorch/kernels/product_structure_kernel.py b/gpytorch/kernels/product_structure_kernel.py index 7c543e72f..39580b582 100644 --- a/gpytorch/kernels/product_structure_kernel.py +++ b/gpytorch/kernels/product_structure_kernel.py @@ -77,3 +77,6 @@ def __call__(self, x1_, x2_=None, diag=False, batch_dims=None, **params): .__call__(x1_, x2_, diag=diag, batch_dims=batch_dims, **params) .evaluate_kernel() ) + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/rbf_kernel.py b/gpytorch/kernels/rbf_kernel.py index 846045855..8de542cb4 100644 --- a/gpytorch/kernels/rbf_kernel.py +++ b/gpytorch/kernels/rbf_kernel.py @@ -1,8 +1,13 @@ #!/usr/bin/env python3 from .kernel import Kernel -from ..utils.deprecation import _deprecate_kwarg from torch.nn.functional import softplus +import torch + + +@torch.jit.script +def postprocess_rbf(dist_mat): + return dist_mat.div_(-2).exp_() class RBFKernel(Kernel): @@ -77,7 +82,6 @@ def __init__( eps=1e-6, **kwargs ): - _deprecate_kwarg(kwargs, "log_lengthscale_prior", "lengthscale_prior", lengthscale_prior) super(RBFKernel, self).__init__( has_lengthscale=True, ard_num_dims=ard_num_dims, @@ -89,8 +93,8 @@ def __init__( eps=eps, ) - def forward(self, x1, x2, **params): + def forward(self, x1, x2, diag=False, **params): x1_ = x1.div(self.lengthscale) x2_ = x2.div(self.lengthscale) - diff = self._covar_dist(x1_, x2_, square_dist=True, **params) - return diff.div_(-2).exp_() + diff = self._covar_dist(x1_, x2_, square_dist=True, diag=diag, postprocess_func=postprocess_rbf, **params) + return diff diff --git a/gpytorch/kernels/rbf_kernel_grad.py b/gpytorch/kernels/rbf_kernel_grad.py new file mode 100644 index 000000000..d873980ca --- /dev/null +++ b/gpytorch/kernels/rbf_kernel_grad.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +from .rbf_kernel import RBFKernel +from torch.nn.functional import softplus +import torch +from ..lazy.kronecker_product_lazy_tensor import KroneckerProductLazyTensor + + +class RBFKernelGrad(RBFKernel): + r""" + Computes a covariance matrix of the RBF kernel that models the covariance + between the values and partial derivatives for inputs :math:`\mathbf{x_1}` + and :math:`\mathbf{x_2}`. + + See :class:`gpytorch.kernels.Kernel` for descriptions of the lengthscale options. + + .. note:: + + This kernel does not have an `outputscale` parameter. To add a scaling parameter, + decorate this kernel with a :class:`gpytorch.kernels.ScaleKernel`. + + Args: + :attr:`batch_size` (int, optional): + Set this if you want a separate lengthscale for each + batch of input data. It should be `b` if :attr:`x1` is a `b x n x d` tensor. Default: `1`. + :attr:`active_dims` (tuple of ints, optional): + Set this if you want to compute the covariance of only a few input dimensions. The ints + corresponds to the indices of the dimensions. Default: `None`. + :attr:`lengthscale_prior` (Prior, optional): + Set this if you want to apply a prior to the lengthscale parameter. Default: `None`. + :attr:`param_transform` (function, optional): + Set this if you want to use something other than softplus to ensure positiveness of parameters. + :attr:`inv_param_transform` (function, optional): + Set this to allow setting parameters directly in transformed space and sampling from priors. + Automatically inferred for common transformations such as torch.exp or torch.nn.functional.softplus. + :attr:`eps` (float): + The minimum value that the lengthscale can take (prevents divide by zero errors). Default: `1e-6`. + + Attributes: + :attr:`lengthscale` (Tensor): + The lengthscale parameter. Size/shape of parameter depends on the + :attr:`ard_num_dims` and :attr:`batch_size` arguments. + + Example: + >>> x = torch.randn(10, 5) + >>> # Non-batch: Simple option + >>> covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernelGrad()) + >>> covar = covar_module(x) # Output: LazyTensor of size (60 x 60), where 60 = n * (d + 1) + >>> + >>> batch_x = torch.randn(2, 10, 5) + >>> # Batch: Simple option + >>> covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernelGrad()) + >>> # Batch: different lengthscale for each batch + >>> covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernelGrad(batch_size=2)) + >>> covar = covar_module(x) # Output: LazyTensor of size (2 x 60 x 60) + """ + + def __init__( + self, + batch_size=1, + active_dims=None, + lengthscale_prior=None, + param_transform=softplus, + inv_param_transform=None, + eps=1e-6, + **kwargs + ): + # TODO: Add support for ARD + super(RBFKernelGrad, self).__init__( + batch_size=batch_size, + active_dims=active_dims, + lengthscale_prior=lengthscale_prior, + param_transform=softplus, + inv_param_transform=inv_param_transform, + eps=eps, + **kwargs + ) + + def forward(self, x1, x2, diag=False, **params): + b = 1 + if len(x1.size()) == 2: + n1, d = x1.size() + n2, d = x2.size() + else: + b, n1, d = x1.size() + _, n2, _ = x2.size() + + K = torch.zeros(b, n1 * (d + 1), n2 * (d + 1), device=x1.device, dtype=x1.dtype) # batch x n1(d+1) x n2(d+1) + ell = self.lengthscale.squeeze(-1) + + if not diag: + # Scale the inputs by the lengthscale (for stability) + x1_ = x1 / ell + x2_ = x2 / ell + + # Form all possible rank-1 products for the gradient and Hessian blocks + outer = x1_.view([b, n1, 1, d]) - x2_.view([b, 1, n2, d]) + outer = torch.transpose(outer, -1, -2).contiguous() + + # 1) Kernel block + diff = self._covar_dist(x1_, x2_, square_dist=True, **params) + K_11 = diff.div_(-2).exp_() + K[..., :n1, :n2] = K_11 + + # 2) First gradient block + outer1 = outer.view([b, n1, n2 * d]) / ell + K[..., :n1, n2:] = outer1 * K_11.repeat([1, 1, d]) + + # 3) Second gradient block + outer2 = outer.transpose(-1, -3).contiguous().view([b, n2, n1 * d]) + outer2 = outer2.transpose(-1, -2) / ell + K[..., n1:, :n2] = -outer2 * K_11.repeat([1, d, 1]) + + # 4) Hessian block + outer3 = outer1.repeat([1, d, 1]) * outer2.repeat([1, 1, d]) + kp = KroneckerProductLazyTensor( + torch.eye(d, d, device=x1.device, dtype=x1.dtype), + torch.ones(n1, n2, device=x1.device, dtype=x1.dtype) + ) + chain_rule = kp.evaluate() / ell.pow(2) - outer3 + K[..., n1:, n2:] = chain_rule * K_11.repeat([1, d, d]) + + # Symmetrize for stability + if n1 == n2 and torch.eq(x1, x2).all(): + K = 0.5 * (K.transpose(-1, -2) + K) + + # Apply a perfect shuffle permutation to match the MutiTask ordering + pi1 = torch.arange(n1 * (d + 1)).view(d + 1, n1).t().contiguous().view((n1 * (d + 1))) + pi2 = torch.arange(n2 * (d + 1)).view(d + 1, n2).t().contiguous().view((n2 * (d + 1))) + K = K[..., pi1, :][..., :, pi2] + + return K + + else: # TODO: This will change when ARD is supported + if not (n1 == n2 and torch.eq(x1, x2).all()): + raise RuntimeError("diag=True only works when x1 == x2") + + kernel_diag = super(RBFKernelGrad, self).forward(x1, x2, diag=True) + grad_diag = torch.ones(1, n2 * d, device=x1.device, dtype=x1.dtype) / (ell.pow(2)) + k_diag = torch.cat((kernel_diag, grad_diag), dim=-1) + pi = torch.arange(n2 * (d + 1)).view(d + 1, n2).t().contiguous().view((n2 * (d + 1))) + return k_diag[..., pi] + + def size(self, x1, x2): + """ + Given `x_1` with `n_1` data points and `x_2` with `n_2` data points, both in + `d` dimensions, RBFKernelGrad returns an `n_1(d+1) x n_2(d+1)` kernel matrix. + """ + non_batch_size = ((x1.size(-1) + 1) * x1.size(-2), (x2.size(-1) + 1) * x2.size(-2)) + if x1.ndimension() == 3: + return torch.Size((x1.size(0),) + non_batch_size) + else: + return torch.Size(non_batch_size) diff --git a/gpytorch/kernels/scale_kernel.py b/gpytorch/kernels/scale_kernel.py index 5227a4867..618380d73 100644 --- a/gpytorch/kernels/scale_kernel.py +++ b/gpytorch/kernels/scale_kernel.py @@ -2,7 +2,6 @@ import torch from .kernel import Kernel -from ..utils.deprecation import _deprecate_kwarg from ..utils.transforms import _get_inv_param_transform from torch.nn.functional import softplus from ..lazy import delazify @@ -64,7 +63,6 @@ def __init__( inv_param_transform=None, **kwargs ): - outputscale_prior = _deprecate_kwarg(kwargs, "log_outputscale_prior", "outputscale_prior", outputscale_prior) super(ScaleKernel, self).__init__(has_lengthscale=False, batch_size=batch_size) self.base_kernel = base_kernel self._param_transform = param_transform @@ -94,11 +92,12 @@ def forward(self, x1, x2, batch_dims=None, diag=False, **params): outputscales = outputscales.unsqueeze(1).repeat(1, x1.size(-1)).view(-1) orig_output = self.base_kernel.forward(x1, x2, diag=diag, batch_dims=batch_dims, **params) - if torch.is_tensor(orig_output): outputscales = outputscales.view(-1, *([1] * (orig_output.dim() - 1))) if diag: return delazify(orig_output) * outputscales - return orig_output.mul(outputscales) + + def size(self, x1, x2): + return self.base_kernel.size(x1, x2) diff --git a/gpytorch/kernels/spectral_mixture_kernel.py b/gpytorch/kernels/spectral_mixture_kernel.py index c8e6cbf33..3d97cdf13 100644 --- a/gpytorch/kernels/spectral_mixture_kernel.py +++ b/gpytorch/kernels/spectral_mixture_kernel.py @@ -4,7 +4,6 @@ import math import torch from .kernel import Kernel -from ..utils.deprecation import _deprecate_kwarg from torch.nn.functional import softplus logger = logging.getLogger() @@ -83,16 +82,6 @@ def __init__( inv_param_transform=None, **kwargs ): - mixture_scales_prior = _deprecate_kwarg( - kwargs, "log_mixture_scales_prior", "mixture_scales_prior", mixture_scales_prior - ) - mixture_means_prior = _deprecate_kwarg( - kwargs, "log_mixture_means_prior", "mixture_means_prior", mixture_means_prior - ) - mixture_weights_prior = _deprecate_kwarg( - kwargs, "log_mixture_weights_prior", "mixture_weights_prior", mixture_weights_prior - ) - if num_mixtures is None: raise RuntimeError("num_mixtures is a required argument") if mixture_means_prior is not None or mixture_scales_prior is not None or mixture_weights_prior is not None: diff --git a/gpytorch/likelihoods/bernoulli_likelihood.py b/gpytorch/likelihoods/bernoulli_likelihood.py index 20dafceb1..47fe3544f 100755 --- a/gpytorch/likelihoods/bernoulli_likelihood.py +++ b/gpytorch/likelihoods/bernoulli_likelihood.py @@ -2,9 +2,8 @@ import torch from torch.distributions import Bernoulli - -from .. import settings from ..distributions import MultivariateNormal +from ..utils.quadrature import GaussHermiteQuadrature1D from ..functions import log_normal_cdf, normal_cdf from .likelihood import Likelihood @@ -21,6 +20,9 @@ class BernoulliLikelihood(Likelihood): p(Y=y|f)=\Phi(yf) \end{equation*} """ + def __init__(self): + super().__init__() + self.quadrature = GaussHermiteQuadrature1D() def forward(self, input): if not isinstance(input, MultivariateNormal): @@ -35,10 +37,9 @@ def forward(self, input): return Bernoulli(probs=output_probs) def variational_log_probability(self, latent_func, target): - num_samples = settings.num_likelihood_samples.value() - samples = latent_func.rsample(torch.Size([num_samples])).view(-1) - target = target.unsqueeze(0).repeat(num_samples, 1).view(-1) - return log_normal_cdf(samples.mul(target)).sum().div(num_samples) + likelihood_func = lambda locs: log_normal_cdf(locs.mul(target.unsqueeze(-1))) + res = self.quadrature(likelihood_func, latent_func) + return res.sum() def pyro_sample_y(self, variational_dist_f, y_obs, sample_shape, name_prefix=""): import pyro diff --git a/gpytorch/likelihoods/gaussian_likelihood.py b/gpytorch/likelihoods/gaussian_likelihood.py index 0175aff16..71926e1c1 100644 --- a/gpytorch/likelihoods/gaussian_likelihood.py +++ b/gpytorch/likelihoods/gaussian_likelihood.py @@ -8,7 +8,6 @@ from ..distributions import MultivariateNormal from ..likelihoods import Likelihood from ..lazy import BlockDiagLazyTensor, DiagLazyTensor -from ..utils.deprecation import _deprecate_kwarg from .noise_models import HomoskedasticNoise @@ -39,7 +38,6 @@ def variational_log_probability(self, input, target): class GaussianLikelihood(_GaussianLikelihoodBase): def __init__(self, noise_prior=None, batch_size=1, param_transform=softplus, inv_param_transform=None, **kwargs): - noise_prior = _deprecate_kwarg(kwargs, "log_noise_prior", "noise_prior", noise_prior) noise_covar = HomoskedasticNoise( noise_prior=noise_prior, batch_size=batch_size, @@ -60,7 +58,7 @@ def noise(self): @noise.setter def noise(self, value): - self.noise_covar.initialize(value) + self.noise_covar.initialize(noise=value) @property def raw_noise(self): diff --git a/gpytorch/likelihoods/multitask_gaussian_likelihood.py b/gpytorch/likelihoods/multitask_gaussian_likelihood.py index c540f662e..c16406b9e 100644 --- a/gpytorch/likelihoods/multitask_gaussian_likelihood.py +++ b/gpytorch/likelihoods/multitask_gaussian_likelihood.py @@ -14,7 +14,6 @@ RootLazyTensor, ) from ..likelihoods import Likelihood, _GaussianLikelihoodBase -from ..utils.deprecation import _deprecate_kwarg from ..utils.transforms import _get_inv_param_transform from .noise_models import MultitaskHomoskedasticNoise @@ -149,9 +148,6 @@ def __init__( Only used when `rank` > 0. """ - task_correlation_prior = _deprecate_kwarg( - kwargs, "task_prior", "task_correlation_prior", task_correlation_prior - ) noise_covar = MultitaskHomoskedasticNoise( num_tasks=num_tasks, noise_prior=noise_prior, @@ -230,7 +226,6 @@ def __init__( `rank` > 0, or a prior over the log of just the diagonal elements, if `rank` == 0. """ - noise_prior = _deprecate_kwarg(kwargs, "log_noise_prior", "noise_prior", noise_prior) super(Likelihood, self).__init__() self._param_transform = param_transform self._inv_param_transform = _get_inv_param_transform(param_transform, inv_param_transform) diff --git a/gpytorch/means/__init__.py b/gpytorch/means/__init__.py index 7733f9758..3c9a7eff3 100644 --- a/gpytorch/means/__init__.py +++ b/gpytorch/means/__init__.py @@ -2,7 +2,8 @@ from .mean import Mean from .constant_mean import ConstantMean +from .constant_mean_grad import ConstantMeanGrad from .multitask_mean import MultitaskMean from .zero_mean import ZeroMean -__all__ = ["Mean", "ConstantMean", "MultitaskMean", "ZeroMean"] +__all__ = ["Mean", "ConstantMean", "ConstantMeanGrad", "MultitaskMean", "ZeroMean"] diff --git a/gpytorch/means/constant_mean_grad.py b/gpytorch/means/constant_mean_grad.py new file mode 100644 index 000000000..f60989bb9 --- /dev/null +++ b/gpytorch/means/constant_mean_grad.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +import torch +from .mean import Mean + + +class ConstantMeanGrad(Mean): + def __init__(self, prior=None, batch_size=None): + super(ConstantMeanGrad, self).__init__() + self.batch_size = batch_size + self.register_parameter(name="constant", parameter=torch.nn.Parameter(torch.zeros(batch_size or 1, 1))) + if prior is not None: + self.register_prior("mean_prior", prior, "constant") + + def forward(self, input): + mean = self.constant.squeeze().repeat(input.size(-2), input.size(-1) + 1) + if input.ndimension() == 3: + mean = self.constant.squeeze().repeat(input.size(0), input.size(1), input.size(2) + 1) + else: + mean = self.constant.squeeze().repeat(input.size(0), input.size(1) + 1) + mean[..., :, 1:] = 0 + return mean diff --git a/gpytorch/module.py b/gpytorch/module.py index 7b5803594..805c97485 100644 --- a/gpytorch/module.py +++ b/gpytorch/module.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -import itertools -import warnings from collections import OrderedDict import torch @@ -63,26 +61,16 @@ def hyperparameters(self): yield param def initialize(self, **kwargs): - # TODO: Change to initialize actual parameter (e.g. lengthscale) rather than untransformed parameter. """ Set a value for a parameter kwargs: (param_name, value) - parameter to initialize Value can take the form of a tensor, a float, or an int """ - from .utils.log_deprecation import MODULES_WITH_LOG_PARAMS for name, val in kwargs.items(): if isinstance(val, int): val = float(val) - if any(isinstance(self, mod_type) for mod_type in MODULES_WITH_LOG_PARAMS) and "log_" in name: - base_name = name.split("log_")[1] - name = "raw_" + base_name - if not torch.is_tensor(val): - val = self._inv_param_transform(torch.tensor(val).exp()).item() - else: - val = self._inv_param_transform(val.exp()) - if not hasattr(self, name): raise AttributeError("Unknown parameter {p} for {c}".format(p=name, c=self.__class__.__name__)) elif name not in self._parameters: @@ -234,56 +222,14 @@ def variational_parameters(self): for _, param in self.named_variational_parameters(): yield param - def _load_from_state_dict( - self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs - ): - from .utils.log_deprecation import LOG_DEPRECATION_MSG, MODULES_WITH_LOG_PARAMS - - local_name_params = itertools.chain(self._parameters.items(), self._buffers.items()) - local_state = {k: v.data for k, v in local_name_params if v is not None} - - super()._load_from_state_dict( - state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs - ) - if not any(isinstance(self, mod_type) for mod_type in MODULES_WITH_LOG_PARAMS): - return - - # Load log space parameters and throw deprecation warnings. - for name, param in local_state.items(): - if "raw_" in name: - base_name = name.split("raw_")[1] - log_name = "log_" + base_name - log_key = prefix + log_name - if log_key in state_dict and log_key not in local_state: - warnings.warn(LOG_DEPRECATION_MSG.format(log_name=log_name, name=name), DeprecationWarning) - input_param = state_dict[log_key] - if isinstance(input_param, nn.Parameter): - input_param = input_param.data - - real_input_param = self._inv_param_transform(input_param.exp()) - param.copy_(real_input_param) - - if prefix + name in missing_keys: - missing_keys.remove(prefix + name) - if prefix + name in unexpected_keys: - unexpected_keys.remove(prefix + log_name) - def __getattr__(self, name): try: return super().__getattr__(name) except AttributeError as e: - from .utils.log_deprecation import LOG_DEPRECATION_MSG, MODULES_WITH_LOG_PARAMS - - if any(isinstance(self, mod_type) for mod_type in MODULES_WITH_LOG_PARAMS) and "log_" in name: - base_name = name.split("log_")[1] # e.g. log_lengthscale -> lengthscale - raw_name = "raw_" + base_name - warnings.warn(LOG_DEPRECATION_MSG.format(log_name=name, name=raw_name), DeprecationWarning) - return super().__getattribute__(base_name).log() # Get real param value and transform to log - else: - try: - return super().__getattribute__(name) - except AttributeError: - raise e + try: + return super().__getattribute__(name) + except AttributeError: + raise e def _extract_named_added_loss_terms(module, memo=None, prefix=""): diff --git a/gpytorch/settings.py b/gpytorch/settings.py index 5ae1abb17..7316dae5f 100644 --- a/gpytorch/settings.py +++ b/gpytorch/settings.py @@ -343,6 +343,16 @@ class num_likelihood_samples(_value_context): _global_value = 10 +class num_gauss_hermite_locs(_value_context): + """ + The number of samples to draw from a latent GP when computing a likelihood + This is used in variational inference and training + Default: 10 + """ + + _global_value = 20 + + class num_trace_samples(_value_context): """ The number of samples to draw when stochastically computing the trace of a matrix diff --git a/gpytorch/utils/__init__.py b/gpytorch/utils/__init__.py index dde02c083..104e53e69 100644 --- a/gpytorch/utils/__init__.py +++ b/gpytorch/utils/__init__.py @@ -13,6 +13,7 @@ from . import lanczos from . import pivoted_cholesky from . import sparse +from . import quadrature def prod(items): @@ -40,5 +41,6 @@ def prod(items): "interpolation", "lanczos", "pivoted_cholesky", + "quadrature", "sparse", ] diff --git a/gpytorch/utils/linear_cg.py b/gpytorch/utils/linear_cg.py index 9803d1039..8c01aa7e1 100644 --- a/gpytorch/utils/linear_cg.py +++ b/gpytorch/utils/linear_cg.py @@ -8,6 +8,48 @@ def _default_preconditioner(x): return x.clone() +@torch.jit.script +def _jit_linear_cg_updates( + result, alpha, residual_inner_prod, eps, beta, residual, precond_residual, mul_storage, curr_conjugate_vec +): + # # Update result + # # result_{k} = result_{k-1} + alpha_{k} p_vec_{k-1} + torch.addcmul(result, alpha, curr_conjugate_vec, out=result) + + # beta_{k} = (precon_residual{k}^T r_vec_{k}) / (precon_residual{k-1}^T r_vec_{k-1}) + residual_inner_prod.add_(eps) + torch.reciprocal(residual_inner_prod, out=beta) + torch.mul(residual, precond_residual, out=mul_storage) + torch.sum(mul_storage, -2, keepdim=True, out=residual_inner_prod) + beta.mul_(residual_inner_prod) + + # Update curr_conjugate_vec + # curr_conjugate_vec_{k} = precon_residual{k} + beta_{k} curr_conjugate_vec_{k-1} + curr_conjugate_vec.mul_(beta).add_(precond_residual) + + +@torch.jit.script +def _jit_linear_cg_updates_no_precond( + mvms, result, alpha, residual_inner_prod, eps, beta, residual, precond_residual, mul_storage, curr_conjugate_vec +): + torch.mul(curr_conjugate_vec, mvms, out=mul_storage) + torch.sum(mul_storage, dim=-2, keepdim=True, out=alpha) + alpha.add_(eps) + torch.div(residual_inner_prod, alpha, out=alpha) + + # Update residual + # residual_{k} = residual_{k-1} - alpha_{k} mat p_vec_{k-1} + torch.addcmul(residual, -alpha, mvms, out=residual) + + # Update precond_residual + # precon_residual{k} = M^-1 residual_{k} + precond_residual = residual.clone() + + _jit_linear_cg_updates( + result, alpha, residual_inner_prod, eps, beta, residual, precond_residual, mul_storage, curr_conjugate_vec + ) + + def linear_cg( matmul_closure, rhs, @@ -55,6 +97,9 @@ def linear_cg( initial_guess = torch.zeros_like(rhs) if preconditioner is None: preconditioner = _default_preconditioner + precond = False + else: + precond = True # If we are running m CG iterations, we obviously can't get more than m Lanczos coefficients if max_tridiag_iter > max_iter: @@ -115,39 +160,44 @@ def linear_cg( # Get next alpha # alpha_{k} = (residual_{k-1}^T precon_residual{k-1}) / (p_vec_{k-1}^T mat p_vec_{k-1}) mvms = matmul_closure(curr_conjugate_vec) - torch.mul(curr_conjugate_vec, mvms, out=mul_storage) - torch.sum(mul_storage, -2, keepdim=True, out=alpha) - alpha.add_(eps) - torch.div(residual_inner_prod, alpha, out=alpha) - - # Update result - # result_{k} = result_{k-1} + alpha_{k} p_vec_{k-1} - torch.addcmul(result, alpha, curr_conjugate_vec, out=result) - - # Update residual - # residual_{k} = residual_{k-1} - alpha_{k} mat p_vec_{k-1} - torch.addcmul(residual, -1, alpha, mvms, out=residual) - - # If residual are sufficiently small, then exit loop - # Alternatively, exit if this is our last iteration - torch.norm(residual, 2, dim=-2, out=residual_norm) - if (residual_norm < tolerance).all() and not (n_tridiag and k < n_tridiag_iter): - break - - # Update precond_residual - # precon_residual{k} = M^-1 residual_{k} - precond_residual = preconditioner(residual) - - # beta_{k} = (precon_residual{k}^T r_vec_{k}) / (precon_residual{k-1}^T r_vec_{k-1}) - residual_inner_prod.add_(eps) - torch.reciprocal(residual_inner_prod, out=beta) - torch.mul(residual, precond_residual, out=mul_storage) - torch.sum(mul_storage, -2, keepdim=True, out=residual_inner_prod) - beta.mul_(residual_inner_prod) - - # Update curr_conjugate_vec - # curr_conjugate_vec_{k} = precon_residual{k} + beta_{k} curr_conjugate_vec_{k-1} - curr_conjugate_vec.mul_(beta).add_(precond_residual) + if precond: + torch.mul(curr_conjugate_vec, mvms, out=mul_storage) + torch.sum(mul_storage, -2, keepdim=True, out=alpha) + alpha.add_(eps) + torch.div(residual_inner_prod, alpha, out=alpha) + + # Update residual + # residual_{k} = residual_{k-1} - alpha_{k} mat p_vec_{k-1} + torch.addcmul(residual, -1, alpha, mvms, out=residual) + + # Update precond_residual + # precon_residual{k} = M^-1 residual_{k} + precond_residual = preconditioner(residual) + + _jit_linear_cg_updates( + result, + alpha, + residual_inner_prod, + torch.tensor(eps), + beta, + residual, + precond_residual, + mul_storage, + curr_conjugate_vec, + ) + else: + _jit_linear_cg_updates_no_precond( + mvms, + result, + alpha, + residual_inner_prod, + torch.tensor(eps), + beta, + residual, + precond_residual, + mul_storage, + curr_conjugate_vec, + ) # Update tridiagonal matrices, if applicable if n_tridiag and k < n_tridiag_iter and update_tridiag: diff --git a/gpytorch/utils/log_deprecation.py b/gpytorch/utils/log_deprecation.py deleted file mode 100644 index b7f786f89..000000000 --- a/gpytorch/utils/log_deprecation.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python3 - -from ..kernels import ( - CosineKernel, - IndexKernel, - MaternKernel, - PeriodicKernel, - RBFKernel, - ScaleKernel, - SpectralMixtureKernel, -) -from ..likelihoods import GaussianLikelihood, MultitaskGaussianLikelihood - - -MODULES_WITH_LOG_PARAMS = [ - CosineKernel, - IndexKernel, - MaternKernel, - PeriodicKernel, - RBFKernel, - ScaleKernel, - SpectralMixtureKernel, - GaussianLikelihood, - MultitaskGaussianLikelihood, -] - -LOG_DEPRECATION_MSG = ( - "The '{log_name}' parameter is deprecated in favor of '{name}' because we no longer ensure " - "positiveness with torch.exp for improved stability reasons and will be removed in a future " - "release." -) diff --git a/gpytorch/utils/quadrature.py b/gpytorch/utils/quadrature.py new file mode 100644 index 000000000..a0fa7c5d5 --- /dev/null +++ b/gpytorch/utils/quadrature.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import math +import numpy as np +import torch +from torch.nn import Module +from .. import settings + + +class GaussHermiteQuadrature1D(Module): + """ + Implements Gauss-Hermite quadrature for integrating a function with respect to several 1D Gaussian distributions + in batch mode. Within GPyTorch, this is useful primarily for computing expected log likelihoods for variational + inference. + + This is implemented as a Module because Gauss-Hermite quadrature has a set of locations and weights that it + should initialize one time, but that should obey parent calls to .cuda(), .double() etc. + """ + def __init__(self, num_locs=settings.num_gauss_hermite_locs.value()): + super().__init__() + self .num_locs = num_locs + + locations, weights = self._locs_and_weights(num_locs) + + self.locations = locations + self.weights = weights + + def _apply(self, fn): + self.locations = fn(self.locations) + self.weights = fn(self.weights) + return super(GaussHermiteQuadrature1D, self)._apply(fn) + + def _locs_and_weights(self, num_locs): + """ + Get locations and weights for Gauss-Hermite quadrature. Note that this is **not** intended to be used + externally, because it directly creates tensors with no knowledge of a device or dtype to cast to. + + Instead, create a GaussHermiteQuadrature1D object and get the locations and weights from buffers. + """ + locations, weights = np.polynomial.hermite.hermgauss(num_locs) + locations = torch.Tensor(locations) + weights = torch.Tensor(weights) + return locations, weights + + def forward(self, func, gaussian_dists): + """ + Runs Gauss-Hermite quadrature on the callable func, integrating against the Gaussian distributions specified + by gaussian_dists. + + Args: + - func (callable): Function to integrate + - gaussian_dists (Distribution): Either a MultivariateNormal whose covariance is assumed to be diagonal + or a :obj:`torch.distributions.Normal`. + Returns: + - Result of integrating func against each univariate Gaussian in gaussian_dists. + """ + mus = gaussian_dists.mean + vars = gaussian_dists.variance + + shifted_locs = torch.sqrt(2.0 * vars).unsqueeze(-1) * self.locations + mus.unsqueeze(-1) + res = (1 / math.sqrt(math.pi)) * (func(shifted_locs) * self.weights).sum(-1) + + return res diff --git a/test/examples/test_simple_gp_regression.py b/test/examples/test_simple_gp_regression.py index 912e3a9d1..d39ab944a 100644 --- a/test/examples/test_simple_gp_regression.py +++ b/test/examples/test_simple_gp_regression.py @@ -57,11 +57,11 @@ def test_prior(self, cuda=False): gp_model = ExactGPModel(None, None, likelihood) # Update lengthscale prior to accommodate extreme parameters gp_model.covar_module.base_kernel.register_prior( - "log_lengthscale_prior", SmoothedBoxPrior(exp(-10), exp(10), sigma=0.5), "raw_lengthscale" + "lengthscale_prior", SmoothedBoxPrior(exp(-10), exp(10), sigma=0.5), "raw_lengthscale" ) gp_model.mean_module.initialize(constant=1.5) - gp_model.covar_module.base_kernel.initialize(log_lengthscale=0) - likelihood.initialize(log_noise=0) + gp_model.covar_module.base_kernel.initialize(lengthscale=1) + likelihood.initialize(noise=0) if cuda: gp_model.cuda() @@ -87,8 +87,8 @@ def test_posterior_latent_gp_and_likelihood_without_optimization(self, cuda=Fals # We're manually going to set the hyperparameters to be ridiculous likelihood = GaussianLikelihood() gp_model = ExactGPModel(train_x, train_y, likelihood) - gp_model.covar_module.base_kernel.initialize(raw_lengthscale=-15) - likelihood.initialize(log_noise=-15) + gp_model.covar_module.base_kernel.initialize(lengthscale=exp(-15)) + likelihood.initialize(noise=exp(-15)) if cuda: gp_model.cuda() @@ -122,9 +122,9 @@ def test_posterior_latent_gp_and_likelihood_with_optimization(self, cuda=False): likelihood = GaussianLikelihood(noise_prior=SmoothedBoxPrior(exp(-3), exp(3), sigma=0.1)) gp_model = ExactGPModel(train_x, train_y, likelihood) mll = gpytorch.ExactMarginalLogLikelihood(likelihood, gp_model) - gp_model.covar_module.base_kernel.initialize(log_lengthscale=1) + gp_model.covar_module.base_kernel.initialize(lengthscale=exp(1)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=1) + likelihood.initialize(noise=exp(1)) if cuda: gp_model.cuda() @@ -170,9 +170,9 @@ def test_fantasy_updates(self, cuda=False): likelihood = GaussianLikelihood() gp_model = ExactGPModel(train_x, train_y, likelihood) mll = gpytorch.ExactMarginalLogLikelihood(likelihood, gp_model) - gp_model.covar_module.base_kernel.initialize(log_lengthscale=1) + gp_model.covar_module.base_kernel.initialize(lengthscale=exp(1)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=1) + likelihood.initialize(noise=exp(1)) if cuda: gp_model.cuda() @@ -240,9 +240,9 @@ def test_fantasy_updates_batch(self, cuda=False): likelihood = GaussianLikelihood() gp_model = ExactGPModel(train_x, train_y, likelihood) mll = gpytorch.ExactMarginalLogLikelihood(likelihood, gp_model) - gp_model.covar_module.base_kernel.initialize(log_lengthscale=1) + gp_model.covar_module.base_kernel.initialize(lengthscale=exp(1)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=1) + likelihood.initialize(noise=exp(1)) if cuda: gp_model.cuda() @@ -311,9 +311,9 @@ def test_posterior_latent_gp_and_likelihood_fast_pred_var(self, cuda=False): likelihood = GaussianLikelihood(noise_prior=SmoothedBoxPrior(exp(-3), exp(3), sigma=0.1)) gp_model = ExactGPModel(train_x, train_y, likelihood) mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, gp_model) - gp_model.covar_module.base_kernel.initialize(log_lengthscale=1) + gp_model.covar_module.base_kernel.initialize(lengthscale=exp(1)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=1) + likelihood.initialize(noise=exp(1)) if cuda: gp_model.cuda() diff --git a/test/examples/test_white_noise_regression.py b/test/examples/test_white_noise_regression.py index 5a256252a..bca9c0f7b 100644 --- a/test/examples/test_white_noise_regression.py +++ b/test/examples/test_white_noise_regression.py @@ -59,11 +59,11 @@ def test_posterior_latent_gp_and_likelihood_without_optimization(self, cuda=Fals gp_model = ExactGPModel(train_x, train_y, likelihood) # Update lengthscale prior to accommodate extreme parameters gp_model.rbf_covar_module.register_prior( - "log_lengthscale_prior", SmoothedBoxPrior(exp(-10), exp(10), sigma=0.5), "raw_lengthscale" + "lengthscale_prior", SmoothedBoxPrior(exp(-10), exp(10), sigma=0.5), "raw_lengthscale" ) - gp_model.rbf_covar_module.initialize(log_lengthscale=-10) + gp_model.rbf_covar_module.initialize(lengthscale=exp(-10)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=-10) + likelihood.initialize(noise=exp(-10)) if cuda: gp_model.cuda() @@ -96,9 +96,9 @@ def test_posterior_latent_gp_and_likelihood_with_optimization(self, cuda=False): likelihood = GaussianLikelihood(noise_prior=SmoothedBoxPrior(exp(-3), exp(3), sigma=0.1)) gp_model = ExactGPModel(train_x, train_y, likelihood) mll = gpytorch.ExactMarginalLogLikelihood(likelihood, gp_model) - gp_model.rbf_covar_module.initialize(log_lengthscale=1) + gp_model.rbf_covar_module.initialize(lengthscale=exp(1)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=1) + likelihood.initialize(noise=exp(1)) if cuda: gp_model.cuda() @@ -146,9 +146,9 @@ def test_posterior_latent_gp_and_likelihood_fast_pred_var(self, cuda=False): likelihood = GaussianLikelihood(noise_prior=SmoothedBoxPrior(exp(-3), exp(3), sigma=0.1)) gp_model = ExactGPModel(train_x, train_y, likelihood) mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, gp_model) - gp_model.rbf_covar_module.initialize(log_lengthscale=1) + gp_model.rbf_covar_module.initialize(lengthscale=exp(1)) gp_model.mean_module.initialize(constant=0) - likelihood.initialize(log_noise=1) + likelihood.initialize(noise=exp(1)) if cuda: gp_model.cuda() diff --git a/test/kernels/test_additive_kernel.py b/test/kernels/test_additive_kernel.py index d0727d20b..301ac94c5 100644 --- a/test/kernels/test_additive_kernel.py +++ b/test/kernels/test_additive_kernel.py @@ -12,8 +12,8 @@ def test_computes_product_of_radial_basis_function(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel_1 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_2 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel_1 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_2 = RBFKernel().initialize(lengthscale=lengthscale) kernel = kernel_1 * kernel_2 actual = torch.tensor([[16, 4], [4, 0], [64, 36]], dtype=torch.float) @@ -28,8 +28,8 @@ def test_computes_sum_of_radial_basis_function(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel_1 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_2 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel_1 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_2 = RBFKernel().initialize(lengthscale=lengthscale) kernel = kernel_1 + kernel_2 actual = torch.tensor([[16, 4], [4, 0], [64, 36]], dtype=torch.float) @@ -44,9 +44,9 @@ def test_computes_product_of_three_radial_basis_function(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel_1 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_2 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_3 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel_1 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_2 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_3 = RBFKernel().initialize(lengthscale=lengthscale) kernel = ProductKernel(kernel_1, kernel_2, kernel_3) actual = torch.tensor([[16, 4], [4, 0], [64, 36]], dtype=torch.float) @@ -61,9 +61,9 @@ def test_computes_sum_of_three_radial_basis_function(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel_1 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_2 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_3 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel_1 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_2 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_3 = RBFKernel().initialize(lengthscale=lengthscale) kernel = AdditiveKernel(kernel_1, kernel_2, kernel_3) actual = ( @@ -87,8 +87,8 @@ def test_computes_sum_radial_basis_function_gradient(self): actual_output.backward(torch.eye(3)) actual_param_grad = param.grad.sum() * 2 - kernel_1 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_2 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel_1 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_2 = RBFKernel().initialize(lengthscale=lengthscale) kernel = kernel_1 + kernel_2 kernel.eval() @@ -110,9 +110,9 @@ def test_computes_sum_three_radial_basis_function_gradient(self): actual_output.backward(torch.eye(3)) actual_param_grad = param.grad.sum() * 3 - kernel_1 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_2 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) - kernel_3 = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel_1 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_2 = RBFKernel().initialize(lengthscale=lengthscale) + kernel_3 = RBFKernel().initialize(lengthscale=lengthscale) kernel = AdditiveKernel(kernel_1, kernel_2, kernel_3) kernel.eval() diff --git a/test/kernels/test_cosine_kernel.py b/test/kernels/test_cosine_kernel.py index d2efa74e2..e422ddab0 100644 --- a/test/kernels/test_cosine_kernel.py +++ b/test/kernels/test_cosine_kernel.py @@ -11,7 +11,7 @@ def test_computes_periodic_function(self): a = torch.tensor([[4, 1], [2, 2], [8, 0]], dtype=torch.float) b = torch.tensor([[0, 0], [2, 1], [1, 0]], dtype=torch.float) period = 1 - kernel = CosineKernel().initialize(log_period_length=math.log(period)) + kernel = CosineKernel().initialize(period_length=period) kernel.eval() actual = torch.zeros(3, 3) @@ -45,7 +45,7 @@ def test_batch(self): a = torch.tensor([[4, 2, 8], [1, 2, 3]], dtype=torch.float).view(2, 3, 1) b = torch.tensor([[0, 2, 1], [-1, 2, 0]], dtype=torch.float).view(2, 3, 1) period = torch.tensor(1, dtype=torch.float).view(1, 1, 1) - kernel = CosineKernel().initialize(log_period_length=torch.log(period)) + kernel = CosineKernel().initialize(period_length=period) kernel.eval() actual = torch.zeros(2, 3, 3) @@ -61,7 +61,7 @@ def test_batch_separate(self): a = torch.tensor([[[4, 1], [2, 2], [8, 0]], [[2, 5], [6, 1], [0, 1]]], dtype=torch.float) b = torch.tensor([[[0, 0], [2, 1], [1, 0]], [[1, 1], [2, 3], [1, 0]]], dtype=torch.float) period = torch.tensor([1, 2], dtype=torch.float).view(2, 1, 1) - kernel = CosineKernel(batch_size=2).initialize(log_period_length=torch.log(period)) + kernel = CosineKernel(batch_size=2).initialize(period_length=period) kernel.eval() actual = torch.zeros(2, 3, 3) diff --git a/test/kernels/test_grid_kernel.py b/test/kernels/test_grid_kernel.py index 5aab2d524..42a97b62c 100644 --- a/test/kernels/test_grid_kernel.py +++ b/test/kernels/test_grid_kernel.py @@ -28,7 +28,7 @@ def test_grid_grid(self): self.assertIsInstance(grid_covar, KroneckerProductLazyTensor) grid_eval = kernel(grid_data, grid_data).evaluate() actual_eval = base_kernel(grid_data, grid_data).evaluate() - self.assertLess(torch.norm(grid_eval - actual_eval), 1e-5) + self.assertLess(torch.norm(grid_eval - actual_eval), 1.2e-5) def test_nongrid_grid(self): base_kernel = RBFKernel() diff --git a/test/kernels/test_matern_kernel.py b/test/kernels/test_matern_kernel.py index 8dbc11dff..fe1b0bea6 100644 --- a/test/kernels/test_matern_kernel.py +++ b/test/kernels/test_matern_kernel.py @@ -12,7 +12,7 @@ def test_forward_nu_1_over_2(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel = MaternKernel(nu=0.5).initialize(log_lengthscale=math.log(lengthscale)) + kernel = MaternKernel(nu=0.5).initialize(lengthscale=lengthscale) kernel.eval() actual = torch.tensor([[4, 2], [2, 0], [8, 6]], dtype=torch.float).div_(-lengthscale).exp() @@ -24,7 +24,7 @@ def test_forward_nu_3_over_2(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel = MaternKernel(nu=1.5).initialize(log_lengthscale=math.log(lengthscale)) + kernel = MaternKernel(nu=1.5).initialize(lengthscale=lengthscale) kernel.eval() dist = torch.tensor([[4, 2], [2, 0], [8, 6]], dtype=torch.float).mul_(math.sqrt(3) / lengthscale) @@ -37,7 +37,7 @@ def test_forward_nu_5_over_2(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 - kernel = MaternKernel(nu=2.5).initialize(log_lengthscale=math.log(lengthscale)) + kernel = MaternKernel(nu=2.5).initialize(lengthscale=lengthscale) kernel.eval() dist = torch.tensor([[4, 2], [2, 0], [8, 6]], dtype=torch.float).mul_(math.sqrt(5) / lengthscale) @@ -51,7 +51,7 @@ def test_ard(self): lengthscales = torch.tensor([1, 2], dtype=torch.float).view(1, 1, 2) kernel = MaternKernel(nu=2.5, ard_num_dims=2) - kernel.initialize(log_lengthscale=torch.log(lengthscales)) + kernel.initialize(lengthscale=lengthscales) kernel.eval() dist = torch.tensor([[1, 1], [2, 2]], dtype=torch.float) @@ -83,7 +83,7 @@ def test_ard_batch(self): lengthscales = torch.tensor([[[1, 2, 1]]], dtype=torch.float) kernel = MaternKernel(nu=2.5, batch_size=2, ard_num_dims=3) - kernel.initialize(log_lengthscale=torch.log(lengthscales)) + kernel.initialize(lengthscale=lengthscales) kernel.eval() dist = torch.tensor([[[1], [1]], [[2], [0]]], dtype=torch.float).mul_(math.sqrt(5)) @@ -97,7 +97,7 @@ def test_ard_separate_batch(self): lengthscales = torch.tensor([[[1, 2, 1]], [[2, 1, 0.5]]], dtype=torch.float) kernel = MaternKernel(nu=2.5, batch_size=2, ard_num_dims=3) - kernel.initialize(log_lengthscale=torch.log(lengthscales)) + kernel.initialize(lengthscale=lengthscales) kernel.eval() dist = torch.tensor([[[1, 1], [1, 1]], [[4, 4], [0, 0]]], dtype=torch.float).mul_(math.sqrt(5)) diff --git a/test/kernels/test_periodic_kernel.py b/test/kernels/test_periodic_kernel.py index 4646e1c04..00733a6a9 100644 --- a/test/kernels/test_periodic_kernel.py +++ b/test/kernels/test_periodic_kernel.py @@ -12,7 +12,7 @@ def test_computes_periodic_function(self): b = torch.tensor([0, 2], dtype=torch.float).view(2, 1) lengthscale = 2 period = 3 - kernel = PeriodicKernel().initialize(log_lengthscale=math.log(lengthscale), log_period_length=math.log(period)) + kernel = PeriodicKernel().initialize(lengthscale=lengthscale, period_length=period) kernel.eval() actual = torch.zeros(3, 2) @@ -30,7 +30,7 @@ def test_batch(self): period = torch.tensor(1, dtype=torch.float).view(1, 1, 1) lengthscale = torch.tensor(2, dtype=torch.float).view(1, 1, 1) kernel = PeriodicKernel().initialize( - log_lengthscale=torch.log(lengthscale), log_period_length=torch.log(period) + lengthscale=lengthscale, period_length=period ) kernel.eval() @@ -50,7 +50,7 @@ def test_batch_separate(self): period = torch.tensor([1, 2], dtype=torch.float).view(2, 1, 1) lengthscale = torch.tensor([2, 1], dtype=torch.float).view(2, 1, 1) kernel = PeriodicKernel(batch_size=2).initialize( - log_lengthscale=torch.log(lengthscale), log_period_length=torch.log(period) + lengthscale=lengthscale, period_length=period ) kernel.eval() diff --git a/test/kernels/test_rbf_kernel.py b/test/kernels/test_rbf_kernel.py index 740fadf52..3fd150fce 100644 --- a/test/kernels/test_rbf_kernel.py +++ b/test/kernels/test_rbf_kernel.py @@ -13,7 +13,7 @@ def test_ard(self): lengthscales = torch.tensor([1, 2], dtype=torch.float).view(1, 1, 2) kernel = RBFKernel(ard_num_dims=2) - kernel.initialize(log_lengthscale=lengthscales.log()) + kernel.initialize(lengthscale=lengthscales) kernel.eval() scaled_a = a.div(lengthscales) @@ -44,7 +44,7 @@ def test_ard_batch(self): lengthscales = torch.tensor([[[1, 2, 1]]], dtype=torch.float) kernel = RBFKernel(batch_size=2, ard_num_dims=3) - kernel.initialize(log_lengthscale=lengthscales.log()) + kernel.initialize(lengthscale=lengthscales) kernel.eval() scaled_a = a.div(lengthscales) @@ -75,7 +75,7 @@ def test_ard_separate_batch(self): lengthscales = torch.tensor([[[1, 2, 1]], [[2, 1, 0.5]]], dtype=torch.float) kernel = RBFKernel(batch_size=2, ard_num_dims=3) - kernel.initialize(log_lengthscale=lengthscales.log()) + kernel.initialize(lengthscale=lengthscales) kernel.eval() scaled_a = a.div(lengthscales) @@ -97,7 +97,7 @@ def test_subset_active_compute_radial_basis_function(self): lengthscale = 2 kernel = RBFKernel(active_dims=[0]) - kernel.initialize(log_lengthscale=math.log(lengthscale)) + kernel.initialize(lengthscale=lengthscale) kernel.eval() actual = torch.tensor([[16, 4, 0], [4, 0, 4], [64, 36, 16]], dtype=torch.float) @@ -115,7 +115,7 @@ def test_computes_radial_basis_function(self): b = torch.tensor([0, 2, 4], dtype=torch.float).view(3, 1) lengthscale = 2 - kernel = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel = RBFKernel().initialize(lengthscale=lengthscale) kernel.eval() actual = torch.tensor([[16, 4, 0], [4, 0, 4], [64, 36, 16]], dtype=torch.float) @@ -134,7 +134,7 @@ def test_computes_radial_basis_function_gradient(self): b = torch.tensor([0, 2, 2], dtype=torch.float).view(3, 1) lengthscale = 2 - kernel = RBFKernel().initialize(log_lengthscale=math.log(lengthscale)) + kernel = RBFKernel().initialize(lengthscale=lengthscale) kernel.eval() param = math.log(math.exp(lengthscale) - 1) * torch.ones(3, 3) @@ -166,7 +166,7 @@ def test_subset_active_computes_radial_basis_function_gradient(self): actual_param_grad = param.grad.sum() kernel = RBFKernel(active_dims=[0]) - kernel.initialize(log_lengthscale=math.log(lengthscale)) + kernel.initialize(lengthscale=lengthscale) kernel.eval() output = kernel(a, b).evaluate() output.backward(gradient=torch.eye(3)) diff --git a/test/kernels/test_rbf_kernel_grad.py b/test/kernels/test_rbf_kernel_grad.py new file mode 100644 index 000000000..6e9803947 --- /dev/null +++ b/test/kernels/test_rbf_kernel_grad.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +import torch +import unittest +from gpytorch.kernels import RBFKernelGrad + + +class TestRBFKernelGrad(unittest.TestCase): + def test_kernel(self, cuda=False): + a = torch.tensor([[[1, 2], [2, 4]]], dtype=torch.float) + b = torch.tensor([[[1, 3], [0, 4]]], dtype=torch.float) + + actual = torch.tensor( + [ + [0.35321, 0, -0.73517, 0.0054977, 0.011443, -0.022886], + [0, 0.73517, 0, -0.011443, -0.012374, 0.047633], + [0.73517, 0, -0.79499, 0.022886, 0.047633, -0.083824], + [0.12476, 0.25967, 0.25967, 0.015565, 0.064793, 0], + [-0.25967, -0.2808, -0.54047, -0.064793, -0.23732, 0], + [-0.25967, -0.54047, -0.2808, 0, 0, 0.032396], + ] + ) + + if cuda: + a = a.cuda() + b = b.cuda() + actual = actual.cuda() + + kernel = RBFKernelGrad() + res = kernel(a, b).evaluate() + + self.assertLess(torch.norm(res - actual), 1e-5) + + def test_kernel_cuda(self): + if torch.cuda.is_available(): + self.test_kernel(cuda=True) + + def test_kernel_batch(self): + a = torch.tensor([[[1, 2, 3], [2, 4, 0]], [[-1, 1, 2], [2, 1, 4]]], dtype=torch.float) + b = torch.tensor([[[1, 3, 1]], [[2, -1, 0]]], dtype=torch.float).repeat(1, 2, 1) + + kernel = RBFKernelGrad() + res = kernel(a, b).evaluate() + + # Compute each batch separately + actual = torch.zeros(2, 8, 8) + actual[0, :, :] = kernel(a[0, :, :].squeeze(), b[0, :, :].squeeze()).evaluate() + actual[1, :, :] = kernel(a[1, :, :].squeeze(), b[1, :, :].squeeze()).evaluate() + + self.assertLess(torch.norm(res - actual), 1e-5) + + def test_initialize_lengthscale(self): + kernel = RBFKernelGrad() + kernel.initialize(lengthscale=3.14) + actual_value = torch.tensor(3.14).view_as(kernel.lengthscale) + self.assertLess(torch.norm(kernel.lengthscale - actual_value), 1e-5) + + def test_initialize_lengthscale_batch(self): + kernel = RBFKernelGrad(batch_size=2) + ls_init = torch.tensor([3.14, 4.13]) + kernel.initialize(lengthscale=ls_init) + actual_value = ls_init.view_as(kernel.lengthscale) + self.assertLess(torch.norm(kernel.lengthscale - actual_value), 1e-5) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/kernels/test_scale_kernel.py b/test/kernels/test_scale_kernel.py index bdebd03fb..adcc5caaa 100644 --- a/test/kernels/test_scale_kernel.py +++ b/test/kernels/test_scale_kernel.py @@ -12,9 +12,9 @@ def test_ard(self): lengthscales = torch.tensor([1, 2], dtype=torch.float).view(1, 1, 2) base_kernel = RBFKernel(ard_num_dims=2) - base_kernel.initialize(log_lengthscale=lengthscales.log()) + base_kernel.initialize(lengthscale=lengthscales) kernel = ScaleKernel(base_kernel) - kernel.initialize(log_outputscale=torch.tensor([3], dtype=torch.float).log()) + kernel.initialize(outputscale=torch.tensor([3], dtype=torch.float)) kernel.eval() scaled_a = a.div(lengthscales) @@ -47,9 +47,9 @@ def test_ard_batch(self): lengthscales = torch.tensor([[[1, 2, 1]]], dtype=torch.float) base_kernel = RBFKernel(batch_size=2, ard_num_dims=3) - base_kernel.initialize(log_lengthscale=lengthscales.log()) + base_kernel.initialize(lengthscale=lengthscales) kernel = ScaleKernel(base_kernel, batch_size=2) - kernel.initialize(log_outputscale=torch.tensor([1, 2], dtype=torch.float).log()) + kernel.initialize(outputscale=torch.tensor([1, 2], dtype=torch.float)) kernel.eval() scaled_a = a.div(lengthscales) diff --git a/test/kernels/test_spectral_mixture_kernel.py b/test/kernels/test_spectral_mixture_kernel.py index ab01b5490..40606b4ed 100644 --- a/test/kernels/test_spectral_mixture_kernel.py +++ b/test/kernels/test_spectral_mixture_kernel.py @@ -14,9 +14,9 @@ def test_standard(self): weights = [4, 2] kernel = SpectralMixtureKernel(num_mixtures=2, ard_num_dims=2) kernel.initialize( - log_mixture_weights=torch.tensor([[4, 2]], dtype=torch.float).log(), - log_mixture_means=torch.tensor([[[[1, 2]], [[2, 1]]]], dtype=torch.float).log(), - log_mixture_scales=torch.tensor([[[[0.5, 0.25]], [[0.25, 0.5]]]], dtype=torch.float).log(), + mixture_weights=torch.tensor([[4, 2]], dtype=torch.float), + mixture_means=torch.tensor([[[[1, 2]], [[2, 1]]]], dtype=torch.float), + mixture_scales=torch.tensor([[[[0.5, 0.25]], [[0.25, 0.5]]]], dtype=torch.float), ) kernel.eval() @@ -68,7 +68,7 @@ def test_batch_separate(self): weights = torch.tensor([[4, 2], [1, 2]], dtype=torch.float).view(2, 2) kernel = SpectralMixtureKernel(batch_size=2, num_mixtures=2) kernel.initialize( - log_mixture_weights=weights.log(), log_mixture_means=means.log(), log_mixture_scales=scales.log() + mixture_weights=weights, mixture_means=means, mixture_scales=scales ) kernel.eval() diff --git a/test/means/test_constant_mean_grad.py b/test/means/test_constant_mean_grad.py new file mode 100644 index 000000000..38fbb69e1 --- /dev/null +++ b/test/means/test_constant_mean_grad.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import torch +import unittest +from gpytorch.means import ConstantMeanGrad + + +class TestConstantMeanGrad(unittest.TestCase): + def setUp(self): + self.mean = ConstantMeanGrad() + self.mean.constant.data.fill_(5) + + def test_forward(self): + a = torch.tensor([[1, 2], [2, 4]], dtype=torch.float) + res = self.mean(a) + self.assertEqual(tuple(res.size()), (2, 3)) + self.assertTrue(res[:, 0].eq(5).all()) + self.assertTrue(res[:, 1:].eq(0).all()) + + def test_forward_batch(self): + a = torch.tensor([[[1, 2], [1, 2], [2, 4]], [[2, 3], [2, 3], [1, 3]]], dtype=torch.float) + res = self.mean(a) + self.assertEqual(tuple(res.size()), (2, 3, 3)) + self.assertTrue(res[:, :, 0].eq(5).all()) + self.assertTrue(res[:, :, 1:].eq(0).all()) diff --git a/test/utils/test_quadrature.py b/test/utils/test_quadrature.py new file mode 100644 index 000000000..3171fafc1 --- /dev/null +++ b/test/utils/test_quadrature.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +import os +import random +import torch +import unittest + +from gpytorch.utils.quadrature import GaussHermiteQuadrature1D +from gpytorch.distributions import MultivariateNormal +from gpytorch.lazy import DiagLazyTensor + + +class TestQuadrature(unittest.TestCase): + def setUp(self): + if os.getenv("UNLOCK_SEED") is None or os.getenv("UNLOCK_SEED").lower() == "false": + self.rng_state = torch.get_rng_state() + torch.manual_seed(1) + if torch.cuda.is_available(): + torch.cuda.manual_seed_all(1) + random.seed(1) + + def tearDown(self): + if hasattr(self, "rng_state"): + torch.set_rng_state(self.rng_state) + + def test_gauss_hermite_quadrature_1D_normal_nonbatch(self, cuda=False): + func = lambda x: torch.sin(x) + + means = torch.randn(10) + variances = torch.randn(10).abs() + quadrature = GaussHermiteQuadrature1D() + + if cuda: + means = means.cuda() + variances = variances.cuda() + quadrature = quadrature.cuda() + + dist = torch.distributions.Normal(means, variances.sqrt()) + + # Use quadrature + results = quadrature(func, dist) + + # Use Monte-Carlo + samples = dist.rsample(torch.Size([20000])) + actual = func(samples).mean(0) + + self.assertLess(torch.mean(torch.abs(actual - results)), 0.1) + + def test_gauss_hermite_quadrature_1D_normal_nonbatch_cuda(self): + if torch.cuda.is_available(): + self.test_gauss_hermite_quadrature_1D_normal_nonbatch(cuda=True) + + def test_gauss_hermite_quadrature_1D_normal_batch(self, cuda=False): + func = lambda x: torch.sin(x) + + means = torch.randn(3, 10) + variances = torch.randn(3, 10).abs() + quadrature = GaussHermiteQuadrature1D() + + if cuda: + means = means.cuda() + variances = variances.cuda() + quadrature = quadrature.cuda() + + dist = torch.distributions.Normal(means, variances.sqrt()) + + # Use quadrature + results = quadrature(func, dist) + + # Use Monte-Carlo + samples = dist.rsample(torch.Size([20000])) + actual = func(samples).mean(0) + + self.assertLess(torch.mean(torch.abs(actual - results)), 0.1) + + def test_gauss_hermite_quadrature_1D_normal_batch_cuda(self): + if torch.cuda.is_available(): + self.test_gauss_hermite_quadrature_1D_normal_nonbatch(cuda=True) + + def test_gauss_hermite_quadrature_1D_mvn_nonbatch(self, cuda=False): + func = lambda x: torch.sin(x) + + means = torch.randn(10) + variances = torch.randn(10).abs() + + quadrature = GaussHermiteQuadrature1D() + + if cuda: + means = means.cuda() + variances = variances.cuda() + quadrature = quadrature.cuda() + + dist = MultivariateNormal(means, DiagLazyTensor(variances.sqrt())) + + # Use quadrature + results = quadrature(func, dist) + + # Use Monte-Carlo + samples = dist.rsample(torch.Size([20000])) + actual = func(samples).mean(0) + + self.assertLess(torch.mean(torch.abs(actual - results)), 0.1) + + def test_gauss_hermite_quadrature_1D_mvn_nonbatch_cuda(self): + if torch.cuda.is_available(): + self.test_gauss_hermite_quadrature_1D_normal_nonbatch(cuda=True) + + def test_gauss_hermite_quadrature_1D_mvn_batch(self, cuda=False): + func = lambda x: torch.sin(x) + + means = torch.randn(3, 10) + variances = torch.randn(3, 10).abs() + quadrature = GaussHermiteQuadrature1D() + + if cuda: + means = means.cuda() + variances = variances.cuda() + quadrature = quadrature.cuda() + + dist = MultivariateNormal(means, DiagLazyTensor(variances.sqrt())) + + # Use quadrature + results = quadrature(func, dist) + + # Use Monte-Carlo + samples = dist.rsample(torch.Size([20000])) + actual = func(samples).mean(0) + + self.assertLess(torch.mean(torch.abs(actual - results)), 0.1) + + def test_gauss_hermite_quadrature_1D_mvn_batch_cuda(self): + if torch.cuda.is_available(): + self.test_gauss_hermite_quadrature_1D_normal_nonbatch(cuda=True) + + +if __name__ == "__main__": + unittest.main()