From 93076e162f0d5a55b4041b7b6e17a5c47b2cfca4 Mon Sep 17 00:00:00 2001 From: Andrei Stoian <95410270+andrei-stoian-zama@users.noreply.github.com> Date: Fri, 10 Dec 2021 20:01:41 +0100 Subject: [PATCH] feat: add GLM example and benchmark, improve quantization (#1115) Starting from sklearn tutorial on PoissonRegression, quantize the regressor and compile to FHE Closes #979, #599, #1132 --- benchmarks/glm.py | 276 ++++++++ concrete/quantization/quantized_array.py | 27 +- .../QuantizedGeneralizedLinearModel.ipynb | 633 ++++++------------ poetry.lock | 228 ++++++- pyproject.toml | 2 + 5 files changed, 703 insertions(+), 463 deletions(-) create mode 100644 benchmarks/glm.py diff --git a/benchmarks/glm.py b/benchmarks/glm.py new file mode 100644 index 000000000..03626ab71 --- /dev/null +++ b/benchmarks/glm.py @@ -0,0 +1,276 @@ +# bench: Full Target: Generalized Linear Model + +from copy import deepcopy +from typing import Any, Dict + +import numpy as np +from common import BENCHMARK_CONFIGURATION +from sklearn.compose import ColumnTransformer +from sklearn.datasets import fetch_openml +from sklearn.decomposition import PCA +from sklearn.linear_model import PoissonRegressor +from sklearn.metrics import mean_poisson_deviance +from sklearn.model_selection import train_test_split +from sklearn.pipeline import Pipeline, make_pipeline +from sklearn.preprocessing import ( + FunctionTransformer, + KBinsDiscretizer, + OneHotEncoder, + StandardScaler, +) +from tqdm import tqdm + +from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule +from concrete.quantization.quantized_activations import QuantizedActivation + + +class QuantizedExp(QuantizedActivation): + """ + Quantized Exponential function + + This class will build a quantized lookup table for the exp function + applied to input calibration data + """ + + def calibrate(self, x: np.ndarray): + self.q_out = QuantizedArray(self.n_bits, np.exp(x)) + + def __call__(self, q_input: QuantizedArray) -> QuantizedArray: + """Process the forward pass of the exponential. + + Args: + q_input (QuantizedArray): Quantized input. + + Returns: + q_out (QuantizedArray): Quantized output. + """ + + quant_exp = np.exp(self.dequant_input(q_input)) + + q_out = self.quant_output(quant_exp) + return q_out + + +class QuantizedGLM(QuantizedModule): + """ + Quantized Generalized Linear Model + + Building on top of QuantizedModule, this class will chain together a linear transformation + and an inverse-link function + """ + + def __init__(self, n_bits, sklearn_model, calibration_data) -> None: + self.n_bits = n_bits + + # We need to calibrate to a sufficiently low number of bits + # so that the output of the Linear layer (w . x + b) + # does not exceed 7 bits + self.q_calibration_data = QuantizedArray(self.n_bits, calibration_data) + + # Quantize the weights and create the quantized linear layer + q_weights = QuantizedArray(self.n_bits, np.expand_dims(sklearn_model.coef_, 1)) + q_bias = QuantizedArray(self.n_bits, sklearn_model.intercept_) + q_layer = QuantizedLinear(self.n_bits, q_weights, q_bias) + + # Store quantized layers + quant_layers_dict: Dict[str, Any] = {} + + # Calibrate the linear layer and obtain calibration_data for the next layers + calibration_data = self._calibrate_and_store_layers_activation( + "linear", q_layer, calibration_data, quant_layers_dict + ) + + # Add the inverse-link for inference. + # This function needs to be quantized since it's computed in FHE. + # However, we can use 7 bits of output since, in this case, + # the result of the inverse-link is not processed by any further layers + # Seven bits is the maximum precision but this could be lowered to improve speed + # at the possible expense of higher deviance of the regressor + q_exp = QuantizedExp(n_bits=7) + + # Now calibrate the inverse-link function with the linear layer's output data + calibration_data = self._calibrate_and_store_layers_activation( + "invlink", q_exp, calibration_data, quant_layers_dict + ) + + # Finally construct out Module using the quantized layers + super().__init__(quant_layers_dict) + + def _calibrate_and_store_layers_activation( + self, name, q_function, calibration_data, quant_layers_dict + ): + # Calibrate the output of the layer + q_function.calibrate(calibration_data) + # Store the learned quantized layer + quant_layers_dict[name] = q_function + # Create new calibration data (output of the previous layer) + q_calibration_data = QuantizedArray(self.n_bits, calibration_data) + # Dequantize to have the value in clear and ready for next calibration + return q_function(q_calibration_data).dequant() + + def quantize_input(self, x): + q_input_arr = deepcopy(self.q_calibration_data) + q_input_arr.update_values(x) + return q_input_arr + + +def score_estimator(y_pred, y_gt, gt_weight): + """Score an estimator on the test set.""" + + y_pred = np.squeeze(y_pred) + # Ignore non-positive predictions, as they are invalid for + # the Poisson deviance. We want to issue a warning if for some reason + # (e.g. FHE noise, bad quantization, user error), the regressor predictions are negative + + # Find all strictly positive values + mask = y_pred > 0 + # If any non-positive values are found, issue a warning + if (~mask).any(): + n_masked, n_samples = (~mask).sum(), mask.shape[0] + print( + "WARNING: Estimator yields invalid, non-positive predictions " + f" for {n_masked} samples out of {n_samples}. These predictions " + "are ignored when computing the Poisson deviance." + ) + + # Compute the Poisson Deviance for all valid values + dev = mean_poisson_deviance( + y_gt[mask], + y_pred[mask], + sample_weight=gt_weight[mask], + ) + print(f"mean Poisson deviance: {dev}") + return dev + + +def score_sklearn_estimator(estimator, df_test): + """A wrapper to score a sklearn pipeline on a dataframe""" + return score_estimator(estimator.predict(df_test), df_test["Frequency"], df_test["Exposure"]) + + +def score_concrete_glm_estimator(poisson_glm_pca, q_glm, df_test): + """A wrapper to score QuantizedGLM on a dataframe, transforming the dataframe using + a sklearn pipeline + """ + test_data = poisson_glm_pca["pca"].transform(poisson_glm_pca["preprocessor"].transform(df_test)) + q_test_data = q_glm.quantize_input(test_data) + y_pred = q_glm.forward_and_dequant(q_test_data) + return score_estimator(y_pred, df_test["Frequency"], df_test["Exposure"]) + + +def run_glm_benchmark(): + """ + This is our main benchmark function. It gets a dataset, trains a GLM model, + then trains a GLM model on PCA reduced features, a QuantizedGLM model + and finally compiles the QuantizedGLM to FHE. All models are evaluated and poisson deviance + is computed to determine the increase in deviance from quantization and to verify + that the FHE compiled model acheives the same deviance as the quantized model in the 'clear' + """ + + df, _ = fetch_openml( + data_id=41214, as_frame=True, cache=True, data_home="~/.cache/sklean", return_X_y=True + ) + df = df.head(50000) + + df["Frequency"] = df["ClaimNb"] / df["Exposure"] + + log_scale_transformer = make_pipeline( + FunctionTransformer(np.log, validate=False), StandardScaler() + ) + + linear_model_preprocessor = ColumnTransformer( + [ + ("passthrough_numeric", "passthrough", ["BonusMalus"]), + ("binned_numeric", KBinsDiscretizer(n_bins=10), ["VehAge", "DrivAge"]), + ("log_scaled_numeric", log_scale_transformer, ["Density"]), + ( + "onehot_categorical", + OneHotEncoder(sparse=False), + ["VehBrand", "VehPower", "VehGas", "Region", "Area"], + ), + ], + remainder="drop", + ) + + df_train, df_test = train_test_split(df, test_size=0.2, random_state=0) + df_calib, df_test = train_test_split(df_test, test_size=100, random_state=0) + + poisson_glm = Pipeline( + [ + ("preprocessor", linear_model_preprocessor), + ("regressor", PoissonRegressor(alpha=1e-12, max_iter=300)), + ] + ) + + poisson_glm_pca = Pipeline( + [ + ("preprocessor", linear_model_preprocessor), + ("pca", PCA(n_components=15, whiten=True)), + ("regressor", PoissonRegressor(alpha=1e-12, max_iter=300)), + ] + ) + + poisson_glm.fit(df_train, df_train["Frequency"], regressor__sample_weight=df_train["Exposure"]) + + poisson_glm_pca.fit( + df_train, df_train["Frequency"], regressor__sample_weight=df_train["Exposure"] + ) + + # Let's check what prediction performance we lose due to PCA + print("PoissonRegressor evaluation:") + _ = score_sklearn_estimator(poisson_glm, df_test) + print("PoissonRegressor+PCA evaluation:") + _ = score_sklearn_estimator(poisson_glm_pca, df_test) + + # Now, get calibration data from the held out set + calib_data = poisson_glm_pca["pca"].transform( + poisson_glm_pca["preprocessor"].transform(df_calib) + ) + + # Let's see how performance decreases with bit-depth. + # This is just a test of our quantized model, not in FHE + for n_bits in [28, 16, 6, 5, 4, 3, 2]: + q_glm = QuantizedGLM(n_bits, poisson_glm_pca["regressor"], calib_data) + print(f"{n_bits}b Quantized PoissonRegressor evaluation:") + score_concrete_glm_estimator(poisson_glm_pca, q_glm, df_test) + + q_glm = QuantizedGLM(2, poisson_glm_pca["regressor"], calib_data) + dev_pca_quantized = score_concrete_glm_estimator(poisson_glm_pca, q_glm, df_test) + test_data = poisson_glm_pca["pca"].transform(poisson_glm_pca["preprocessor"].transform(df_test)) + q_test_data = q_glm.quantize_input(test_data) + + # bench: Measure: Compilation Time (ms) + engine = q_glm.compile( + q_test_data, + BENCHMARK_CONFIGURATION, + show_mlir=False, + ) + # bench: Measure: End + + y_pred_fhe = np.zeros((test_data.shape[0],), np.float32) + for i, test_sample in enumerate(tqdm(q_test_data.qvalues)): + # bench: Measure: Evaluation Time (ms) + q_sample = np.expand_dims(test_sample, 1).transpose([1, 0]).astype(np.uint8) + q_pred_fhe = engine.run(q_sample) + y_pred_fhe[i] = q_glm.dequantize_output(q_pred_fhe) + # bench: Measure: End + + dev_pca_quantized_fhe = score_estimator(y_pred_fhe, df_test["Frequency"], df_test["Exposure"]) + + if dev_pca_quantized_fhe > 0.001: + difference = abs(dev_pca_quantized - dev_pca_quantized_fhe) * 100 / dev_pca_quantized_fhe + else: + difference = 0 + + print(f"Quantized deviance: {dev_pca_quantized}") + print(f"FHE Quantized deviance: {dev_pca_quantized_fhe}") + print(f"Percentage difference: {difference}%") + + # bench: Measure: Non Homomorphic Loss = dev_pca_quantized + # bench: Measure: Homomorphic Loss = dev_pca_quantized_fhe + # bench: Measure: Relative Loss Difference (%) = difference + # bench: Alert: Relative Loss Difference (%) > 7.5 + + +if __name__ == "__main__": + run_glm_benchmark() diff --git a/concrete/quantization/quantized_array.py b/concrete/quantization/quantized_array.py index 0e316985f..5c4830207 100644 --- a/concrete/quantization/quantized_array.py +++ b/concrete/quantization/quantized_array.py @@ -40,13 +40,28 @@ class QuantizedArray: rmin = numpy.min(self.values) if rmax - rmin < STABILITY_CONST: - scale = 1 + # In this case there is a single unique value to quantize + # This value could be multiplied with inputs at some point in the model + # Since zero points need to be integers, if this value is a small float (ex: 0.01) + # it will be quantized to 0 with a 0 zero-point, thus becoming useless in multiplication - # Ideally we should get rid of round here but it is risky - # regarding the FHE compilation. - # Indeed, the zero_point value for the weights has to be an integer - # for the compilation to work. - zero_point = numpy.round(-rmin) + if numpy.abs(rmax) < STABILITY_CONST: + # If the value is a 0 we cannot do it since the scale would become 0 as well + # resulting in division by 0 + scale = 1 + # Ideally we should get rid of round here but it is risky + # regarding the FHE compilation. + # Indeed, the zero_point value for the weights has to be an integer + # for the compilation to work. + zero_point = numpy.round(-rmin) + else: + # If the value is not a 0 we can tweak the scale factor so that + # the value quantizes to 2^b - 1, the highest possible quantized value + + # TODO: should we quantize it to the value of 1 what ever the number of bits + # in order to save some precision bits ? + scale = rmax / (2 ** self.n_bits - 1) + zero_point = 0 else: scale = (rmax - rmin) / (2 ** self.n_bits - 1) if rmax != rmin else 1.0 diff --git a/docs/user/advanced_examples/QuantizedGeneralizedLinearModel.ipynb b/docs/user/advanced_examples/QuantizedGeneralizedLinearModel.ipynb index 2090d4b8a..e3d3302e9 100644 --- a/docs/user/advanced_examples/QuantizedGeneralizedLinearModel.ipynb +++ b/docs/user/advanced_examples/QuantizedGeneralizedLinearModel.ipynb @@ -5,9 +5,9 @@ "id": "b760a0f6", "metadata": {}, "source": [ - "# FIXME(Andrei): To be done with 979\n", + "# Generalized Linear Model : Poisson Regression\n", "\n", - "FIXME(Andrei): to be done with 979!" + "Currently, **Concrete** only supports unsigned integers up to 7-bits, for both parameters, inputs and intermediate values such as accumulators. Nevertheless, we want to evaluate a linear regression model with it. Luckily, we can make use of **quantization** to overcome this limitation!" ] }, { @@ -25,7 +25,19 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np" + "from copy import deepcopy\n", + "import numpy as np\n", + "\n", + "from sklearn.linear_model import PoissonRegressor\n", + "from sklearn.datasets import fetch_openml\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.decomposition import PCA\n", + "from tqdm import tqdm\n", + "\n", + "from concrete.quantization import QuantizedLinear, QuantizedArray, QuantizedModule\n", + "from concrete.quantization.quantized_activations import QuantizedActivation\n", + "\n", + "import concrete.numpy as hnp" ] }, { @@ -54,7 +66,7 @@ "id": "53e676b8", "metadata": {}, "source": [ - "### We need an inputset, a handcrafted one for simplicity" + "### We need an inputset, get one from OpenML for insurance claims" ] }, { @@ -64,8 +76,28 @@ "metadata": {}, "outputs": [], "source": [ - "x = np.array([[69], [130], [110], [100], [145], [160], [185], [200], [80], [50]], dtype=np.float32)\n", - "y = np.array([181, 325, 295, 268, 400, 420, 500, 520, 220, 120], dtype=np.float32)" + "df = fetch_openml(data_id=41214, as_frame=True, cache=True, data_home=\"~/.cache/sklearn\").frame\n", + "df = df.head(50000)" + ] + }, + { + "cell_type": "markdown", + "id": "4690cc15", + "metadata": {}, + "source": [ + "### We want to predict the frequency of insurance claims. Our example will only use a single predictor feature so we can easily visualize results\n", + "### First, compute the target value from the input dataset\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5e163891", + "metadata": {}, + "outputs": [], + "source": [ + "df[\"Frequency\"] = df[\"ClaimNb\"] / df[\"Exposure\"]" ] }, { @@ -73,31 +105,20 @@ "id": "75f4fdb7", "metadata": {}, "source": [ - "### Let's visualize our inputset to get a grasp of it" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "2a124a62", - "metadata": {}, - "outputs": [], - "source": [ - "plt.ioff()\n", - "fig, ax = plt.subplots(1)" + "### Let's visualize our inputset to get a grasp of it. The target variable, \"Frequency\" has a poisson distribution" ] }, { "cell_type": "code", "execution_count": 5, - "id": "edcd361b", + "id": "2a124a62", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ - "
" + "
" ] }, "metadata": {}, @@ -105,7 +126,17 @@ } ], "source": [ - "ax.scatter(x[:, 0], y, marker=\"x\", color=\"red\")\n", + "plt.ioff()\n", + "fig, ax = plt.subplots(1,2,figsize=(15,7))\n", + "fig.patch.set_facecolor('xkcd:mint green')\n", + "ax[0].set_title(\"Frequency of claims vs. Driver Age\")\n", + "ax[0].set_xlabel(\"Driver Age\")\n", + "ax[0].set_ylabel(\"Frequency of claims\")\n", + "ax[0].scatter(df[\"DrivAge\"], df[\"Frequency\"], marker=\"o\", color=\"red\")\n", + "ax[1].set_title(\"Histogram of Frequency of claims\")\n", + "ax[1].set_xlabel(\"Frequency of claims\")\n", + "ax[1].set_ylabel(\"Count\")\n", + "df[\"Frequency\"].hist(bins=30, log=True, ax=ax[1])\n", "display(fig)" ] }, @@ -116,36 +147,18 @@ "source": [ "### Now, we need a model so let's define it\n", "\n", - "The main purpose of this tutorial is not to train a linear regression model but to use it homomorphically. So we will not discuss about how the model is trained." + "### First let's split the data, keeping a part of the data to be used for calibration. The calibration set is not used in training nor for testing the model" ] }, { "cell_type": "code", "execution_count": 6, - "id": "91d4a1da", + "id": "d81db277", "metadata": {}, "outputs": [], "source": [ - "class Model:\n", - " w = None\n", - " b = None\n", - "\n", - " def fit(self, x, y):\n", - " a = np.ones((x.shape[0], x.shape[1] + 1), dtype=np.float32)\n", - " a[:, 1:] = x\n", - "\n", - " regularization_contribution = np.identity(x.shape[1] + 1, dtype=np.float32)\n", - " regularization_contribution[0][0] = 0\n", - "\n", - " parameters = np.linalg.pinv(a.T @ a + regularization_contribution) @ a.T @ y\n", - "\n", - " self.b = parameters[0]\n", - " self.w = parameters[1:].reshape(-1, 1)\n", - "\n", - " return self\n", - "\n", - " def evaluate(self, x):\n", - " return x @ self.w + self.b" + "df_train, df_test = train_test_split(df, test_size=0.2, random_state=0)\n", + "df_calib, df_test = train_test_split(df_test, test_size=100, random_state=0)\n" ] }, { @@ -153,7 +166,7 @@ "id": "faa5247c", "metadata": {}, "source": [ - "### And create one" + "### Train the scikit-learn PoissonRegressor model " ] }, { @@ -161,9 +174,21 @@ "execution_count": 7, "id": "682fb2d8", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "PoissonRegressor(alpha=1e-12, max_iter=300)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "model = Model().fit(x, y)" + "reg = PoissonRegressor(alpha=1e-12, max_iter=300)\n", + "reg.fit(df_train[\"DrivAge\"].values.reshape(-1,1), df_train[\"Frequency\"])" ] }, { @@ -181,8 +206,8 @@ "metadata": {}, "outputs": [], "source": [ - "inputs = np.linspace(40, 210, 100).reshape(-1, 1)\n", - "predictions = model.evaluate(inputs)" + "test_data = np.sort(df_test[\"DrivAge\"].values).reshape(-1,1)\n", + "predictions = reg.predict(test_data)" ] }, { @@ -190,7 +215,7 @@ "id": "f28155cf", "metadata": {}, "source": [ - "### Let's visualize our predictions to see how our model performs" + "### Let's visualize our predictions to see how our model performs. Note that the graph is on a Y log scale so the regression line looks linear" ] }, { @@ -201,7 +226,7 @@ "outputs": [ { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEWCAYAAACJ0YulAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAAAsS0lEQVR4nO3deVhUZRsG8HvYNxlAwIUdF1BRUQGzTK1c0iI1TTPMPdIsNa2syFwStU1b/NTIXFJELXPLskxzzVQUVERwQRZxQfZ9m5nvj4mDKMMMyGzM/bsur5lzZubMc8bhvee871lEK2TrZSAiIgJgpO0CiIhIdzAUiIhIwFAgIiIBQ4GIiAQMBSIiEjAUiIhIwFAgg3X92BUs8flAZ98/OzkTb4smQlIpeaT3uXY4AQtcZ9f7MTJMJtougPTbIs93UHg3HyJjI5jbmMP32c4YsXIszG0stF2aUm2ebI8PE5fqzPsv8nwHo9dOhE//TlqriYhbCvTIJu+diU8L1+Cd2EVIj0nFX0v3Nfp7POqvZdIs/n/pL24pUKOxbSmGzyA/pMemCvOS/72O3bOjcCf+Fhw8HDH861fQtp8vACDrxj1sGb8W6TGpcO/pDWeflijNK8bYza8jOzkTn3i9i9FrJ+KPhbvh4OmIt45+gFPrjuLQ5/tRcCcP7kFeGBUxAQ4ejpDJZNg1eyvORZ5ERWkFHDya49WoqWjl54r4385jzzvbkZuWDQtbC/R9eyCeemcwrh1OwOaxEVhwczkA4O7lW/hp2o9Ij02F2MUezy8dCb8XugEAtkxYCzNrc2QnZyLp6BW06Ngar255HY5tnB/6HCLHf4/WXdzw1JxnkZueg4WuszFi5Vj0nv4MMq9nYEXgInyS+Q2Sjl4R3n/zqxHITc3GD8FfQ2RshIEfv4Buo4IAAGcjT+L3eTtRUVyOvm8PxICw4Fo/f0Xr+aCj3xzAP2sOY+qfcx56LO9WDn55KxLXjybC3Ea+jD4zBgAAUk4nYefMLbh7+RZMLc3QdUQPDF0+BiZm8mbkbdFEjFg5Fke++hPSSinGrJ+MzWMj0PftgTj06W8QGRvhuSUj0HPikyp/p0jzGArUaHJvZiPh9wto+3QH+XR6Dr5/bgVCNr0G32c74+rBy1g/YiU+SFgCGydbbHrlO3g90RbT/noXqaeTEDFkBfxe8K+xzOtHEvH+5SUQGYlwcfc5/LVkH6bsnQnHdi1wcNk+bBqzBjP/+QiJf8Yh6WgiPryyDBZiS2Qk3IaFnRUAYOvk9Ri//Q20ebI9inOKkHXj3kO1SyoqsTb4awRNehJT/3wHN45fwQ9Dv8Xs6I/h7NMKABCz9RRCf58N1+4e2DJ+LX4L24FxW6c9tKy2fX1wYec5PDXnWVw/koDm3k64fjQRvac/g+tHEuD9ZDsYGdXcSB+7KRRJx67U6D7KTs4EANw4fhUfJC7FvSt3sCLoE3R5sQdadGj90Puqsp5/LNqNi7vO4c0jc2HjZIvMaxnCY1KpFGuDv4bf0G54NWoqcm9mY3X/L+Ds0xK+gzrDyNgIw1aMgVuAJ/Ju5uC7wctxYtUh9J01UFjGxV3nMOvUPJhamiH1VBIK7uShNK8EC9KXI/HAJWwYuQqdh3WHlb31w18g0gnsPqJHtm7Yt3i/2TQsdJsDG2dbDF44HABwdvM/6DCkCzoO6QojIyP4DOgEtwBPxP92ATmpWUg7cwODFw2HiZkJvHu3fygQAGDQgqEwtzaHmaUZ/llzGM988BxadGgNYxNj9P/weaTHpiE7JRPGpsYoKyjF3YTbkMlkaNGhNcSt7AAAxqbGuBufjtL8EljZW8Otu+dD75P8bxLKCkvxzPtDYGJmgnZPd0TH57viXNQp4Tmdh3eHR5A3jE2M0SPksRpbRPdr09cXN45fhVQqxfWjV/D0e4Nx48Q1AMC1I4lo09e3Xp/voPlDYWZpBpeu7nDp6ob082m1Pq/O9ZTJsGt2FBL/vITpf8sD4UFpZ26g8F4BBn08FCZmJnD0dkav1/ogZutpAIBbD094PtYGxibGcPB0xOOv98P1I4k1ltH/g+dg7WADM0szoaaBH78AY1MTdBzSFeY25shIvFOv9SfN4pYCPbJJu96CT/9OuHYkAZtf+Q6FmQWwtLNCTkoWzv90Bpf2xgrPlVZI0PapDsi7lQsrB2uYWZkLj9m5OSA3LbvGsu3dHIT7OSlZ2DlzC3bP2Vr9BJkMeek5aPd0R/R+8xnsmL4JOSlZ6PxiDwz9YjQsbC0xccd0HFi8F7++/zNad3HD88tGwrNX2xrvk38rB3ZuDjV+wdt7NEdeeo4wbdtSLNw3szJHWWFZrZ+HYxtnmFmbIz02FUnHrmDgvBdw6odjyEi8jetHEoXuGFU1u+99Ta3MUF5YWuvz6lrPktxinIw4gnHbpsFSbFXr67NTspB/Kxcf2L0hzJNJZPB+sh0AIOPKHeyevRVp0TdQXlwOaaUUrj08aizD7r7/LwCwam4DYxPjGvWXKaifdANDgRpN276+CJzQG3ve2YbJu2bAzs0BAa8+jtHfT3zoudkpmSjOLkJ5cZkQDA8GAgBAJBLu2rnZY0DY8+gR0qvW9+8zYwD6zBiAgox8bBy1Coc+/x1DPnkR7oHemLx7JiQVlTi28iA2jlqF+WnLa7zWtrU9ctOyIZVKhWDITc2CU/uWDfos2vT1wYWfoyEpr4Sdiz3a9PXBmY0nUJJTDBd/91pfI7pvXRuirvW0tLfG2M2h2DhqFSbufAveT7R76PX2bg5w8HJE2NVPa13+z9N+hEs3d7wa9TosmlniyFd/4vzP0Q+uxCOtA2kfu4+oUfWdNQBXDlxC+vlU9BjbC5f2xiLhj4uQSqSoKK3AtcMJyL2ZDQcPR7gFeGL/gt2oLK9E8slruLT3fJ3LfnzqU/hr6T7cvpQOACjJK0bsT2cAAKlnkpBy6jokFZUwszaHqYUpREYiVJZX4mzkSZTkFcPY1AQWtpYQGT38tffo6Q1TK3Mc+ux3SCoqce1wAi7tPY9uL/ds0OfQpq8Pjq08iDZ9fOTT/XxxbOVBePVuByPj2v/smrWwRVbSw+MAqlBlPdv288XYyNex/sWVSDmd9NAy3IO8Yd7MAgc/3YfyknJIJVLcjruJ1DPy55YVlMLC1hLmNha4m3AbJ1b/3aBaSbdxS4EalY2TLQLGPYE/F+3BxB1vYvLuGdj73k/YNOY7iIxFcA/yxkurxwEAxka+ji0T1uKj5m/BPcgL/qODIJNIFS67y/AeKCssw6aXVyM7JQuWYku0H9AJ/i8FojS/FLvejkJW0j2YWpjCZ5Afnn5XvudN9KZ/sOPNzZBKpHD2aYmxkaEPLdvEzART9s7Ejjc24eDSfRC72OOVH6eghW+rBn0Obfv6oKygFN592gMAvHu3Q0VxOdr8N12bZz54Dr+8FYm9723HwI+C0XVkYL3eU5X19BnQCS+vm4S1wV8j9Pe3azxmZGyE136dhd1ztmGx17uoLKuEs09LDF78IgDghS9GY3voBhz67He4dHNHt9FBuHrocr1qJN0n4kV2SFdsHL0Kzr6thIFqItI8dh+R1qSeSULm9QxIpVJc3n8Rcbtj0HlYd22XRWTQ2H1EWpN/Jx/rX1yJoqwi2LnaY+TqcXDt5qH8hUSkNuw+IiIiAbuPiIhIoNfdR4sd34Gnp6e2yyAi0isJyYlYnPltrY/pdSh4enoiOjpa+ROJiEjgFuCl8DF2HxERkYChQEREAoYCEREJGApERCRgKBARkUAvQyFubyy2hW5AXl6etkshQxIZCXh6AkZG8tvISG1XRNTo9DIU/IL9MTpiAsRisfInEzWGyEggNBRISQFkMvltaCiDgZocvQwFIo0LCwOKi2vOKy6WzydqQhgKRKpIrf16zArnE+kphgKRKtxrv4SmwvlEeoqhQKSK8HDA6oEL3ltZyecTNSEMBSJVhIQAERGAh4f84vQeHvLpkBBtV0bUqPT6hHhEGhUSwhCgJo9bCkREJGAoEBGRQC+7j+L2xuLS3lge0UxE1Mj0ckuBRzQTEamHXoYCERGpB0OBiIgEDAUiIhIwFIiISMBQICIiAUOBiIgEDAUiIhIwFIiISMBQICIiAUOBiIgEDAUiIhLwhHhERCTQyy0FnhCPiEg99DIUiIhIPRgKREQkYCgQEZGAoUBERAKGAhERCRgKREQkYCgQEZGAoUBERAKGAhERCRgKREQkYCgQEZGAoUBERAKGAhERCRgKREQkYCgQEZGAoUBERAKGAhERCRgKREQk0KlrNF/cdQ7x+86jNL8EPSf3ge9AP22XRERkUNQeClGTfkD8r+dh42yLuXGLhfmX91/EzplbIJNI0XNKH/R//zl0HtYdnYd1R3FOEfa8s42hQESkYWrvPgqa0Buh+2fXmCeVSLFj+iaE/v425saHIybqFO7EpwuPH1i8F09Mf1rdpRER0QPUHgpt+vjA2sGmxrzU00lwbOsMR29nmJiZoNvLQYjbHQOZTIa9c7fDd3BnuHX3VHdpRET0AK2MKeSm58DOzUGYFrs6IPXUdRz79i9c+SseJXklyLyWgSemPvXQa/+JOIyTEUcAAEb3pBqrmYjIEOjUQHOfGQPQZ8aAOp/zeGg/PB7aDwCwOWClBqoiIjIcWtkl1c7FHrlp2cJ03s1siF3stVEKERHdRyuh4BbohXtXM5B14x4qyysRs/U0Or3QTeXXx+2NxbbQDcjLy1NjlUREhkft3Uc/jlmDa4cTUJRZiAWus/HswmF4bHIfjFgZgu8GfQmpRIqek55Eq04uKi/TL9gffsH+7D4iImpkag+FcVFTa53fcUhXdBzSVd1vT0RE9cDTXBARkaBeWwrFOUXITctG6y5u6qpHJXF7Y3FpbyzHFIiIGpnSLYWV/ZahNL8ERdmF+LL7Amx7bQN2zY7SQGmK+QX7Y3TEBIjFYq3WQUTU1CgNhdK8EljYWuLiL2cRMO5xvH1qHq78Fa+J2oiISMOUhoK0UoK827mI2X4GnZ7nwDARUVOmNBQGfjwU3w36Ek5tneEe6I3MpAw4tWuhidqIiEjDlA40+78UCP+XAoVpR29nTNzxplqLUoYDzURE6qE0FLJu3MOxb/9CdnImpJXVJ6CbsmemWgurCw9eIyJSD6WhsG7Yt+g5+Ul0CvaHyEikiZqIiEhLlIaCiYWp0jOXEhFR06A0FPrM7I/9C3fBd6AfjM2rn86L4BARNT1KQ+H2xZuI3nQS1w4lVHcfiYDph+aquzaFONBMRKQeSkPh/E/R+CjpM5iY6c71eDjQTESkHkqPU2jp54KS3GJN1EJERFqm9Od/SW4xlvl+CLdAT5iYmwrztblLKhFpUWQkEBYGpKYC7u5AeDgQEqLtqqiRKA2FZxcO00AZRKQXIiOB0FCg+L/eg5QU+TTAYGgilIZC276+mqiDiPRBWFh1IFQpLpbPZyg0CQpD4ZveSzDj+Id4v9k04P5j1mQARMCy/NXqr04B7n1EpCWpqfWbT3pHtEK2XqbtIhpqc8BKREdHa7sMIsPh6SnvMnqQhweQnKzpaqiB3AK8MCd6fq2PqXw5zoKMfOSkZgn/iMgAhYcDVlY151lZyedTk6B0TCFuTwx2z9mK/Fu5sHG2RU5KFpw7tML7l/glIDI4VeMG3PuoyVK6pfD7vJ2Y9e88OLVviXk3Pse0g+/C87E2mqiNiHRRSIi8q0gqld8yEJoUpaFgZGoM6+Y2kEllkEqlaPdUB6RFJ2ugNCIi0jSl3UeWdlYoKyyFd5/22BwSARvnZjCzNtNEbUREpGFKQ2Hy7hkwtTDFsBVjcDbyJErzSjDo46GaqE0h7pJKRKQeSkPB3NpcuB80vrdai1EVT4hHRKQeCkNBOGjtv4PVBDpw8BoREamHwlBYVsBGn4jI0Cjd+yj53+soLSgRpksLSpBy6rpaiyIiIu1QGgo/T/sR5jYWwrSZtTl+mvajWosiIiLtUBoKMpkMIlH1oIKRkRGklVK1FkVERNqhNBSaezvh6DcHIKmohKSiEke+/hPNvZ00URsREWmY0l1SX1ozHjtnROLA4r2ASIT2z3TAqIgJGiiNiIg0TWkoNHO2xbit0zRRCxERaZnSUNBFPKKZiEg9VL6egi7xC/bH6IgJEIvF2i6FiKhJURgKR77+EwCQdOKqxoohIiLtUhgKp9cfBwD88tZmjRVDRETapXBMoUWH1ghvNxf5t3LxWZd51Q/IZIBIhPcufKKJ+oiISIMUhsK4qKnIv5OH7wZ9icl7ZmiyJiIi0pI69z6ybSnGu+cXobK8Eveu3AEAOPu0hLGpXu60RERESiht3a8dScCWcWvh4OkImUyG3LRsvLJxCtr08dFEfUREpEFKQ2H37K2Y+uccOPu0AgBkXLmDTWPWYM7ZBequjYiINEzpcQqSCokQCADg3L4lJBUStRZFRETaoXRLwS3AE1unrEPA2McBAGcjT8ItwFPddRERkRYo3VJ4afU4tOzogqPfHMDRbw6gRcfWeGn1OE3URuoWGQl4egJGRvLbyEhtV0REWqZ0S8HE3BT9Zg9Cv9mDNFEPaUpkJBAaChQXy6dTUuTTABASor26iEir9PLcR9QIwsKqA6FKcbF8PhEZLL084IBnSW0Eqan1m09EBkHplsKti2maqKNeeJbURuDuXr/5RGQQlIbCz29swoqgRTi+6hBK8oqVPZ30RXg4YGVVc56VlXw+ERkspaEw49iHGBsZity0LCzvsRCbXlmDxAOXNFEbqVNICBARAXh4ACKR/DYigoPMRAZOpTEFp3YtMWTxCLgFeGHnjEjcjEkFZDI8t2QEurwYoO4aSV1CQhgCRFSD0lC4dSENp9Yfx+V959F+QCdM3jsTbt09kXcrB1/3CmcoEBE1IUpD4Ze3ItFzSh88t2QEzCzNhPni1vYYvPhFtRZHRESapTQUXts3C6aWZjAylg8/SKVSVJZWwMzKHIGvPq72AomISHOUDjSv7v85KkrKhemK4nKs7v+5WosiIiLtUBoKFaUVMLexEKbNbSxQXlxexyuIiEhfKQ0FM2tzpJ1LFqbTzibD9L6xBSIiajqUjikM/2oMNr60Crat7QAZUHAnD+O2TdNAaUREpGlKQ8E90BsfJCxBRiKv0UxE1NSp1LqnnrmB7ORMSCuluHkuBQAQOO4JtRZGRESapzQUNr8agazrGXDxd4fov91SRSIRQ4GIqAlSGgpp0cl4Pz4cIpFIE/UQEZEWKd37qJWfC/Lv8LoFRESGQOmWQlFmIT7tGAb3IC+YmJsK86fsmanWwoiISPOUhsKgBUM1UQcREekApaHQtq8vslMyce/qXfj074Ty4jJIJVJN1EZERBqmdEzh5PdHsGHk//DT6xsBAHnpOVg37NtGLyQzKQNbJ6/D+pH/a/RlExGRapSGwvH/HcSME2GwsLUEIL/gTmFGvkoLj5r0A+Y5z8Cnfh/VmH95/0Us8fkA4W3n4q9l+wAAjt7OePmHSfWtn4iIGpHSUDAxN4WJWXUvk6RSIr98owqCJvRG6P7ZNeZJJVLsmL4Job+/jbnx4YiJOoU78en1LJuIiNRB6ZhCm74+OLDkV1SUlCPxwCWcWHUInYL9VVp4mz4+yE7OrDEv9XQSHNs6w9HbGQDQ7eUgxO2OQcuOLiot85+IwzgZcQQAYHSPYxtERI1J6ZbC88tGwsapGVp1dsU/3x1GhyFdMOQRrriWm54DOzcHYVrs6oC89BwUZRVi+9SNSI9JwV9Lf1X4+sdD+2FO9HzMiZ4PJyenBtdBREQPU7qlYGRkhF6v9UWv1/qqtRDr5jYYtWa8Wt+DiIjqpjQUPvF6t9YxhHlJnzXoDe1c7JGbli1M593MhtjFvl7LiNsbi0t7Y5GXxyOtiYgak9JQmB09X7hfUVqB8z+dQXF2UYPf0C3QC/euZiDrxj2IXewRs/U0xm55vV7L8Av2h1+wPzYHrGxwHaQjIiOBsDAgNRVwdwfCw4GQEG1XRWSwlI4pWDe3Ef7Zudij76yBiN93XqWF/zhmDb7qtRgZiXewwHU2/v3hKIxNjDFiZQi+G/QllnX4EP6jAtGqk2qDzNTEREYCoaFASgogk8lvQ0Pl84lIK5SfJfW+S3HKpDKkRSdDWqnaXj/joqbWOr/jkK7oOKSrahVS0xUWBhQX15xXXCyfz60FIq1QGgp75mwT7huZGMHB0xHjt2v3cpwcU2giUlPrN5+I1E5pKEz/e64m6qgXjik0Ee7u8i6j2uYTkVYoDYXDy/+o8/F+swc1WjFkYMLD5WMI93chWVnJ5xORVigdaE6LvoETqw8hLz0Heek5+GfN37h5LhmlBSUoLSjRRI1NU2Qk4OkJGBnJbw1xcDUkBIiIADw85Ls9e3jIpzmeQKQ1SrcUcm/mYM65BbBoJj8h3qAFQ/H9c19h7Ob67UZK96na66bqF3LVXjeA4TWIISGGt85EOkzplkLB3fwaJ8QzMTNBwV3VzpKqLnF7Y7EtdIP+DjTXtdcNEZEWKd1SCBz3OFYEfYLOw7sDAC7uOofA8U+ovbC66P1AM/e6ISIdpTQUBoQFw3dwZyQduwIAGLN+Mly7eai9sCaNe90QkY5S2n0EABXF5bCwtUTfmQNh52qPrBv31F1X0xYeLt/L5n7c64aIdIDSUNi/cBcOfvobDi6VXyFNUiHB5rERai+sSeNeN0Sko5SGwsWd5zBlz0yYWZsDAMSt7VFWUKr2wuqi9wPNgDwAkpMBqVR+y0AgIh2g/HKcZiYQiUTAf2fPLisqU3dNSvkF+2N0xASIxWJtl0JE1KQoHWj2HxWI7a9vQEluCU5+fwSn1h3DY2q+4A4REWlHnaEgk8ngPzoIGQl3YGFriYzE2xi8aDh8BnTSVH1ERKRBdYaCSCTC90NW4L2LixkEREQGQOmYgkt3D6SeSdJELZrBcw4RESmkdEwh9VQSvtp8Eg6ejvI9kGQyQCTCexc+0UR9tWrw9RR4ziEiojqJVsjWy2p7ICc1C/buzZGdklnrCx08HNVamCo2B6xEdHS06i/w9Kz9SGIPD/luoUREBsAtwAtzoufX+pjC7qMfhn0DQN747569FQ4ejjX+6SWec4iIqE6KxxTu237ISmoip7VQdG4hnnOIiAhAXaEgUnBfn/GcQ0REdVI40HzrfBret50GyICKknL5fUC+BSECluWv1lCJjahqMDksTN5l5O4uDwQOMhMRAagjFJZL1mmyDs3hlb6IiBRSukuqLmrwLqlERFQnla6noGt4QjwiIvXQy1AgIiL1YCgQEZGAoUBERAKGAhERCRgKREQk0MtdUomImrLyciArS/4vM7P69v7706YBvXo1/nszFIiI1KiqgX+wUa9tXtX9/HzFy2vWDGjeHBg+XD31MhSIiFRUVvZwo15X456ZCRQUKF5es2aAo6O8kXd0BHx8qu/fP7/qfvPmgLm5etdRL0OBRzQT0aO6v4FXpXHPzAQKCxUvr6qBr/rn61t7w15138FB/Q18Q+hlKPgF+8Mv2B+bA1ZquxQi9YmM5MkbVVRaWrMPXpVGvq4G3ta2+pe5k1N1A39/w64PDXxD6GUoEDV5Bnzp2Ad/wT/YqNfWyCtr4KsacWdnoGNH5V00ZmaaW19dw1Ag0kVhYdWBUKW4WD5fj0Khtl/wtfXHq9pFU9XAOzpWN/DKumgMuYFvCIYCkS7SwUvHVjXwqva/K/sFLxZXN+BOTkCHDop/vbOB1xyGApEucneXdxnVNr8RlJYqbtAVNfJFRYqXJxZXN+AtWgCdOtXe917VPWPoXTS6jKFApIvCw2uOKQAKLx1bUlL/vWge7Jm6n51ddUPesqW8gVf2C97UtPE/AtIOhgKRDhEa+E4hyJrRAplrd8kbdLE3MnsOQeY+X2RubFgD36oV4OenvA+eDbxhYygQqUlJSd17ztTWXVOzge//3z8AeYDd6Zq/4Dt3rnsvGjbw1BAMBSIVFBfX3ojX1S/fkF/wdfXBs4EnTWAokMEpLlZtkPX+6ZISxcuzs6tuxFu3Brp0qdmw19YHb8K/PNJR/GqSXqtq4FX9BZ+ZKd/zRhF7++oG3NUV6Nr14V/tTk41u2iaVAPPo6gNXlP6OpMek8lqdtGouhdNXQ28g0P1r3M3N8DfX/GpCppkA19fBnwUNVUz5D8BUpOqBl7V/d+rphvSwCvqprG3N/AGviGayFHU9Gj08s+GZ0nVHEUNvLIGX1EDLxLJ++CrumDc3YHu3WsfXHVyYgOvUTp4FDVpnl7+qfEsqQ3zYAOvahdNWVntyxOJav6Cr2rgFTXujo7yBt7YWLPrTSpS81HUpB/0MhRI3sAXFam258z991Vt4D08Hm7ga+uiYQPfhNTjKGpquhgKOuD+Br4+u0qq0sA7OQGenkBAgOJ94PkLngBUjxtw7yODxlBoZDKZ/MyQ9d2Lpry89uUZGckb7KouGC+v6gZeUTeNnR0beGqgkBCGgIFjKNShqoGvz6mClTXwDg7VjbmXFxAYqPhqTo6O8gbeyEijq01EBswgQyE5GUhIUK2RV6WBb94caNMGCAqq+1QF9vZs4IlItxlkKKxbB3zySfV0VQNf1QXj7V3dwCvaVZK/4DWMR9oSaYRBhsLEicDgwdUNPht4HccjbYk0xiCbQi8voFcvoH17+RYCA0HH1XWkbUNERsp3yTIykt9GRj5qhURNBptD0n2NeaRt1VZHSop8T4KqrQ4Gg/YwpHUKQ4F0n6IjahtypG1jb3XQo2FI6xyGAum+8HD5kbX3a+iRtjy/j25hSOschgLpvpAQICJCfu4NkUh+GxHRsEHmxtzqoEfHkNY5DAXSDyEh8gNMpFL5bUP3OmrMrQ56dAxpncNQIMPSmFsd9OgY0jrHII9TIAPH8/voDp6ET+cwFIhIuxjSOoXdR0REJGAoEBGRQGdCoayoDJHjv8e219bjbORJbZdDjUGVI1XfeEN+AWaRSH77xhuarrJ2hnqUrSrrbSifja6up5rrUuuYQtSkHxD/63nYONtibtxiYf7l/Rexc+YWyCRS9JzSB/3ffw4XfjmLriMD4Rfsj42jV6FHSC91lkbqpspJ7N54A1i9uvo1Ekn19KpVmqv1QYZ6Aj5V1ttQPhtdXU8N1KXWLYWgCb0Run92jXlSiRQ7pm9C6O9vY258OGKiTuFOfDrybmbD3s1BXpSxzmzAUEOpcqRqRETtr1U0X1MM9ShbVdbbUD4bXV1PDdSl1i2FNn18kJ2cWWNe6ukkOLZ1hqO3MwCg28tBiNsdA7GrA3JvZsPF3x0yqUzhMv+JOIyTEUcAAEb3pOornh6NKkeqSiS1P0fRfE0x1KNsVVlvQ/lsdHU9NVCXxn+S56bnwO6/LQIAELs6IC89B11e7IELO87ip2k/olOwv8LXPx7aD3Oi52NO9Hw4OTlpoGJqEFWOVFV0IWltX2DaUI+yVWW9DeWz0dX11EBdOtNPY25tjjHrJ+Ol1eM4ntAUqHKkalVf6IMUzdcUQz3KVpX1NpTPRlfXUwN1aTwU7FzskZuWLUzn3cyG2MW+XsuI2xuLbaEbkJeX19jlUWNR5XQSq1YB06ZVbxkYG8untTnIDBjuqTBUWW9D+Wx0dT01UJdohWy94g78RpCdnInvn/9K2PtIUinBkvYf4I2D70LsYo8VgYswdsvraNXJpd7L3hywEtHR0Y1dMhFRk+YW4IU50fNrfUytA80/jlmDa4cTUJRZiAWus/HswmF4bHIfjFgZgu8GfQmpRIqek55sUCAQEVHjU2sojIuaWuv8jkO6ouOQrup8ayIiagC9PCFe3N5YXNobyzEFIqJGpjN7H9WHX7A/RkdMgFgs1nYpRERNil6GAhERqYdedh9VSUhOhFuAV4NeW3SvANZOzRq5Is3R5/r1uXZAv+vX59oB1t9Ych4408T91L5Lqq76MmChwl2y9IE+16/PtQP6Xb8+1w6wfk1g9xEREQkYCkREJDDYUOgV2lfbJTwSfa5fn2sH9Lt+fa4dYP2aYLBjCkRE9DCD3VIgIqKHMRSIiEig18cpqConLQtbxq1Fwd18QCTv1+s7cyCKsgvx4+jVyE7OhIOnI8ZvfwNW9tbaLreGitIKrOyzFJVllZBUStB1ZAAGLxyOrBv38OPLa1CcVQjXHh4I2RQKEzPd/e+USqRYHrAQYhd7vPbrLL2pf5HnO7BoZgGRsRGMTIwxJ3q+XnxvqpTkFmPrlPW4E3cTEIkwZt0kOPm01Iv6MxJvY+Po6mt4ZyXdw+BFwxEw7nG9qP/wij/w79qjEIlEaNXZFWPWT0b+7Vyd/94bxJhC3u1c5N/OhVt3T5QWlGB5j4WYtOstnN5wAlYO1uj//nP4a9k+lOQUIfjTUdoutwaZTIbyojKY21hAUlGJb3ovxfCvX8Hh5X+gy4s90P3lntg+dSNcurrhiWlPa7tchQ4v/wNp0TdQml+K136dhQ2jVulF/Ys838Hs6Pmwcaw+4GjPe9t1/ntTJXL892jzZHs8NqUvKssrUVFcjgNLftWb+qtIJVIscHkbs07Nw/H/HdL5+nPTc/Bt7yWYGx8OM0szbBi1Ch2HdEH8bxd0/ntvEN1H4lZ2cOvuCQCwaGaJFh1aIS89F3G7YxA4/gkAQOD4J3BxV4wWq6ydSCSCuY0FAEBSIYGkohIiEXDt0GV0HRkAAAga/wQu7jqnzTLrlHszG/H7zuOxKX0AyINOn+p/kD58bwCgJK8YSUevoOdk+eduYmYCSzsrvan/flcOxqN5G2c4eDjqTf3SSgkqSsohqZSgorgctq3EevG9163tFg3ITs7EzZhUePT0RsHdPIhb2QEAbFuKUXBXN8+6KpVI8WWPBci8loHe059G8zbOsLSzgrGJ/IplYld75KXnarfIOuycFYXgz0ahrKAUAFCUVag39YtEIqwZ+AVEIhF6vd4Pj4f205vvTfaNTNg4NUPUxB9w63waXHt4YPjXIXpT//1itp5C9zE9AUAv6rdzsUe/d57FIvd3YGppCp+BfnDt4akX33uDCoWywlKsH7ESw78aAwtbyxqPiUQiiEQiLVVWNyNjI7wbuwglucVYN/xbZCTc1nZJKrv0ayyaOTeDWw9PXDucoO1y6u2t4x/CzsUeBRn5WDPgC7TwbVXjcV3+3kgqJbh5LgUvfhsCj55t8MvMSBxctq/Gc3S5/iqV5ZW4tCcWzy8d+dBjulp/cU4R4nbHYN6Nz2BpZ4UNL61Cwv6L2i5LJQYTCpKKSqwfsRI9Qnqhy4vyzbdmLcTIu50LcSs75N3OhY2zrZarrJulnRXaPuWL5JPXUZJbDEmlBMYmxsi7mQOxi522y6vVjRNXEbcnFvG/XUBlaQVK80uxc+YWvanf7r/rhzdztkXn4d2RejpJb743dq4OELvaw6NnGwBA15GBOLhsn97UX+Xy7xfg0t0DzVrIT5WvD/Vf+Ssezb2cYOMkr63Liz1w48Q1vfjeG8SYgkwmw9bJ69GiQ2v0mz1ImO/3gj/ObDwBADiz8QT8hnbTVokKFd7LR0luMQCgvKQciQcuoUWHVmj7lC/O/yy/PvXpjSfgN7S7NstU6PmlL2HBzeX4OPkLjNs6De2e7oBXI1/Xi/rLispQWlAi3E/8Mw4t/Vz14nsDyLtW7NwckJEo37K8ejAeLTu21pv6q8REVXcdAfrxd2vv7oDkf6+jvLgMMpkMVw7Go0XH1nrxvTeIvY+Sjl/Bt08uRavOrhAZyTc1n1syAh4922DjqFXISc2CvYcjxm+fBmsHGy1XW9OtC2nYMn4tpBIpZFIZ/EcFYtDHQ5GZlIFNL69BcXYRXLq5Y+zmUJiYm2q73DpdO5yAv7/Yj9d+naUX9WcmZWD98JUA5F0xPV55DAPCglGUVajz35sq6bGp2DplPSTllWju7YQx6ydDJpXpTf1lRWVY5D4HHyV9BkuxFQDozef/+/ydiN12GkYmxnDp5o6X105EbnqOzn/vDSIUiIhINQbRfURERKphKBARkYChQEREAoYCEREJGApERCRgKJDBmW08CZ/7f4xlncLwedeP8feX+yGVSmt9bt6tHKwf+T+11XJx1zm8LZqIu3p0lDo1bQZzRDNRFVNLM7wbuwgAUJCRj02vfIfS/BIMXji8xvMklRKIW9tj4s/TH/k9q45ifdC5qH/h1bsdzkX9+9D7E2kDQ4EMWjNnW4yKGI8VgYvw7IJhOLPxBC78chZlhaWQSWR4ZeMUfP/8V5gbtxhfPfYJRv8wCa06uQAAVvZbhhe+GI0WHVrjl7c2405cOiQVEgxaMBSdh3bH6Q3HayzrzSPv13jvssJS3Dh+FW/8PRdrg78WQkEqleKXNzfj6qHLsHNzgLGpMYImPQn/kYFIO5uM3bO3oqywFNaONhizYYpwcjiixsBQIIPn6O0MqUSKwox8AMDNcyl498IiWDvYIDs5U3ie/+ggxG4/jVYLh/93jY48uAd4Yd+HP6Pd0x0wZt1klOQWY0XQIrTv3+mhZT0obncMfJ/tDOf2LWHd3BppZ5Ph1sMTF345i+zkTMyND0dhRgGWdfgQQZOehKSiEr+8tRmTd8+AjZMtYradwm9hOzBm3WTNfFBkEBgKRA/wGdCp1kbcf1QQ1gz8AoMXDkfs9tPCefET/ryEuD2x+PuL/QDkV8vLTc2qc1kAcC7qFPrMHAAA6PZyT5yL+hduPTxx4/hVdH0pEEZGRrBtKUbbp3wBABmJd3A7Lh2rB3wBAJBJpGjGrQRqZAwFMniZSRkwMjYSzrZpZm1W6/PsXOxh3dwGty6kIXbbaby0Zrz8AZkME3dMh7NPzdNqp5xKUrisouxCXD10Gbcv3gRE8gYeIhFe+Hy0wjplMhladnLBrJMfNWAtiVTDvY/IoBXey8dPU39E7zefUem8/N1GB+HQZ7+hJK8Erbu4AQB8B/nh2Ld/QSaTn0bsZkyK0uWc/zkaAa/2wscpX+Dj5C8wP205mns5IenYFXg90Q4XdpyFVCpFwd08XD+cCABw9mmFonsFSD55DYD8dPC3L6U3dNWJasUtBTI4FSXl+Nz/Y0gq5HsEBbzaC33vO6V6XbqODMDOmVswYF6wMG/AvBewa9YWfN5lHqRSGZp7OeG1X2fVuZyYqFN4eu6QGvO6jOiBc1GnMOJ/Y3HlYDw+7RgGOzcHuHT3gKXYCiZmJpjw83T8MiMSpXklkFRK0HfWQGHgm6gx8CypRDqorLAU5jYWKMoqxIqgRZhxIgy2LcXaLosMALcUiHTQ989/Jb9KV7kEA+e9wEAgjeGWAhERCTjQTEREAoYCEREJGApERCRgKBARkYChQEREgv8D380HdRtTwFwAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -211,225 +236,86 @@ } ], "source": [ - "ax.plot(inputs, predictions, color=\"blue\")\n", + "plt.clf()\n", + "fig, ax = plt.subplots(1)\n", + "fig.patch.set_facecolor('xkcd:mint green')\n", + "ax.set_yscale(\"log\")\n", + "ax.plot(test_data, predictions, color=\"blue\")\n", + "ax.scatter(df_test[\"DrivAge\"], df_test[\"Frequency\"], marker=\"o\", color=\"red\")\n", + "ax.set_xlabel(\"Driver Age\")\n", + "ax.set_title(\"Regression with sklearn\")\n", + "ax.set_ylabel(\"Frequency of claims\")\n", "display(fig)" ] }, - { - "cell_type": "markdown", - "id": "23852861", - "metadata": {}, - "source": [ - "### As a bonus let's inspect the model parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "7877cb2e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[2.6698928]]\n", - "-3.2299957\n" - ] - } - ], - "source": [ - "print(model.w)\n", - "print(model.b)" - ] - }, - { - "cell_type": "markdown", - "id": "de63118c", - "metadata": {}, - "source": [ - "They are floating point numbers and we can't directly work with them!" - ] - }, { "cell_type": "markdown", "id": "2d959640", "metadata": {}, "source": [ - "### So, let's abstract quantization\n", + "### FHE models need to be quantized, so let's define a **Quantized Poisson Regressor** (Generalized Linear Model with exponential link)\n", "\n", - "Here is a quick summary of quantization. We have a range of values and we want to represent them using small number of bits (n). To do this, we split the range into 2^n sections and map each section to a value. Here is a visualization of the process!" + "We use the quantization primitives available in the Concrete library: QuantizedArray, QuantizedFunction and QuantizedLinear" ] }, { "cell_type": "code", - "execution_count": 11, - "id": "9da2e1a4", - "metadata": {}, - "outputs": [ - { - "data": { - "image/svg+xml": "
min(x)
min(x)
max(x)
max(x)
Map
to 0
Map...
Map
to 1
Map...
Distance
Between
Consecutive
Values
Distan...
Map
to 2
Map...
Map
to 3
Map...
(when n = 2)
(when n = 2)
0
0
= 1 / scale
= 1 / q
= 1 / scale...
x = (x   + zp  ) / q
x = (x   + zp  ) / q
q
q
x
x
x
x
zero point
zp = 2
zero point...
Viewer does not support full SVG 1.1
", - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from IPython.display import SVG\n", - "SVG(filename=\"figures/QuantizationVisualized.svg\")" - ] - }, - { - "cell_type": "markdown", - "id": "45d12e7a", - "metadata": {}, - "source": [ - "If you want to learn more, head to https://intellabs.github.io/distiller/algo_quantization.html" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "2541cdb7", + "execution_count": 10, + "id": "9f5acbfe", "metadata": {}, "outputs": [], "source": [ - "class QuantizationParameters:\n", - " def __init__(self, q, zp, n):\n", - " # q = scale factor = 1 / distance between consecutive values\n", - " # zp = zero point which is used to determine the beginning of the quantized range\n", - " # (quantized 0 = the beginning of the quantized range = zp * distance between consecutive values)\n", - " # n = number of bits\n", + "class QuantizedExp(QuantizedActivation):\n", + " \"\"\"Quantized exponential function.\"\"\"\n", + "\n", + " def calibrate(self, x: np.ndarray):\n", + " self.q_out = QuantizedArray(self.n_bits, np.exp(x))\n", + "\n", + " def __call__(self, q_input: QuantizedArray) -> QuantizedArray:\n", + " quant_exp = np.exp(self.dequant_input(q_input))\n", + " q_out = self.quant_output(quant_exp)\n", + " return q_out\n", " \n", - " # e.g.,\n", - " \n", - " # n = 2\n", - " # zp = 2\n", - " # q = 0.66\n", - " # distance between consecutive values = 1 / q = 1.5151\n", - " \n", - " # quantized 0 = zp / q = zp * distance between consecutive values = 3.0303\n", - " # quantized 1 = quantized 0 + distance between consecutive values = 4.5454\n", - " # quantized 2 = quantized 1 + distance between consecutive values = 6.0606\n", - " # quantized 3 = quantized 2 + distance between consecutive values = 7.5757\n", - " \n", - " self.q = q\n", - " self.zp = zp\n", - " self.n = n\n", + "class QuantizedGLM(QuantizedModule):\n", + " def __init__(self, n_bits, sklearn_model, calibration_data) -> None:\n", + " # Create a QuantizedLinear layer\n", + " self.n_bits = n_bits\n", "\n", - "class QuantizedArray:\n", - " def __init__(self, values, parameters):\n", - " # values = quantized values\n", - " # parameters = parameters used during quantization\n", - " \n", - " # e.g.,\n", - " \n", - " # values = [1, 0, 2, 1]\n", - " # parameters = QuantizationParameters(q=0.66, zp=2, n=2)\n", - " \n", - " # original array = [4.5454, 3.0303, 6.0606, 4.5454]\n", - " \n", - " self.values = np.array(values)\n", - " self.parameters = parameters\n", + " self.q_calibration_data = QuantizedArray(n_bits, calibration_data)\n", "\n", - " @staticmethod\n", - " def of(x, n):\n", - " if not isinstance(x, np.ndarray):\n", - " x = np.array(x)\n", + " q_weights = QuantizedArray(1, np.expand_dims(sklearn_model.coef_,1))\n", + " q_bias = QuantizedArray(1, sklearn_model.intercept_)\n", + " q_layer = QuantizedLinear(6, q_weights, q_bias)\n", + " quant_layers_dict = {}\n", + " # Calibrate and get new calibration_data for next layer/activation\n", + " calibration_data = self._calibrate_and_store_layers_activation(\n", + " \"linear\", q_layer, calibration_data, quant_layers_dict\n", + " )\n", "\n", - " min_x = x.min()\n", - " max_x = x.max()\n", + " # Create a new quantized layer (based on type(layer))\n", + " q_exp = QuantizedExp(n_bits=7)\n", + " calibration_data = self._calibrate_and_store_layers_activation(\n", + " \"invlink\", q_exp, calibration_data, quant_layers_dict\n", + " )\n", "\n", - " if min_x == max_x: # encoding single valued arrays\n", - " \n", - " if min_x == 0.0: # encoding 0s\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = 0 --> q_x = 1 && zp_x = 0 && x_q = 0\n", - " q_x = 1\n", - " zp_x = 0\n", - " x_q = np.zeros(x.shape, dtype=np.uint)\n", - " \n", - " elif min_x < 0.0: # encoding negative scalars\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = -x --> q_x = 1 / x & zp_x = -1 & x_q = 0\n", - " q_x = abs(1 / min_x)\n", - " zp_x = -1\n", - " x_q = np.zeros(x.shape, dtype=np.uint)\n", - " \n", - " else: # encoding positive scalars\n", - " \n", - " # dequantization = (x_q + zp_x) / q_x = x --> q_x = 1 / x & zp_x = 0 & x_q = 1\n", - " q_x = 1 / min_x\n", - " zp_x = 0\n", - " x_q = np.ones(x.shape, dtype=np.uint)\n", - " \n", - " else: # encoding multi valued arrays\n", - " \n", - " # distance between consecutive values = range of x / number of different quantized values = (max_x - min_x) / (2^n - 1)\n", - " # q = 1 / distance between consecutive values\n", - " q_x = (2**n - 1) / (max_x - min_x)\n", - " \n", - " # zp = what should be added to 0 to get min_x -> min_x = (0 + zp) / q -> zp = min_x * q\n", - " zp_x = int(round(min_x * q_x))\n", - " \n", - " # x = (x_q + zp) / q -> x_q = (x * q) - zp\n", - " x_q = ((q_x * x) - zp_x).round().astype(np.uint)\n", + " super().__init__(quant_layers_dict)\n", "\n", - " return QuantizedArray(x_q, QuantizationParameters(q_x, zp_x, n))\n", "\n", - " def dequantize(self):\n", - " # x = (x_q + zp) / q\n", - " # x = (x_q + zp) / q\n", - " return (self.values.astype(np.float32) + float(self.parameters.zp)) / self.parameters.q\n", + " def _calibrate_and_store_layers_activation(self, name, q_function, calibration_data, quant_layers_dict):\n", + " # Calibrate the output of the layer\n", + " q_function.calibrate(calibration_data)\n", + " # Store the learned quantized layer\n", + " quant_layers_dict[name] = q_function\n", + " # Create new calibration data (output of the previous layer)\n", + " q_calibration_data = QuantizedArray(self.n_bits, calibration_data)\n", + " # Dequantize to have the value in clear and ready for next calibration\n", + " return q_function(q_calibration_data).dequant()\n", "\n", - " def affine(self, w, b, min_y, max_y, n_y):\n", - " # the formulas used in this method was derived from the following equations\n", - " #\n", - " # x = (x_q + zp_x) / q_x\n", - " # w = (w_q + zp_w) / q_w\n", - " # b = (b_q + zp_b) / q_b\n", - " #\n", - " # (x * w) + b = ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b)\n", - " # = y = (y_q + zp_y) / q_y\n", - " #\n", - " # So, ((x_q + zp_x) / q_x) * ((w_q + zp_w) / q_w) + ((b_q + zp_b) / q_b) = (y_q + zp_y) / q_y\n", - " # We can calculate zp_y and q_y from min_y, max_y, n_y. So, the only unknown is y_q and it can be solved.\n", "\n", - " x_q = self.values\n", - " w_q = w.values\n", - " b_q = b.values\n", - "\n", - " q_x = self.parameters.q\n", - " q_w = w.parameters.q\n", - " q_b = b.parameters.q\n", - "\n", - " zp_x = self.parameters.zp\n", - " zp_w = w.parameters.zp\n", - " zp_b = b.parameters.zp\n", - "\n", - " q_y = (2**n_y - 1) / (max_y - min_y)\n", - " zp_y = int(round(min_y * q_y))\n", - "\n", - " y_q = (q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b))\n", - " y_q -= min_y * q_y\n", - " y_q = y_q.round().clip(0, 2**n_y - 1).astype(np.uint)\n", - "\n", - " return QuantizedArray(y_q, QuantizationParameters(q_y, zp_y, n_y))\n", - "\n", - "class QuantizedFunction:\n", - " def __init__(self, table):\n", - " self.table = table\n", - "\n", - " @staticmethod\n", - " def of(f, input_bits, output_bits):\n", - " domain = np.array(range(2**input_bits), dtype=np.uint)\n", - " table = f(domain).round().clip(0, 2**output_bits - 1).astype(np.uint)\n", - " return QuantizedFunction(table)" + " def quantize_input(self, x):\n", + " q_input_arr = deepcopy(self.q_calibration_data)\n", + " q_input_arr.update_values(x)\n", + " return q_input_arr" ] }, { @@ -439,20 +325,19 @@ "source": [ "### Let's quantize our model parameters\n", "\n", - "Since the parameters only consist of scalars, we can use a single bit quantization." + "First we get the calibration data, we then run it through the non quantized model to determine all possible intermediate values. After each operation these values are quantized and the quantized version of the operations are stored in the QuantizedGLM module" ] }, { "cell_type": "code", - "execution_count": 13, - "id": "c8b08ef4", + "execution_count": 11, + "id": "09d12194", "metadata": {}, "outputs": [], "source": [ - "parameter_bits = 1\n", - "\n", - "w_q = QuantizedArray.of(model.w, parameter_bits)\n", - "b_q = QuantizedArray.of(model.b, parameter_bits)" + "calib_data = np.expand_dims(df_calib[\"DrivAge\"].values, 1)\n", + "n_bits = 5\n", + "q_glm = QuantizedGLM(n_bits, reg, calib_data)" ] }, { @@ -460,19 +345,18 @@ "id": "e2528092", "metadata": {}, "source": [ - "### And quantize our inputs" + "### And quantize our inputs and perform quantized inference " ] }, { "cell_type": "code", - "execution_count": 14, - "id": "affe644e", + "execution_count": 12, + "id": "f0f0699a", "metadata": {}, "outputs": [], "source": [ - "input_bits = 6\n", - "\n", - "x_q = QuantizedArray.of(inputs, input_bits)" + "q_test_data = q_glm.quantize_input(test_data)\n", + "y_pred = q_glm.forward_and_dequant(q_test_data)\n" ] }, { @@ -480,42 +364,18 @@ "id": "a5a50eb8", "metadata": {}, "source": [ - "### Time to make quantized inference" + "### Visualize the results" ] }, { "cell_type": "code", - "execution_count": 15, - "id": "0fdfd3d9", - "metadata": {}, - "outputs": [], - "source": [ - "output_bits = 7\n", - "\n", - "min_y = predictions.min()\n", - "max_y = predictions.max()\n", - "y_q = x_q.affine(w_q, b_q, min_y, max_y, output_bits)\n", - "\n", - "quantized_predictions = y_q.dequantize()" - ] - }, - { - "cell_type": "markdown", + "execution_count": 13, "id": "5fb15eb4", "metadata": {}, - "source": [ - "### And visualize the results" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "8076a406", - "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -525,7 +385,15 @@ } ], "source": [ - "ax.plot(inputs, quantized_predictions, color=\"black\")\n", + "plt.clf()\n", + "fig, ax = plt.subplots(1)\n", + "fig.patch.set_facecolor('xkcd:mint green')\n", + "ax.set_yscale(\"log\")\n", + "ax.plot(test_data, y_pred, color=\"blue\")\n", + "ax.scatter(df_test[\"DrivAge\"], df_test[\"Frequency\"], marker=\"o\", color=\"red\")\n", + "ax.set_xlabel(\"Driver Age\")\n", + "ax.set_ylabel(\"Frequency of claims\")\n", + "ax.set_title(\"Quantized regression with {} bits\".format(6))\n", "display(fig)" ] }, @@ -539,88 +407,24 @@ }, { "cell_type": "code", - "execution_count": 17, - "id": "cbda8067", + "execution_count": 14, + "id": "fe9935bd", "metadata": {}, "outputs": [], "source": [ - "q_y = (2**output_bits - 1) / (max_y - min_y)\n", - "zp_y = int(round(min_y * q_y))\n", "\n", - "q_x = x_q.parameters.q\n", - "q_w = w_q.parameters.q\n", - "q_b = b_q.parameters.q\n", + "BENCHMARK_CONFIGURATION = hnp.CompilationConfiguration(\n", + " dump_artifacts_on_unexpected_failures=True,\n", + " enable_topological_optimizations=True,\n", + " check_every_input_in_inputset=True,\n", + " treat_warnings_as_errors=True,\n", + ")\n", "\n", - "zp_x = x_q.parameters.zp\n", - "zp_w = w_q.parameters.zp\n", - "zp_b = b_q.parameters.zp\n", - "\n", - "x_q = x_q.values\n", - "w_q = w_q.values\n", - "b_q = b_q.values" - ] - }, - { - "cell_type": "markdown", - "id": "b8e95e3d", - "metadata": {}, - "source": [ - "### Simplification to rescue!\n", - "\n", - "The `y_q` formula in `QuantizedArray.affine(...)` can be rewritten to make it easier to implement in homomorphically. Here is the breakdown.\n", - "```\n", - "(q_y / (q_x * q_w)) * ((x_q + zp_x) @ (w_q + zp_w) + (q_x * q_w / q_b) * (b_q + zp_b)) - (min_y * q_y)\n", - "^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^\n", - "constant (c1) can be done constant (c2) constant (c3) constant (c4)\n", - " on the circuit \n", - " \n", - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - " can be done on the circuit\n", - " \n", - "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", - "cannot be done on the circuit because of floating point operation so will be a single table lookup\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "c6e101ae", - "metadata": {}, - "source": [ - "### Let's import the Concrete numpy package now!" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "4da7aed5", - "metadata": {}, - "outputs": [], - "source": [ - "import concrete.numpy as hnp" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "d3816fa5", - "metadata": {}, - "outputs": [], - "source": [ - "c1 = q_y / (q_x * q_w)\n", - "c2 = w_q + zp_w\n", - "c3 = (q_x * q_w / q_b) * (b_q + zp_b)\n", - "c4 = min_y * q_y\n", - "\n", - "f = lambda intermediate: (c1 * (intermediate + c3)) - c4\n", - "f_q = QuantizedFunction.of(f, input_bits + parameter_bits, output_bits)\n", - "\n", - "table = hnp.LookupTable([int(entry) for entry in f_q.table])\n", - "\n", - "w_0 = int(c2.flatten()[0])\n", - "\n", - "def infer(x_0):\n", - " return table[(x_0 + zp_x) * w_0]" + "engine = q_glm.compile(\n", + " q_test_data,\n", + " BENCHMARK_CONFIGURATION,\n", + " show_mlir=False,\n", + ")\n" ] }, { @@ -633,73 +437,13 @@ }, { "cell_type": "code", - "execution_count": 20, - "id": "81304aca", + "execution_count": 15, + "id": "c1fc0f48", "metadata": {}, "outputs": [], - "source": [ - "inputset = [int(x_i[0]) for x_i in x_q]\n", - "\n", - "circuit = hnp.compile_numpy_function(\n", - " infer,\n", - " {\"x_0\": hnp.EncryptedScalar(hnp.Integer(input_bits, is_signed=False))},\n", - " inputset,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "c62af039", - "metadata": {}, - "source": [ - "### Here are some representations of the fhe circuit" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "0c533af6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "%0 = Constant(1) # ClearScalar>\n", - "%1 = x_0 # EncryptedScalar>\n", - "%2 = Constant(15) # ClearScalar>\n", - "%3 = Add(1, 2) # EncryptedScalar>\n", - "%4 = Mul(3, 0) # EncryptedScalar>\n", - "%5 = TLU(4) # EncryptedScalar>\n", - "return(%5)\n", - "\n" - ] - } - ], - "source": [ - "print(circuit)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "c1fc0f48", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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smULZ8+e1WLUmId25ihWr17NlVdeSRsPpUT3NnoGjh8T7DNz7rjjDqKjo8nIyDDbFHegZ+D4M8HszCkpKWHdunUB00QF3Uz1a4LZmfPDDz+Qm5urxajxHYLVmZOZmUmDBg1o27at2aa4DS1GPydYnTmZmZn06NGDkJDAeYQD506CmGCbmVNaWsratWsDqokKWowBQzA5c7Zt28apU6f8fmV/ebQYA4RgcuZkZmZSv359UlJSzDbFrWgxBhDB4syx9RcDbWxVizGACAZnjogEZH8RtBgDDk84c7Zu3Urfvn2Ji4sjNjaW3r17s27dOrd8d03ZsWMHv/zyS5X9xaVLl5KUlERYmP9EI9ViDEDc6czZuHEj3bp1IzY2ll27drF//36aN29OWloay5cvd4O1NSMzM5N69erRoUMHl8f37dtH//79ef755zl58qR3jaslem5qgPLss88yc+ZMfvrpp0sOgFxaWkr79u3Jyclh3759REdHA2oqWtu2bblw4QLZ2dlERka60/RKufvuuzl//jxLlixxefzee++lffv2PPPMMzRt2pQTJ05QXFxROhufQs9NDVTc4czJyspix44dDB48uEyIAKGhoQwbNozDhw+zePFid5hbLUSErKysSvuLM2fOZMKECX7VPLWhxRiguMOZs2rVKgCuv/56p2O2fStXrrx0I2vI7t27OXnyZKVitP/R8De0GAOY2jpzdu/eDcBVV13ldCwhIQGAPXv21M7IGpCZmUlMTAzXXXed167pTbQYA5zaOHNyc3MBqFvXOUdTTEwMAKdPn66VfTUhMzOT7t27Ex4e7rVrehMtxgDHUzNzbH4/bwaCqqq/6O9oMQYBl+rMiYuLA+D8+fNOx2z7bOd4mj179nDs2DEtRo1/c6nOnNatWwNw5MgRp2NHjx4F8Fq80szMTOrUqePSmRQoaDEGCZfizLHlr9i8ebPTMdu+Xr16uc/ISsjMzKRbt25ERER45XpmoMUYRNicOdOnT6/W+T179iQ5OZn58+eTn59ftr+kpIR58+aRmJhI3759PWWuA2vWrAnoJipoMQYVycnJPPnkk0ycOLFazpyQkBBmzpxJTk4Oo0eP5sSJE5w6dYpx48aRnZ3NjBkziIqK8rjd+/bt49ChQ1qMmsCips6crl27sn79es6cOUOrVq1o2rQp2dnZrF69mt///vcetlaRmZlJVFQUnTt3rvLcxYsXY7FYsFgsHD16lJKSkrLPH374oResvXT03NQgZN68eQwfPpx169bRtWtXs82pklGjRnH48OGyGUEBip6bGoz4W8wcW3KbQEeLMUipqTPHLA4dOsTBgwe1GDWBS02dOWbxzTffEBkZSZcuXcw2xeNoMQYx/hAzJzMzky5duvj1aozqosUYxMTExPh8zJxg6S+C9qZq8N1sVkeOHCExMZGvv/7aazN9TER7UzW+68xZvXo1ERERfjH84g60GDU+68zJzMykc+fOLtdTBiJajBrAN505wdRfBC1GjRV7Z85///tfs83h+PHjZGdnB5UYtQNH44CvOHPmzp3LyJEjycnJITY21jQ7vIh24GgcmTp1qk84czIzM7n++uuDRYiAbqZqytGmTRufcOYEW38RtBg1LvCmM+f06dO89NJLfP3112VxdX755Rd++umnoBOj7jNqXGJbZrV27VpuvPFGp+MlJSVu6VMWFBQQHR2NiBAaGsp1111HQkIC//73vzl8+DBNmjSp9TX8hAwtRk2FuHLm7N+/nyeffJKRI0cyaNAgt1ynXr165OXllX0ODw+nqKiI0NBQ2rdvz+9+9zt69uxJampqIPchMxCNpgJ27twp4eHh8t5778nFixflL3/5i0RERAggzz33nNuu06JFCwEqfIWHhwsgf/vb39x2TR8kXdeMmkr505/+xCeffEKdOnU4ePBg2WLk1NRUsrKy3HKNnj17VvpdYWFhXHvttWzcuNGn5s66mQz/S9Wj8RpHjhzhwIEDnDhxgpCQEEpLS8uOff/995SWlhISUnsfYGJiotP322OxWJg9e3YgCxHQ3lSNCwoLC3n33Xdp2bIlCxcuBHASyvnz58nOznbL9Ro3blxh/ozQ0FBeeeUV2rZt65Zr+TK6ZtQ4cPDgQW6++WYOHDhAZT2YkJAQvvvuO1q1alXrazZq1MjltcLCwkhKSuLZZ5+t9TX8AV0zahy4+uqr+dOf/kRISEilzcKwsDC+++47t1yzcePGLrMLiwhz5swJ2KxT5dFi1DgxduxYvvnmG2JjYyvMAFxYWMi6devccr34+HinZnBoaCjPP/88nTp1css1/AHtTdVUyL59++jTpw8HDhygqKjI6XhERATnzp2rdc21fft2UlJSyj6HhYVx9dVXs337dq9ELPcR9ERxTcW0aNGC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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], "source": [ "from PIL import Image\n", - "file = Image.open(circuit.draw())\n", + "file = Image.open(engine.draw())\n", "file.show()\n", "file.close()" ] @@ -714,16 +458,24 @@ }, { "cell_type": "code", - "execution_count": 23, - "id": "c0b246f7", + "execution_count": 16, + "id": "ca928b78", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 100/100 [01:54<00:00, 1.15s/it]\n" + ] + } + ], "source": [ - "homomorphic_predictions = []\n", - "for x_i in (int(x_i[0]) for x_i in x_q):\n", - " inference = QuantizedArray(circuit.run(x_i), y_q.parameters)\n", - " homomorphic_predictions.append(inference.dequantize())\n", - "homomorphic_predictions = np.array(homomorphic_predictions, dtype=np.float32)" + "y_pred_fhe = np.zeros((test_data.shape[0],), np.float32)\n", + "for i, test_sample in enumerate(tqdm(q_test_data.qvalues)):\n", + " q_sample = np.expand_dims(test_sample, 1).transpose([1,0]).astype(np.uint8)\n", + " q_pred_fhe = engine.run(q_sample)\n", + " y_pred_fhe[i] = q_glm.dequantize_output(q_pred_fhe)" ] }, { @@ -736,13 +488,13 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 17, "id": "92c7f2f5", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -752,7 +504,14 @@ } ], "source": [ - "ax.plot(inputs, homomorphic_predictions, color=\"green\")\n", + "plt.clf()\n", + "fig, ax = plt.subplots(1)\n", + "fig.patch.set_facecolor('xkcd:mint green')\n", + "ax.set_yscale(\"log\")\n", + "ax.plot(test_data, y_pred_fhe, color=\"blue\")\n", + "ax.scatter(df_test[\"DrivAge\"], df_test[\"Frequency\"], marker=\"o\", color=\"red\")\n", + "ax.set_xlabel(\"Driver Age\")\n", + "ax.set_ylabel(\"Frequency of claims\")\n", "display(fig)" ] }, diff --git a/poetry.lock b/poetry.lock index 29df31c2c..cced2f076 100644 --- a/poetry.lock +++ b/poetry.lock @@ -16,20 +16,35 @@ python-versions = "*" [[package]] name = "argon2-cffi" -version = "21.1.0" +version = "21.2.0" description = "The secure Argon2 password hashing algorithm." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] -cffi = ">=1.0.0" +argon2-cffi-bindings = "*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "furo", "wheel", "pre-commit"] -docs = ["sphinx", "furo"] +dev = ["pre-commit", "cogapp", "tomli", "coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "sphinx-notfound-page", "furo"] +docs = ["sphinx", "sphinx-notfound-page", "furo"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] +[[package]] +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["pytest", "cogapp", "pre-commit", "wheel"] +tests = ["pytest"] + [[package]] name = "astroid" version = "2.8.6" @@ -568,6 +583,14 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "joblib" +version = "1.1.0" +description = "Lightweight pipelining with Python functions" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "jsonschema" version = "4.2.1" @@ -1027,6 +1050,26 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +[[package]] +name = "pandas" +version = "1.3.4" +description = "Powerful data structures for data analysis, time series, and statistics" +category = "dev" +optional = false +python-versions = ">=3.7.1" + +[package.dependencies] +numpy = [ + {version = ">=1.17.3", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""}, + {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""}, + {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""}, +] +python-dateutil = ">=2.7.3" +pytz = ">=2017.3" + +[package.extras] +test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"] + [[package]] name = "pandocfilters" version = "1.5.0" @@ -1144,7 +1187,7 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.23" +version = "3.0.24" description = "Library for building powerful interactive command lines in Python" category = "dev" optional = false @@ -1592,6 +1635,37 @@ python-versions = "*" [package.extras] idna2008 = ["idna"] +[[package]] +name = "scikit-learn" +version = "1.0.1" +description = "A set of python modules for machine learning and data mining" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +joblib = ">=0.11" +numpy = ">=1.14.6" +scipy = ">=1.1.0" +threadpoolctl = ">=2.0.0" + +[package.extras] +benchmark = ["matplotlib (>=2.2.3)", "pandas (>=0.25.0)", "memory-profiler (>=0.57.0)"] +docs = ["matplotlib (>=2.2.3)", "scikit-image (>=0.14.5)", "pandas (>=0.25.0)", "seaborn (>=0.9.0)", "memory-profiler (>=0.57.0)", "sphinx (>=4.0.1)", "sphinx-gallery (>=0.7.0)", "numpydoc (>=1.0.0)", "Pillow (>=7.1.2)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] +examples = ["matplotlib (>=2.2.3)", "scikit-image (>=0.14.5)", "pandas (>=0.25.0)", "seaborn (>=0.9.0)"] +tests = ["matplotlib (>=2.2.3)", "scikit-image (>=0.14.5)", "pandas (>=0.25.0)", "pytest (>=5.0.1)", "pytest-cov (>=2.9.0)", "flake8 (>=3.8.2)", "black (>=21.6b0)", "mypy (>=0.770)", "pyamg (>=4.0.0)"] + +[[package]] +name = "scipy" +version = "1.7.3" +description = "SciPy: Scientific Library for Python" +category = "dev" +optional = false +python-versions = ">=3.7,<3.11" + +[package.dependencies] +numpy = ">=1.16.5,<1.23.0" + [[package]] name = "secretstorage" version = "3.3.1" @@ -1831,6 +1905,14 @@ python-versions = ">= 3.5" [package.extras] test = ["pytest", "pathlib2"] +[[package]] +name = "threadpoolctl" +version = "3.0.0" +description = "threadpoolctl" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "toml" version = "0.10.2" @@ -1991,7 +2073,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = ">=3.8,<3.9" -content-hash = "061349291b83bd3051337a6001008126a5eb3b84e0da8b30851fe64a6ff55eb9" +content-hash = "0eda99f80ae5bffae54208ac20656600483ab277c061326392d338b1c931d391" [metadata.files] alabaster = [ @@ -2003,17 +2085,31 @@ appnope = [ {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] argon2-cffi = [ - {file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"}, - {file = "argon2_cffi-21.1.0-cp35-abi3-macosx_10_14_x86_64.whl", hash = "sha256:217b4f0f853ccbbb5045242946ad2e162e396064575860141b71a85eb47e475a"}, - {file = "argon2_cffi-21.1.0-cp35-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa7e7d1fc22514a32b1761fdfa1882b6baa5c36bb3ef557bdd69e6fc9ba14a41"}, - {file = "argon2_cffi-21.1.0-cp35-abi3-win32.whl", hash = "sha256:e4d8f0ae1524b7b0372a3e574a2561cbdddb3fdb6c28b70a72868189bda19659"}, - {file = "argon2_cffi-21.1.0-cp35-abi3-win_amd64.whl", hash = "sha256:65213a9174320a1aee03fe826596e0620783966b49eb636955958b3074e87ff9"}, - {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:245f64a203012b144b7b8c8ea6d468cb02b37caa5afee5ba4a10c80599334f6a"}, - {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4ad152c418f7eb640eac41ac815534e6aa61d1624530b8e7779114ecfbf327f8"}, - {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:bc513db2283c385ea4da31a2cd039c33380701f376f4edd12fe56db118a3b21a"}, - {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c7a7c8cc98ac418002090e4add5bebfff1b915ea1cb459c578cd8206fef10378"}, - {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:165cadae5ac1e26644f5ade3bd9c18d89963be51d9ea8817bd671006d7909057"}, - {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:566ffb581bbd9db5562327aee71b2eda24a1c15b23a356740abe3c011bbe0dcb"}, + {file = "argon2-cffi-21.2.0.tar.gz", hash = "sha256:50936e5ad9e860c5a6678063c5ac732c2fc8a178994cca9e1e7220351f930e9a"}, + {file = "argon2_cffi-21.2.0-py3-none-any.whl", hash = "sha256:d5d7b9d38963c2769cd0dbfc5901ae00eb9bb98a9cb5a2ea0c9c7c4fec3e6b98"}, +] +argon2-cffi-bindings = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, ] astroid = [ {file = "astroid-2.8.6-py3-none-any.whl", hash = "sha256:cd8326b424c971e7d87678609cf6275d22028afd37d6ac59c16d47f1245882f6"}, @@ -2318,6 +2414,10 @@ jinja2 = [ {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, ] +joblib = [ + {file = "joblib-1.1.0-py2.py3-none-any.whl", hash = "sha256:f21f109b3c7ff9d95f8387f752d0d9c34a02aa2f7060c2135f465da0e5160ff6"}, + {file = "joblib-1.1.0.tar.gz", hash = "sha256:4158fcecd13733f8be669be0683b96ebdbbd38d23559f54dca7205aea1bf1e35"}, +] jsonschema = [ {file = "jsonschema-4.2.1-py3-none-any.whl", hash = "sha256:2a0f162822a64d95287990481b45d82f096e99721c86534f48201b64ebca6e8c"}, {file = "jsonschema-4.2.1.tar.gz", hash = "sha256:390713469ae64b8a58698bb3cbc3859abe6925b565a973f87323ef21b09a27a8"}, @@ -2654,6 +2754,32 @@ packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] +pandas = [ + {file = "pandas-1.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9707bdc1ea9639c886b4d3be6e2a45812c1ac0c2080f94c31b71c9fa35556f9b"}, + {file = "pandas-1.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2f44425594ae85e119459bb5abb0748d76ef01d9c08583a667e3339e134218e"}, + {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:372d72a3d8a5f2dbaf566a5fa5fa7f230842ac80f29a931fb4b071502cf86b9a"}, + {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99d2350adb7b6c3f7f8f0e5dfb7d34ff8dd4bc0a53e62c445b7e43e163fce63"}, + {file = "pandas-1.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:4acc28364863127bca1029fb72228e6f473bb50c32e77155e80b410e2068eeac"}, + {file = "pandas-1.3.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c2646458e1dce44df9f71a01dc65f7e8fa4307f29e5c0f2f92c97f47a5bf22f5"}, + {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5298a733e5bfbb761181fd4672c36d0c627320eb999c59c65156c6a90c7e1b4f"}, + {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22808afb8f96e2269dcc5b846decacb2f526dd0b47baebc63d913bf847317c8f"}, + {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b528e126c13816a4374e56b7b18bfe91f7a7f6576d1aadba5dee6a87a7f479ae"}, + {file = "pandas-1.3.4-cp37-cp37m-win32.whl", hash = "sha256:fe48e4925455c964db914b958f6e7032d285848b7538a5e1b19aeb26ffaea3ec"}, + {file = "pandas-1.3.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaca36a80acaacb8183930e2e5ad7f71539a66805d6204ea88736570b2876a7b"}, + {file = "pandas-1.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42493f8ae67918bf129869abea8204df899902287a7f5eaf596c8e54e0ac7ff4"}, + {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a388960f979665b447f0847626e40f99af8cf191bce9dc571d716433130cb3a7"}, + {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba0aac1397e1d7b654fccf263a4798a9e84ef749866060d19e577e927d66e1b"}, + {file = "pandas-1.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f567e972dce3bbc3a8076e0b675273b4a9e8576ac629149cf8286ee13c259ae5"}, + {file = "pandas-1.3.4-cp38-cp38-win32.whl", hash = "sha256:c1aa4de4919358c5ef119f6377bc5964b3a7023c23e845d9db7d9016fa0c5b1c"}, + {file = "pandas-1.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:dd324f8ee05925ee85de0ea3f0d66e1362e8c80799eb4eb04927d32335a3e44a"}, + {file = "pandas-1.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47750cf07dee6b55d8423471be70d627314277976ff2edd1381f02d52dbadf9"}, + {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d1dc09c0013d8faa7474574d61b575f9af6257ab95c93dcf33a14fd8d2c1bab"}, + {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10e10a2527db79af6e830c3d5842a4d60383b162885270f8cffc15abca4ba4a9"}, + {file = "pandas-1.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c77609acd2e4d517da41bae0c11c70d31c87aae8dd1aabd2670906c6d2c143"}, + {file = "pandas-1.3.4-cp39-cp39-win32.whl", hash = "sha256:003ba92db58b71a5f8add604a17a059f3068ef4e8c0c365b088468d0d64935fd"}, + {file = "pandas-1.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:a51528192755f7429c5bcc9e80832c517340317c861318fea9cea081b57c9afd"}, + {file = "pandas-1.3.4.tar.gz", hash = "sha256:a2aa18d3f0b7d538e21932f637fbfe8518d085238b429e4790a35e1e44a96ffc"}, +] pandocfilters = [ {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"}, @@ -2738,8 +2864,8 @@ prometheus-client = [ {file = "prometheus_client-0.12.0.tar.gz", hash = "sha256:1b12ba48cee33b9b0b9de64a1047cbd3c5f2d0ab6ebcead7ddda613a750ec3c5"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.23-py3-none-any.whl", hash = "sha256:5f29d62cb7a0ecacfa3d8ceea05a63cd22500543472d64298fc06ddda906b25d"}, - {file = "prompt_toolkit-3.0.23.tar.gz", hash = "sha256:7053aba00895473cb357819358ef33f11aa97e4ac83d38efb123e5649ceeecaf"}, + {file = "prompt_toolkit-3.0.24-py3-none-any.whl", hash = "sha256:e56f2ff799bacecd3e88165b1e2f5ebf9bcd59e80e06d395fa0cc4b8bd7bb506"}, + {file = "prompt_toolkit-3.0.24.tar.gz", hash = "sha256:1bb05628c7d87b645974a1bad3f17612be0c29fa39af9f7688030163f680bad6"}, ] psutil = [ {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, @@ -3039,6 +3165,64 @@ rfc3986 = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, ] +scikit-learn = [ + {file = "scikit-learn-1.0.1.tar.gz", hash = "sha256:ac2ca9dbb754d61cfe1c83ba8483498ef951d29b93ec09d6f002847f210a99da"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:116e05fd990d9b363fc29bd3699ec2117d7da9088f6ca9a90173b240c5a063f1"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bd78a2442c948536f677e2744917c37cff014559648102038822c23863741c27"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32d941f12fd7e245f01da2b82943c5ce6f1133fa5375eb80caa51457532b3e7e"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb7214103f6c36c1371dd8c166897e3528264a28f2e2e42573ba8c61ed4d7142"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46248cc6a8b72490f723c73ff2e65e62633d14cafe9d2df3a7b3f87d332a6f7e"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fecb5102f0a36c16c1361ec519a7bb0260776ef40e17393a81f530569c916a7b"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-win32.whl", hash = "sha256:02aee3b257617da0ec98dee9572b10523dc00c25b68c195ddf100c1a93b1854b"}, + {file = "scikit_learn-1.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:538f3a85c4980c7572f3e754f0ba8489363976ef3e7f6a94e8f1af5ae45f6f6a"}, + {file = "scikit_learn-1.0.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:59b1d6df8724003fa16b7365a3b43449ee152aa6e488dd7a19f933640bb2d7fb"}, + {file = "scikit_learn-1.0.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:515b227f01f569145dc9f86e56f4cea9f00a613fc4d074bbfc0a92ca00bff467"}, + {file = "scikit_learn-1.0.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fc75f81571137b39f9b31766e15a0e525331637e7fe8f8000a3fbfba7da3add9"}, + {file = "scikit_learn-1.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:648f4dbfdd0a1b45bf6e2e4afe3f431774c55dee05e2d28f8394d6648296f373"}, + {file = "scikit_learn-1.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:53bb7c605427ab187869d7a05cd3f524a3015a90e351c1788fc3a662e7f92b69"}, + {file = "scikit_learn-1.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a800665527c1a63f7395a0baae3c89b0d97b54d2c23769c1c9879061bb80bc19"}, + {file = "scikit_learn-1.0.1-cp38-cp38-win32.whl", hash = "sha256:ee59da47e18b703f6de17d5d51b16ce086c50969d5a83db5217f0ae9372de232"}, + {file = "scikit_learn-1.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:ebbe4275556d3c02707bd93ae8b96d9651acd4165126e0ae64b336afa2a6dcb1"}, + {file = "scikit_learn-1.0.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:11a57405c1c3514227d0c6a0bee561c94cd1284b41e236f7a1d76b3975f77593"}, + {file = "scikit_learn-1.0.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a51fdbc116974d9715957366df73e5ec6f0a7a2afa017864c2e5f5834e6f494d"}, + {file = "scikit_learn-1.0.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:944f47b2d881b9d24aee40d643bfdc4bd2b6dc3d25b62964411c6d8882f940a1"}, + {file = "scikit_learn-1.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc60e0371e521995a6af2ef3f5d911568506124c272889b318b8b6e497251231"}, + {file = "scikit_learn-1.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ce4e3ddb6e6e9dcdb3e5ac7f0575dbaf56f79ce2b2edee55192b12b52df5be"}, + {file = "scikit_learn-1.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:059c5be0c0365321ddbcac7abf0db806fad8ecb64ee6c7cbcd58313c7d61634d"}, + {file = "scikit_learn-1.0.1-cp39-cp39-win32.whl", hash = "sha256:c6b9510fd2e1642314efb7aa951a0d05d963f3523e01c30b2dadde2395ebe6b4"}, + {file = "scikit_learn-1.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:c604a813df8e7d6dfca3ae0db0a8fd7e5dff4ea9d94081ab263c81bf0b61ab4b"}, +] +scipy = [ + {file = "scipy-1.7.3-1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c9e04d7e9b03a8a6ac2045f7c5ef741be86727d8f49c45db45f244bdd2bcff17"}, + {file = "scipy-1.7.3-1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:b0e0aeb061a1d7dcd2ed59ea57ee56c9b23dd60100825f98238c06ee5cc4467e"}, + {file = "scipy-1.7.3-1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:b78a35c5c74d336f42f44106174b9851c783184a85a3fe3e68857259b37b9ffb"}, + {file = "scipy-1.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:173308efba2270dcd61cd45a30dfded6ec0085b4b6eb33b5eb11ab443005e088"}, + {file = "scipy-1.7.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:21b66200cf44b1c3e86495e3a436fc7a26608f92b8d43d344457c54f1c024cbc"}, + {file = "scipy-1.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceebc3c4f6a109777c0053dfa0282fddb8893eddfb0d598574acfb734a926168"}, + {file = "scipy-1.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7eaea089345a35130bc9a39b89ec1ff69c208efa97b3f8b25ea5d4c41d88094"}, + {file = "scipy-1.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:304dfaa7146cffdb75fbf6bb7c190fd7688795389ad060b970269c8576d038e9"}, + {file = "scipy-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:033ce76ed4e9f62923e1f8124f7e2b0800db533828c853b402c7eec6e9465d80"}, + {file = "scipy-1.7.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4d242d13206ca4302d83d8a6388c9dfce49fc48fdd3c20efad89ba12f785bf9e"}, + {file = "scipy-1.7.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8499d9dd1459dc0d0fe68db0832c3d5fc1361ae8e13d05e6849b358dc3f2c279"}, + {file = "scipy-1.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca36e7d9430f7481fc7d11e015ae16fbd5575615a8e9060538104778be84addf"}, + {file = "scipy-1.7.3-cp37-cp37m-win32.whl", hash = "sha256:e2c036492e673aad1b7b0d0ccdc0cb30a968353d2c4bf92ac8e73509e1bf212c"}, + {file = "scipy-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:866ada14a95b083dd727a845a764cf95dd13ba3dc69a16b99038001b05439709"}, + {file = "scipy-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:65bd52bf55f9a1071398557394203d881384d27b9c2cad7df9a027170aeaef93"}, + {file = "scipy-1.7.3-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:f99d206db1f1ae735a8192ab93bd6028f3a42f6fa08467d37a14eb96c9dd34a3"}, + {file = "scipy-1.7.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5f2cfc359379c56b3a41b17ebd024109b2049f878badc1e454f31418c3a18436"}, + {file = "scipy-1.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb7ae2c4dbdb3c9247e07acc532f91077ae6dbc40ad5bd5dca0bb5a176ee9bda"}, + {file = "scipy-1.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c2d250074cfa76715d58830579c64dff7354484b284c2b8b87e5a38321672c"}, + {file = "scipy-1.7.3-cp38-cp38-win32.whl", hash = "sha256:87069cf875f0262a6e3187ab0f419f5b4280d3dcf4811ef9613c605f6e4dca95"}, + {file = "scipy-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:7edd9a311299a61e9919ea4192dd477395b50c014cdc1a1ac572d7c27e2207fa"}, + {file = "scipy-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eef93a446114ac0193a7b714ce67659db80caf940f3232bad63f4c7a81bc18df"}, + {file = "scipy-1.7.3-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:eb326658f9b73c07081300daba90a8746543b5ea177184daed26528273157294"}, + {file = "scipy-1.7.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93378f3d14fff07572392ce6a6a2ceb3a1f237733bd6dcb9eb6a2b29b0d19085"}, + {file = "scipy-1.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edad1cf5b2ce1912c4d8ddad20e11d333165552aba262c882e28c78bbc09dbf6"}, + {file = "scipy-1.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1cc2c19afe3b5a546ede7e6a44ce1ff52e443d12b231823268019f608b9b12"}, + {file = "scipy-1.7.3-cp39-cp39-win32.whl", hash = "sha256:2c56b820d304dffcadbbb6cbfbc2e2c79ee46ea291db17e288e73cd3c64fefa9"}, + {file = "scipy-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:3f78181a153fa21c018d346f595edd648344751d7f03ab94b398be2ad083ed3e"}, + {file = "scipy-1.7.3.tar.gz", hash = "sha256:ab5875facfdef77e0a47d5fd39ea178b58e60e454a4c85aa1e52fcb80db7babf"}, +] secretstorage = [ {file = "SecretStorage-3.3.1-py3-none-any.whl", hash = "sha256:422d82c36172d88d6a0ed5afdec956514b189ddbfb72fefab0c8a1cee4eaf71f"}, {file = "SecretStorage-3.3.1.tar.gz", hash = "sha256:fd666c51a6bf200643495a04abb261f83229dcb6fd8472ec393df7ffc8b6f195"}, @@ -3114,6 +3298,10 @@ testpath = [ {file = "testpath-0.5.0-py3-none-any.whl", hash = "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589"}, {file = "testpath-0.5.0.tar.gz", hash = "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417"}, ] +threadpoolctl = [ + {file = "threadpoolctl-3.0.0-py3-none-any.whl", hash = "sha256:4fade5b3b48ae4b1c30f200b28f39180371104fccc642e039e0f2435ec8cc211"}, + {file = "threadpoolctl-3.0.0.tar.gz", hash = "sha256:d03115321233d0be715f0d3a5ad1d6c065fe425ddc2d671ca8e45e9fd5d7a52a"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/pyproject.toml b/pyproject.toml index f6eaff6e8..77ba33e43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,8 @@ pygments-style-tomorrow = "^1.0.0" beautifulsoup4 = "^4.10.0" pip-licenses = "^3.5.3" sphinx-zama-theme = "2.0.6" +scikit-learn = "1.0.1" +pandas = "1.3.4" [build-system] requires = ["poetry-core>=1.0.0"]