diff --git a/Makefile b/Makefile index 5ea4b1515..81051046d 100644 --- a/Makefile +++ b/Makefile @@ -112,16 +112,8 @@ mypy_ns: mypy_test: find ./tests/ -name "*.py" | xargs poetry run mypy --ignore-missing-imports -.PHONY: mypy_concrete_benchmark # Run mypy on concrete benchmark files -mypy_concrete_benchmark: - find ./benchmarks/concrete/ -name "*.py" | xargs poetry run mypy --ignore-missing-imports - -.PHONY: mypy_ml_benchmark # Run mypy on ml benchmark files -mypy_ml_benchmark: - find ./benchmarks/ml/ -name "*.py" | xargs poetry run mypy --ignore-missing-imports - .PHONY: mypy_benchmark # Run mypy on benchmark files -mypy_benchmark: mypy_concrete_benchmark mypy_ml_benchmark + find ./benchmarks/ -name "*.py" | xargs poetry run mypy --ignore-missing-imports .PHONY: mypy_script # Run mypy on scripts mypy_script: @@ -216,17 +208,10 @@ finalize_nb: pytest_nb: find docs -name "*.ipynb" | grep -v _build | grep -v .ipynb_checkpoints | xargs poetry run pytest -Wignore --nbmake -.PHONY: concrete_benchmark # Launch concrete benchmarks -concrete_benchmark: +.PHONY: benchmark # Launch concrete benchmarks +benchmark: rm -rf progress.json && \ - for script in benchmarks/concrete/*.py; do \ - poetry run python $$script; \ - done - -.PHONY: ml_benchmark # Launch ml benchmarks -ml_benchmark: - rm -rf progress.json && \ - for script in benchmarks/ml/*.py; do \ + for script in benchmarks/*.py; do \ poetry run python $$script; \ done diff --git a/benchmarks/concrete/common.py b/benchmarks/common.py similarity index 100% rename from benchmarks/concrete/common.py rename to benchmarks/common.py diff --git a/benchmarks/ml/common.py b/benchmarks/ml/common.py deleted file mode 100644 index c59139c63..000000000 --- a/benchmarks/ml/common.py +++ /dev/null @@ -1,16 +0,0 @@ -import concrete.numpy as hnp -from concrete.numpy import compile as compile_ - -# This is only for benchmarks to speed up compilation times -# pylint: disable=protected-access -compile_._COMPILE_FHE_INSECURE_KEY_CACHE_DIR = "/tmp/keycache" -# pylint: enable=protected-access - -BENCHMARK_CONFIGURATION = hnp.CompilationConfiguration( - check_every_input_in_inputset=True, - dump_artifacts_on_unexpected_failures=True, - enable_topological_optimizations=True, - enable_unsafe_features=True, - treat_warnings_as_errors=True, - use_insecure_key_cache=True, -) diff --git a/benchmarks/ml/glm.py b/benchmarks/ml/glm.py deleted file mode 100644 index 32a5aeb0f..000000000 --- a/benchmarks/ml/glm.py +++ /dev/null @@ -1,282 +0,0 @@ -from copy import deepcopy -from typing import Any, Dict - -import numpy as np -import progress -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"]) - - -@progress.track([{"id": "glm", "name": "Generalized Linear Model"}]) -def main(): - """ - 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) - - engine = q_glm.compile( - q_test_data, - BENCHMARK_CONFIGURATION, - show_mlir=False, - ) - - y_pred_fhe = np.zeros((test_data.shape[0],), np.float32) - for i, test_sample in enumerate(tqdm(q_test_data.qvalues)): - with progress.measure(id="evaluation-time-ms", label="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) - - 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}") - progress.measure( - id="non-homomorphic-loss", - label="Non Homomorphic Loss", - value=dev_pca_quantized, - ) - - print(f"FHE Quantized deviance: {dev_pca_quantized_fhe}") - progress.measure( - id="homomorphic-loss", - label="Homomorphic Loss", - value=dev_pca_quantized_fhe, - ) - - print(f"Percentage difference: {difference}%") - progress.measure( - id="relative-loss-difference-percent", - label="Relative Loss Difference (%)", - value=difference, - alert=(">", 7.5), - ) diff --git a/benchmarks/ml/linear_regression.py b/benchmarks/ml/linear_regression.py deleted file mode 100644 index 224807b42..000000000 --- a/benchmarks/ml/linear_regression.py +++ /dev/null @@ -1,191 +0,0 @@ -from copy import deepcopy -from typing import Any, Dict - -import numpy as np -import progress -from common import BENCHMARK_CONFIGURATION -from sklearn.datasets import make_regression -from sklearn.linear_model import LinearRegression -from sklearn.metrics import r2_score -from sklearn.model_selection import train_test_split -from tqdm import tqdm - -from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule - - -class QuantizedLinearRegression(QuantizedModule): - """ - Quantized Generalized Linear Model - Building on top of QuantizedModule, implement a quantized linear transformation (w.x + b) - """ - - @staticmethod - def from_sklearn(sklearn_model, calibration_data): - """Create a Quantized Linear Regression initialized from a sklearn trained model""" - weights = np.expand_dims(sklearn_model.coef_, 1) - bias = sklearn_model.intercept_ - # Quantize with 6 bits for input data, 1 for weights, 1 for the bias and 6 for the output - return QuantizedLinearRegression(6, 1, 1, 6, weights, bias, calibration_data) - - def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None: - """ - Create the linear regression with different quantization bit precisions: - - Quantization Parameters - Number of bits: - q_bits (int): bits for input data, insuring that the number of bits of - the w . x + b operation does not exceed 7 for the calibration data - w_bits (int): bits for weights: in the case of a univariate regression this - can be 1 - b_bits (int): bits for bias (this is a single value so a single bit is enough) - out_bits (int): bits for the result of the linear transformation (w.x + b). - In our case since the result of the linear transformation is - directly decrypted we can use the maximum of 7 bits - - Other parameters: - weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality - bias: a numpy scalar - calibration_data: a numpy nd-array of data (Nxd) - """ - self.n_bits = out_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(q_bits, calibration_data) - - # Quantize the weights and create the quantized linear layer - q_weights = QuantizedArray(w_bits, weights) - q_bias = QuantizedArray(b_bits, bias) - q_layer = QuantizedLinear(out_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 - ) - - # Finally construct our 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 - ): - """ - This function calibrates a layer of a quantized module (e.g. linear, inverse-link, - activation, etc) by looking at the input data, then computes the output of the quantized - version of the layer to be used as input to the following layers - """ - - # 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): - """Quantize an input set with the quantization parameters determined from calibration""" - q_input_arr = deepcopy(self.q_calibration_data) - q_input_arr.update_values(x) - return q_input_arr - - -@progress.track([{"id": "linear-regression", "name": "Linear Regression"}]) -def main(): - """ - Our linear regression benchmark. Use some synthetic data to train a regression model, - then fit a model with sklearn. We quantize the sklearn model and compile it to FHE. - We compute the training loss for the quantized and FHE models and compare them. We also - predict on a test set and compare FHE results to predictions from the quantized model - """ - - X, y, _ = make_regression( - n_samples=200, n_features=1, n_targets=1, bias=5.0, noise=30.0, random_state=42, coef=True - ) - - # Split it into train/test and sort the sets for nicer visualization - x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42) - - sidx = np.argsort(np.squeeze(x_train)) - x_train = x_train[sidx, :] - y_train = y_train[sidx] - - sidx = np.argsort(np.squeeze(x_test)) - x_test = x_test[sidx, :] - y_test = y_test[sidx] - - # Train a linear regression with sklearn and predict on the test data - linreg = LinearRegression() - linreg.fit(x_train, y_train) - - # Calibrate the model for quantization using both training and test data - calib_data = X # np.vstack((x_train, x_test)) - q_linreg = QuantizedLinearRegression.from_sklearn(linreg, calib_data) - - # Compile the quantized model to FHE - engine = q_linreg.compile( - q_linreg.quantize_input(calib_data), - compilation_configuration=BENCHMARK_CONFIGURATION, - ) - - # Measure test error using the clear-sklearn, the clear-quantized and the FHE quantized model - # as R^2 coefficient for the test data - - # First, predict using the sklearn classifier - y_pred = linreg.predict(x_test) - - # Now that the model is quantized, predict on the test set - x_test_q = q_linreg.quantize_input(x_test) - q_y_pred = q_linreg.forward_and_dequant(x_test_q) - - # Now predict using the FHE quantized model on the testing set - y_test_pred_fhe = np.zeros_like(x_test) - - for i, x_i in enumerate(tqdm(x_test_q.qvalues)): - q_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8) - with progress.measure(id="evaluation-time-ms", label="Evaluation Time (ms)"): - q_pred_fhe = engine.run(q_sample) - y_test_pred_fhe[i] = q_linreg.dequantize_output(q_pred_fhe) - - # Measure the error for the three versions of the classifier - sklearn_r2 = r2_score(y_pred, y_test) - non_homomorphic_test_error = r2_score(q_y_pred, y_test) - homomorphic_test_error = r2_score(y_test_pred_fhe, y_test) - - # Measure the error of the FHE quantized model w.r.t the clear quantized model - difference = ( - abs(homomorphic_test_error - non_homomorphic_test_error) * 100 / non_homomorphic_test_error - ) - - print(f"Sklearn R^2: {sklearn_r2:.4f}") - progress.measure( - id="sklearn-r2", - label="Sklearn R^2", - value=sklearn_r2, - ) - - print(f"Non Homomorphic R^2: {non_homomorphic_test_error:.4f}") - progress.measure( - id="non-homomorphic-r2", - label="Non Homomorphic R^2", - value=non_homomorphic_test_error, - ) - - print(f"Homomorphic R^2: {homomorphic_test_error:.4f}") - progress.measure( - id="homomorphic-r2", - label="Homomorphic R^2", - value=homomorphic_test_error, - ) - - print(f"Relative Loss Difference (%): {difference:.2f}%") - progress.measure( - id="relative-loss-difference-percent", - label="Relative Loss Difference (%)", - value=difference, - alert=(">", 7.5), - ) diff --git a/benchmarks/ml/logistic_regression.py b/benchmarks/ml/logistic_regression.py deleted file mode 100644 index 70480a4d7..000000000 --- a/benchmarks/ml/logistic_regression.py +++ /dev/null @@ -1,230 +0,0 @@ -from copy import deepcopy -from typing import Any, Dict - -import numpy as np -import progress -from common import BENCHMARK_CONFIGURATION -from numpy.random import RandomState -from sklearn.datasets import make_classification -from sklearn.linear_model import LogisticRegression -from sklearn.model_selection import train_test_split -from tqdm import tqdm - -from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule, QuantizedSigmoid - - -class QuantizedLogisticRegression(QuantizedModule): - """ - Quantized Logistic Regression - Building on top of QuantizedModule, this class will chain together a linear transformation - and an inverse-link function, in this case the logistic function - """ - - @staticmethod - def from_sklearn(sklearn_model, calibration_data): - """Create a Quantized Logistic Regression initialized from a sklearn trained model""" - if sklearn_model.coef_.ndim == 1: - weights = np.expand_dims(sklearn_model.coef_, 1) - else: - weights = sklearn_model.coef_.transpose() - - bias = sklearn_model.intercept_ - - # In our case we have two data dimensions, we the weights precision needs to be 2 bits, as - # for now we need the quantized values to be greater than zero for weights - # Thus, to insure a maximum of 7 bits in the output of the linear transformation, we choose - # 4 bits for the data and the minimum of 1 for the bias - return QuantizedLogisticRegression(4, 2, 1, 6, weights, bias, calibration_data) - - def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None: - """ - Create the Logistic regression with different quantization bit precisions: - - Quantization Parameters - Number of bits: - q_bits (int): bits for input data, insuring that the number of bits of - the w . x + b operation does not exceed 7 for the calibration data - w_bits (int): bits for weights: in the case of a univariate regression this - can be 1 - b_bits (int): bits for bias (this is a single value so a single bit is enough) - out_bits (int): bits for the result of the linear transformation (w.x + b). - In the case of Logistic Regression the result of the linear - transformation is input to a univariate inverse-link function, so - this value can be 7 - - Other parameters: - weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality - bias: a numpy scalar - calibration_data: a numpy nd-array of data (Nxd) - """ - self.n_bits = out_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(q_bits, calibration_data) - - # Quantize the weights and create the quantized linear layer - q_weights = QuantizedArray(w_bits, weights) - q_bias = QuantizedArray(b_bits, bias) - q_layer = QuantizedLinear(out_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 needs to be quantized since it's computed in FHE, - # but 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_logit = QuantizedSigmoid(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_logit, calibration_data, quant_layers_dict - ) - - # Finally construct our 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 - ): - """ - This function calibrates a layer of a quantized module (e.g. linear, inverse-link, - activation, etc) by looking at the input data, then computes the output of the quantized - version of the layer to be used as input to the following layers - """ - - # 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 - - -@progress.track([{"id": "logistic-regression", "name": "Logistic Regression"}]) -def main(): - """Main benchmark function: generate some synthetic data for two class classification, - split train-test, train a sklearn classifier, calibrate and quantize it on the whole dataset - then compile it to FHE. Test the three versions of the classifier on the test set and - report accuracy""" - - # Generate some data with a fixed seed - X, y = make_classification( - n_features=2, - n_redundant=0, - n_informative=2, - random_state=2, - n_clusters_per_class=1, - n_samples=100, - ) - - # Scale the data randomly, fixing seeds for reproductibility - rng = RandomState(2) - X += 2 * rng.uniform(size=X.shape) - - # Split it into train/test - x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42) - - # Train a logistic regression with sklearn on the training set - logreg = LogisticRegression() - logreg.fit(x_train, y_train) - - # Calibrate the model for quantization using both training and test data - calib_data = X - q_logreg = QuantizedLogisticRegression.from_sklearn(logreg, calib_data) - - # Now, we can compile our model to FHE, taking as possible input set all of our dataset - engine = q_logreg.compile( - q_logreg.quantize_input(X), - compilation_configuration=BENCHMARK_CONFIGURATION, - ) - - # Start classifier evaluation - - # Test the original classifier - y_pred_test = np.asarray(logreg.predict(x_test)) - - # Now that the model is quantized, predict on the test set - x_test_q = q_logreg.quantize_input(x_test) - q_y_score_test = q_logreg.forward_and_dequant(x_test_q) - q_y_pred_test = (q_y_score_test > 0.5).astype(np.int32) - - non_homomorphic_correct = 0 - homomorphic_correct = 0 - - # Track the samples that are wrongly classified due to quantization issues - q_wrong_predictions = np.zeros((0, 2), dtype=X.dtype) - - # Predict the FHE quantized classifier probabilities on the test set. - # Compute FHE quantized accuracy, clear-quantized accuracy and - # keep track of samples wrongly classified due to quantization - for i, x_i in enumerate(tqdm(x_test_q.qvalues)): - y_i = y_test[i] - - fhe_in_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8) - - with progress.measure(id="evaluation-time-ms", label="Evaluation Time (ms)"): - q_pred_fhe = engine.run(fhe_in_sample) - - y_score_fhe = q_logreg.dequantize_output(q_pred_fhe) - homomorphic_prediction = (y_score_fhe > 0.5).astype(np.int32) - - non_homomorphic_prediction = q_y_pred_test[i] - if non_homomorphic_prediction == y_i: - non_homomorphic_correct += 1 - elif y_pred_test[i] == y_i: - # If this was a correct prediction with the clear-sklearn classifier - q_wrong_predictions = np.vstack((q_wrong_predictions, x_test[i, :])) - - if homomorphic_prediction == y_i: - homomorphic_correct += 1 - - # Aggregate accuracies for all the versions of the classifier - sklearn_acc = np.sum(y_pred_test == y_test) / len(y_test) * 100 - non_homomorphic_accuracy = (non_homomorphic_correct / len(y_test)) * 100 - homomorphic_accuracy = (homomorphic_correct / len(y_test)) * 100 - difference = abs(homomorphic_accuracy - non_homomorphic_accuracy) - - print(f"Sklearn Accuracy (%): {sklearn_acc:.4f}") - progress.measure( - id="sklearn-accuracy-percent", - label="Sklearn Accuracy (%)", - value=sklearn_acc, - ) - - print(f"Non Homomorphic Accuracy (%): {non_homomorphic_accuracy:.4f}") - progress.measure( - id="non-homomorphic-accuracy-percent", - label="Non Homomorphic Accuracy (%)", - value=non_homomorphic_accuracy, - ) - - print(f"Homomorphic Accuracy (%): {homomorphic_accuracy:.4f}") - progress.measure( - id="homomorphic-accuracy-percent", - label="Homomorphic Accuracy (%)", - value=homomorphic_accuracy, - ) - - print(f"Relative Accuracy Difference (%): {difference:.2f}%") - progress.measure( - id="relative-accuracy-difference-percent", - label="Relative Accuracy Difference (%)", - value=difference, - alert=(">", 2.0), - ) diff --git a/benchmarks/concrete/generic.py b/benchmarks/unit.py similarity index 99% rename from benchmarks/concrete/generic.py rename to benchmarks/unit.py index c4f4ccacb..880afe479 100644 --- a/benchmarks/concrete/generic.py +++ b/benchmarks/unit.py @@ -1,7 +1,7 @@ import random import numpy as np -import progress +import py_progress_tracker as progress from common import BENCHMARK_CONFIGURATION import concrete.numpy as hnp diff --git a/concrete/common/compilation/artifacts.py b/concrete/common/compilation/artifacts.py index 998a9b609..28526e908 100644 --- a/concrete/common/compilation/artifacts.py +++ b/concrete/common/compilation/artifacts.py @@ -9,7 +9,6 @@ from typing import Any, Callable, Dict, Optional, Union import networkx as nx from loguru import logger -from PIL import Image from ..debugging import assert_true, draw_graph, format_operation_graph from ..operator_graph import OPGraph @@ -27,7 +26,7 @@ class CompilationArtifacts: source_code_of_the_function_to_compile: Optional[str] parameters_of_the_function_to_compile: Dict[str, str] - drawings_of_operation_graphs: Dict[str, Image.Image] + drawings_of_operation_graphs: Dict[str, str] textual_representations_of_operation_graphs: Dict[str, str] final_operation_graph: Optional[OPGraph] diff --git a/concrete/quantization/__init__.py b/concrete/quantization/__init__.py deleted file mode 100644 index c9facc1dc..000000000 --- a/concrete/quantization/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Modules for quantization.""" -from .post_training import PostTrainingAffineQuantization -from .quantized_activations import QuantizedReLU6, QuantizedSigmoid -from .quantized_array import QuantizedArray -from .quantized_layers import QuantizedLinear -from .quantized_module import QuantizedModule diff --git a/concrete/quantization/post_training.py b/concrete/quantization/post_training.py deleted file mode 100644 index 94e9cdf98..000000000 --- a/concrete/quantization/post_training.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Post Training Quantization methods.""" - -import numpy -from torch import nn - -from ..torch import NumpyModule -from .quantized_activations import QuantizedReLU6, QuantizedSigmoid -from .quantized_array import QuantizedArray -from .quantized_layers import QuantizedLinear -from .quantized_module import QuantizedModule - - -class PostTrainingAffineQuantization: - """Post-training Affine Quantization.""" - - IMPLEMENTED_MODULES = {nn.Linear, nn.Sigmoid, nn.ReLU6} - - quant_layers_dict: dict - n_bits: int - quant_params: dict - numpy_model: NumpyModule - is_signed: bool - - def __init__(self, n_bits: int, numpy_model: NumpyModule, is_signed: bool = False): - """Create the quantized version of numpy module. - - Args: - n_bits (int): Number of bits to quantize the model. Currently this - n_bits will be used for all activation/inputs/weights - numpy_model (NumpyModule): Model in numpy. - is_signed: Whether the weights of the layers can be signed. - Currently, only the weights can be signed. - - Returns: - QuantizedModule: A quantized version of the numpy model. - """ - self.quant_layers_dict = {} - self.n_bits = n_bits - self.quant_params = {} - self.numpy_model = numpy_model - self.is_signed = is_signed - - def quantize_module(self, calibration_data: numpy.ndarray) -> QuantizedModule: - """Quantize numpy module. - - Following https://arxiv.org/abs/1712.05877 guidelines. - - Args: - calibration_data (numpy.ndarray): Data that will be used to compute the bounds, - scales and zero point values for every quantized - object. - - Returns: - QuantizedModule: Quantized numpy module - """ - # First transform all parameters to their quantized version - self._quantize_params() - # Quantize and calibrate each output layer/activation - self._quantize_layers(calibration_data=calibration_data) - # Create quantized module from self.quant_layers_dict - return QuantizedModule(self.quant_layers_dict) - - def _quantize_params(self): - """Transform all floating points parameters to integers.""" - - for name, params in self.numpy_model.numpy_module_dict.items(): - self.quant_params[name] = QuantizedArray(self.n_bits, params, self.is_signed) - - def _calibrate_layers_activation(self, name, q_function, calibration_data): - # Calibrate the output of the layer - q_function.calibrate(calibration_data) - # Store the learned quantized layer - self.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_layers(self, calibration_data: numpy.ndarray): - """Compute all parameters for the static post-training quantization. - - Does a forward pass over a batch of data and compute all - quantization parameters for activations and layers. - """ - for name, layer in self.numpy_model.torch_model.named_children(): - - if isinstance(layer, nn.Linear): - # Create a QuantizedLinear layer - q_weights = self.quant_params[f"{name}.weight"] - q_bias = self.quant_params[f"{name}.bias"] - # Check if layer is last layer from the model - if name == list(self.numpy_model.torch_model.named_children())[-1][0]: - # If last layer, we can use 7 bits (maximum allowed) of precision. - # However, 6 bits is currently used to allow 100% FHE precision - # compared to its quantized counterpart. - # Since this is the last layer and mostly used for classification, - # this does not have much impact. - # TODO: Put back 7 bits when 100% at 7b is achieved (see issue #1332). - q_layer = QuantizedLinear(numpy.maximum(6, self.n_bits), q_weights, q_bias) - else: - q_layer = QuantizedLinear(self.n_bits, q_weights, q_bias) - # Calibrate and get new calibration_data for next layer/activation - calibration_data = self._calibrate_layers_activation( - name, q_layer, calibration_data - ) - elif isinstance(layer, nn.Sigmoid): - # Create a new quantized layer (based on type(layer)) - q_sigmoid = QuantizedSigmoid(n_bits=self.n_bits) - calibration_data = self._calibrate_layers_activation( - name, q_sigmoid, calibration_data - ) - elif isinstance(layer, nn.ReLU6): - # Create a new quantized layer (based on type(layer)) - q_relu = QuantizedReLU6(n_bits=self.n_bits) - calibration_data = self._calibrate_layers_activation(name, q_relu, calibration_data) - else: # pragma: no cover - # If we find a layer that has not been implemented we throw an error - hf_m_names = sorted(module.__name__ for module in self.IMPLEMENTED_MODULES) - raise ValueError( - f"The following module is currently not implemented: {type(layer).__name__}" - f"Please stick to the available quantized modules:" - f"{', '.join(hf_m_names)}." - ) diff --git a/concrete/quantization/quantized_activations.py b/concrete/quantization/quantized_activations.py deleted file mode 100644 index 4b8dd8f09..000000000 --- a/concrete/quantization/quantized_activations.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Quantized activation functions.""" -import copy -from abc import ABC, abstractmethod -from typing import Optional - -import numpy - -from .quantized_array import QuantizedArray - - -class QuantizedActivation(ABC): - """Base class for quantized activation function.""" - - q_out: Optional[QuantizedArray] - - def __init__(self, n_bits) -> None: - self.n_bits = n_bits - self.q_out = None - - @abstractmethod - def __call__(self, q_input: QuantizedArray) -> QuantizedArray: - """Execute the forward pass.""" - - @abstractmethod - def calibrate(self, x: numpy.ndarray) -> None: - """Create corresponding QuantizedArray for the output of the activation function. - - Args: - x (numpy.ndarray): Inputs. - """ - - @staticmethod - def dequant_input(q_input: QuantizedArray) -> numpy.ndarray: - """Dequantize the input of the activation function. - - Args: - q_input (QuantizedArray): Quantized array for the inputs - - Returns: - numpy.ndarray: Return dequantized input in a numpy array - """ - - # TODO remove this + (-x) when issue #721 is fixed - return (q_input.qvalues + (-q_input.zero_point)) * q_input.scale - - def quant_output(self, qoutput_activation: numpy.ndarray) -> QuantizedArray: - """Quantize the output of the activation function. - - Args: - q_out (numpy.ndarray): Output of the activation function. - - Returns: - QuantizedArray: Quantized output. - """ - assert self.q_out is not None - - qoutput_activation = qoutput_activation / self.q_out.scale + self.q_out.zero_point - qoutput_activation = ( - numpy.rint(qoutput_activation).clip(0, 2 ** self.q_out.n_bits - 1).astype(int) - ) - - # TODO find a better way to do the following (see issue #832) - q_out = copy.copy(self.q_out) - q_out.update_qvalues(qoutput_activation) - return q_out - - -class QuantizedSigmoid(QuantizedActivation): - """Quantized sigmoid activation function.""" - - def calibrate(self, x: numpy.ndarray): - self.q_out = QuantizedArray(self.n_bits, 1 / (1 + numpy.exp(-x))) - - def __call__(self, q_input: QuantizedArray) -> QuantizedArray: - """Process the forward pass of the quantized sigmoid. - - Args: - q_input (QuantizedArray): Quantized input. - - Returns: - q_out (QuantizedArray): Quantized output. - """ - - quant_sigmoid = self.dequant_input(q_input) - quant_sigmoid = 1 + numpy.exp(-quant_sigmoid) - quant_sigmoid = 1 / quant_sigmoid - - q_out = self.quant_output(quant_sigmoid) - return q_out - - -class QuantizedReLU6(QuantizedActivation): - """Quantized ReLU6 activation function.""" - - def calibrate(self, x: numpy.ndarray): - x = numpy.minimum(numpy.maximum(0, x), 6) - self.q_out = QuantizedArray(self.n_bits, x) - - def __call__(self, q_input: QuantizedArray) -> QuantizedArray: - """Process the forward pass of the quantized ReLU6. - - Args: - q_input (QuantizedArray): Quantized input. - - Returns: - q_out (QuantizedArray): Quantized output. - """ - - quant_relu6 = self.dequant_input(q_input) - quant_relu6 = numpy.minimum(numpy.maximum(0, quant_relu6), 6) - - q_out = self.quant_output(quant_relu6) - return q_out diff --git a/concrete/quantization/quantized_array.py b/concrete/quantization/quantized_array.py deleted file mode 100644 index 8285106a1..000000000 --- a/concrete/quantization/quantized_array.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Quantization utilities for a numpy array/tensor.""" -from copy import deepcopy -from typing import Optional - -import numpy - -STABILITY_CONST = 10 ** -6 - - -class QuantizedArray: - """Abstraction of quantized array.""" - - def __init__(self, n_bits: int, values: numpy.ndarray, is_signed=False): - """Quantize an array. - - See https://arxiv.org/abs/1712.05877. - - Args: - values (numpy.ndarray): Values to be quantized. - n_bits (int): The number of bits to use for quantization. - is_signed (bool): Whether the quantization can be on signed integers. - """ - - self.offset = 0 - if is_signed: - self.offset = 2 ** (n_bits - 1) - self.values = values - self.n_bits = n_bits - self.is_signed = is_signed - self.scale, self.zero_point, self.qvalues = self.compute_quantization_parameters() - self.n_features = 1 if len(values.shape) <= 1 else values.shape[1] - - def __call__(self) -> Optional[numpy.ndarray]: - return self.qvalues - - def compute_quantization_parameters(self): - """Compute the quantization parameters.""" - # Small constant needed for stability - rmax = numpy.max(self.values) - rmin = numpy.min(self.values) - - if rmax - rmin < STABILITY_CONST: - # In this case there is a single unique value to quantize - - # is is_signed is True, we need to set the offset back to 0. - # Signed quantization does not make sense for a single value. - self.offset = 0 - - # 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 - - 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 - - zero_point = numpy.round( - (rmax * (-self.offset) - (rmin * (2 ** self.n_bits - 1 - self.offset))) - / (rmax - rmin) - ).astype(int) - - # Compute quantized values and store - qvalues = self.values / scale + zero_point - - qvalues = ( - numpy.rint(qvalues) - .clip(-self.offset, 2 ** (self.n_bits) - 1 - self.offset) - .astype(int) # Careful this can be very large with high number of bits - ) - - return scale, zero_point, qvalues - - def update_values(self, values: numpy.ndarray) -> Optional[numpy.ndarray]: - """Update values to get their corresponding qvalues using the related quantized parameters. - - Args: - values (numpy.ndarray): Values to replace self.values - - Returns: - qvalues (numpy.ndarray): Corresponding qvalues - """ - self.values = deepcopy(values) - self.quant() - return self.qvalues - - def update_qvalues(self, qvalues: numpy.ndarray) -> Optional[numpy.ndarray]: - """Update qvalues to get their corresponding values using the related quantized parameters. - - Args: - qvalues (numpy.ndarray): Values to replace self.qvalues - - Returns: - values (numpy.ndarray): Corresponding values - """ - self.qvalues = deepcopy(qvalues) - self.dequant() - return self.values - - def quant(self) -> Optional[numpy.ndarray]: - """Quantize self.values. - - Returns: - numpy.ndarray: Quantized values. - """ - - self.qvalues = ( - numpy.rint(self.values / self.scale + self.zero_point) - .clip(-self.offset, 2 ** (self.n_bits) - 1 - self.offset) - .astype(int) - ) - return self.qvalues - - def dequant(self) -> numpy.ndarray: - """Dequantize self.qvalues. - - Returns: - numpy.ndarray: Dequantized values. - """ - self.values = self.scale * (self.qvalues - self.zero_point) - return self.values diff --git a/concrete/quantization/quantized_layers.py b/concrete/quantization/quantized_layers.py deleted file mode 100644 index fae472903..000000000 --- a/concrete/quantization/quantized_layers.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Quantized layers.""" -import copy -from typing import Optional - -import numpy - -from .quantized_array import QuantizedArray - - -class QuantizedLinear: - """Fully connected quantized layer.""" - - q_out: Optional[QuantizedArray] - - def __init__( - self, n_bits: int, q_weights: QuantizedArray, q_bias: Optional[QuantizedArray] = None - ): - """Implement the forward pass of a quantized linear layer. - - Note: QuantizedLinear seems to become unstable when n_bits > 23. - - Args: - n_bits (int): Maximum number of bits for the ouput. - q_weights (QuantizedArray): Quantized weights (n_features, n_neurons). - q_bias (QuantizedArray, optional): Quantized bias (1, n_neurons). Defaults to None. - """ - self.q_weights = q_weights - self.q_bias = q_bias - self.n_bits = n_bits - - if self.q_bias is None: - self.q_bias = QuantizedArray(n_bits, numpy.zeros(self.q_weights.values.shape[-1])) - self.q_out = None - - def calibrate(self, x: numpy.ndarray): - """Create corresponding QuantizedArray for the output of QuantizedLinear. - - Args: - x (numpy.ndarray): Inputs. - """ - assert self.q_bias is not None - self.q_out = QuantizedArray(self.n_bits, (x @ self.q_weights.values) + self.q_bias.values) - - def __call__(self, q_input: QuantizedArray) -> QuantizedArray: - """Process the forward pass of the quantized linear layer. - - Note: in standard quantization, floats are problematics as quantization - targets a specific integer only hardware. However in FHE, we can create a table lookup - to bypass this problem. Thus we leave the floats as is. - Args: - q_input (QuantizedArray): Quantized input. - - Returns: - q_out_ (QuantizedArray): Quantized output. - """ - # Satisfy mypy. - assert self.q_out is not None - assert self.q_bias is not None - - # The following MatMul is done with integers, and thus, does not use of any PBS. - # Only the final conversion to float is done with a PBS, which can actually - # be merged with the PBS of following activation. - # State of the art quantization method assumes the following results in a int32 accumulator. - - # Here we follow Eq.7 in https://arxiv.org/abs/1712.05877 to split the core computation - # from the zero points and scales. - - p = self.q_weights.qvalues.shape[0] - - # Core matmul operation in full intergers with a shape change (INTEGERS) - matmul = q_input.qvalues @ self.q_weights.qvalues - - # Sum operation in full integers resulting in large integers (INTEGERS) - # [WORKAROUND #995] numpy.sum can't be currently done in our framework - # sum_input = self.q_weights.zero_point * numpy.sum(q_input.qvalues, axis=1, keepdims=True) - # Hack because we can't do numpy.sum(axis...,keepdims...) - const_ones = numpy.ones(shape=(q_input.n_features, 1), dtype=int) - sum_input = self.q_weights.zero_point * (q_input.qvalues @ const_ones) - - # Last part that has to be done in FHE the rest must go in a PBS. - # Forced fusing using .astype(numpy.float32) - numpy_q_out = (matmul + (numpy.negative(sum_input))).astype(numpy.float32) - - # sum_weights is a constant - sum_weights = q_input.zero_point * numpy.sum(self.q_weights.qvalues, axis=0, keepdims=True) - - # Quantization scales and zero points (FLOATS involved) - # This is going to be compiled with a PBS (along with the following activation function) - m_matmul = (q_input.scale * self.q_weights.scale) / (self.q_out.scale) - bias_part = ( - self.q_bias.scale / self.q_out.scale * (self.q_bias.qvalues - self.q_bias.zero_point) - ) - final_term = p * q_input.zero_point * self.q_weights.zero_point - - numpy_q_out = numpy_q_out + final_term + (numpy.negative(sum_weights)) - numpy_q_out = m_matmul * numpy_q_out - numpy_q_out = self.q_out.zero_point + bias_part + numpy_q_out - - numpy_q_out = numpy.rint(numpy_q_out).clip(0, 2 ** self.q_out.n_bits - 1).astype(int) - - # TODO find a more intuitive way to do the following (see issue #832) - # We should be able to reuse q_out quantization parameters - # easily to get a new QuantizedArray - q_out_ = copy.copy(self.q_out) - q_out_.update_qvalues(numpy_q_out) - - return q_out_ diff --git a/concrete/quantization/quantized_module.py b/concrete/quantization/quantized_module.py deleted file mode 100644 index 9670c5983..000000000 --- a/concrete/quantization/quantized_module.py +++ /dev/null @@ -1,128 +0,0 @@ -"""QuantizedModule API.""" -import copy -from typing import Optional, Union - -import numpy - -from ..common.compilation.artifacts import CompilationArtifacts -from ..common.compilation.configuration import CompilationConfiguration -from ..common.fhe_circuit import FHECircuit -from ..numpy.np_fhe_compiler import NPFHECompiler -from .quantized_array import QuantizedArray - - -class QuantizedModule: - """Inference for a quantized model.""" - - quant_layers_dict: dict - _mode: str - q_input: Optional[QuantizedArray] - forward_fhe: Union[None, FHECircuit] - - def __init__(self, quant_layers_dict: dict): - self.quant_layers_dict = copy.deepcopy(quant_layers_dict) - self.compiled = False - self.forward_fhe = None - self.q_input = None - - def __call__(self, x: QuantizedArray): - return self.forward(x) - - def forward(self, q_x: Union[numpy.ndarray, QuantizedArray]) -> numpy.ndarray: - """Forward pass with numpy function only. - - Args: - q_x (Union[numpy.ndarray, QuantizedArray]): QuantizedArray containing the inputs - or a numpy.array containing the q_values. - In the latter, the stored input parameters - are used: - (q_input.scale, q_input.zero_point). - - Returns: - (numpy.ndarray): Predictions of the quantized model - """ - # Following "if not" important for compilation as the tracer - # need to fall in it the statement (tracing). - # If the q_x is a numpy module then we reuse self.q_input parameters - # computed during calibration. - # Later we might want to only allow nympy.array input - if not isinstance(q_x, QuantizedArray): - assert self.q_input is not None - self.q_input.update_qvalues(q_x) - q_x = self.q_input - - for _, layer in self.quant_layers_dict.items(): - q_x = layer(q_x) - - # mypy compliance - assert isinstance(q_x, QuantizedArray) - - return q_x.qvalues - - def forward_and_dequant(self, q_x: Union[numpy.ndarray, QuantizedArray]) -> numpy.ndarray: - """Forward pass with numpy function only plus dequantization. - - Args: - q_x (Union[numpy.ndarray, QuantizedArray]): QuantizedArray containing the inputs - or a numpy.array containing the q_values. - In the latter, the stored input parameters - are used: - (q_input.scale, q_input.zero_point). - - Returns: - (numpy.ndarray): Predictions of the quantized model - """ - q_out = self.forward(q_x) - return self.dequantize_output(q_out) - - def dequantize_output(self, qvalues: numpy.ndarray) -> numpy.ndarray: - """Take the last layer q_out and use its dequant function. - - Args: - qvalues (numpy.ndarray): Quantized values of the last layer. - - Returns: - numpy.ndarray: Dequantized values of the last layer. - """ - last_layer = list(self.quant_layers_dict.values())[-1] - real_values = last_layer.q_out.update_qvalues(qvalues) - return real_values - - def compile( - self, - q_input: QuantizedArray, - compilation_configuration: Optional[CompilationConfiguration] = None, - compilation_artifacts: Optional[CompilationArtifacts] = None, - show_mlir: bool = False, - ) -> FHECircuit: - """Compile the forward function of the module. - - Args: - q_input (QuantizedArray): Needed for tracing and building the boundaries. - compilation_configuration (Optional[CompilationConfiguration]): Configuration object - to use during - compilation - compilation_artifacts (Optional[CompilationArtifacts]): Artifacts object to fill during - compilation - show_mlir (bool, optional): if set, the MLIR produced by the converter and which is - going to be sent to the compiler backend is shown on the screen, e.g., for debugging - or demo. Defaults to False. - - Returns: - FHECircuit: the compiled FHECircuit. - """ - - self.q_input = copy.deepcopy(q_input) - compiler = NPFHECompiler( - self.forward, - { - "q_x": "encrypted", - }, - compilation_configuration, - compilation_artifacts, - ) - self.forward_fhe = compiler.compile_on_inputset( - (numpy.expand_dims(arr, 0) for arr in self.q_input.qvalues), show_mlir - ) - - return self.forward_fhe diff --git a/concrete/torch/__init__.py b/concrete/torch/__init__.py deleted file mode 100644 index 1071631be..000000000 --- a/concrete/torch/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Modules for torch to numpy conversion.""" -from .numpy_module import NumpyModule diff --git a/concrete/torch/compile.py b/concrete/torch/compile.py deleted file mode 100644 index 8872cbbc4..000000000 --- a/concrete/torch/compile.py +++ /dev/null @@ -1,90 +0,0 @@ -"""torch compilation function.""" - -from typing import Iterable, Optional, Union - -import numpy -import torch - -from ..common.compilation import CompilationArtifacts, CompilationConfiguration -from ..quantization import PostTrainingAffineQuantization, QuantizedArray, QuantizedModule -from . import NumpyModule - -TorchDataset = Iterable[torch.Tensor] -NPDataset = Iterable[numpy.ndarray] - - -def convert_torch_tensor_or_numpy_array_to_numpy_array( - torch_tensor_or_numpy_array: Union[torch.Tensor, numpy.ndarray] -) -> numpy.ndarray: - """Convert a torch tensor or a numpy array to a numpy array. - - Args: - torch_tensor_or_numpy_array (Union[torch.Tensor, numpy.ndarray]): the value that is either - a torch tensor or a numpy array. - - Returns: - numpy.ndarray: the value converted to a numpy array. - """ - return ( - torch_tensor_or_numpy_array - if isinstance(torch_tensor_or_numpy_array, numpy.ndarray) - else torch_tensor_or_numpy_array.cpu().numpy() - ) - - -def compile_torch_model( - torch_model: torch.nn.Module, - torch_inputset: Union[TorchDataset, NPDataset], - compilation_configuration: Optional[CompilationConfiguration] = None, - compilation_artifacts: Optional[CompilationArtifacts] = None, - show_mlir: bool = False, - n_bits=7, -) -> QuantizedModule: - """Take a model in torch, turn it to numpy, transform weights to integer. - - Later, we'll compile the integer model. - - Args: - torch_model (torch.nn.Module): the model to quantize, - torch_inputset (Union[TorchDataset, NPDataset]): the inputset, can contain either torch - tensors or numpy.ndarray, only datasets with a single input are supported for now. - function_parameters_encrypted_status (Dict[str, Union[str, EncryptedStatus]]): a dict with - the name of the parameter and its encrypted status - compilation_configuration (CompilationConfiguration): Configuration object to use - during compilation - compilation_artifacts (CompilationArtifacts): Artifacts object to fill - during compilation - show_mlir (bool): if set, the MLIR produced by the converter and which is going - to be sent to the compiler backend is shown on the screen, e.g., for debugging or demo - n_bits: the number of bits for the quantization - - Returns: - QuantizedModule: The resulting compiled QuantizedModule. - """ - - # Create corresponding numpy model - numpy_model = NumpyModule(torch_model) - - # Torch input to numpy - numpy_inputset_as_single_array = numpy.concatenate( - tuple( - numpy.expand_dims(convert_torch_tensor_or_numpy_array_to_numpy_array(input_), 0) - for input_ in torch_inputset - ) - ) - - # Quantize with post-training static method, to have a model with integer weights - post_training_quant = PostTrainingAffineQuantization(n_bits, numpy_model, is_signed=True) - quantized_module = post_training_quant.quantize_module(numpy_inputset_as_single_array) - - # Quantize input - quantized_numpy_inputset = QuantizedArray(n_bits, numpy_inputset_as_single_array) - - quantized_module.compile( - quantized_numpy_inputset, - compilation_configuration, - compilation_artifacts, - show_mlir, - ) - - return quantized_module diff --git a/concrete/torch/numpy_module.py b/concrete/torch/numpy_module.py deleted file mode 100644 index 349062822..000000000 --- a/concrete/torch/numpy_module.py +++ /dev/null @@ -1,73 +0,0 @@ -"""A torch to numpy module.""" -import numpy -from torch import nn - - -class NumpyModule: - """General interface to transform a torch.nn.Module to numpy module.""" - - IMPLEMENTED_MODULES = {nn.Linear, nn.Sigmoid, nn.ReLU6} - - def __init__(self, torch_model: nn.Module): - """Initialize our numpy module. - - Current constraint: All objects used in the forward have to be defined in the - __init__() of torch.nn.Module and follow the exact same order. - (i.e. each linear layer must have one variable defined in the - right order). This constraint will disappear when - TorchScript is in place. (issue #818) - - Args: - torch_model (nn.Module): A fully trained, torch model alond with its parameters. - """ - self.torch_model = torch_model - self.check_compatibility() - self.convert_to_numpy() - - def check_compatibility(self): - """Check the compatibility of all layers in the torch model.""" - - for _, layer in self.torch_model.named_children(): - if (layer_type := type(layer)) not in self.IMPLEMENTED_MODULES: - raise ValueError( - f"The following module is currently not implemented: {layer_type.__name__}. " - f"Please stick to the available torch modules: " - f"{', '.join(sorted(module.__name__ for module in self.IMPLEMENTED_MODULES))}." - ) - return True - - def convert_to_numpy(self): - """Transform all parameters from torch tensor to numpy arrays.""" - self.numpy_module_dict = {} - - for name, weights in self.torch_model.state_dict().items(): - params = weights.detach().numpy() - self.numpy_module_dict[name] = params.T if "weight" in name else params - - def __call__(self, x: numpy.ndarray): - """Return the function to be compiled.""" - return self.forward(x) - - def forward(self, x: numpy.ndarray) -> numpy.ndarray: - """Apply a forward pass with numpy function only. - - Args: - x (numpy.array): Input to be processed in the forward pass. - - Returns: - x (numpy.array): Processed input. - """ - - for name, layer in self.torch_model.named_children(): - - if isinstance(layer, nn.Linear): - # Apply a matmul product and add the bias. - x = ( - x @ self.numpy_module_dict[f"{name}.weight"] - + self.numpy_module_dict[f"{name}.bias"] - ) - elif isinstance(layer, nn.Sigmoid): - x = 1 / (1 + numpy.exp(-x)) - elif isinstance(layer, nn.ReLU6): - x = numpy.minimum(numpy.maximum(0, x), 6) - return x diff --git a/deps_licenses/licenses_linux_user.txt b/deps_licenses/licenses_linux_user.txt index 3c85c2394..4cb0ed904 100644 --- a/deps_licenses/licenses_linux_user.txt +++ b/deps_licenses/licenses_linux_user.txt @@ -8,13 +8,11 @@ loguru 0.5.3 MIT License matplotlib 3.5.1 Python Software Foundation License networkx 2.6.3 BSD License - numpy 1.22.0 BSD License + numpy 1.22.1 BSD License packaging 21.3 Apache Software License; BSD License pygraphviz 1.7 BSD License pyparsing 3.0.6 MIT License python-dateutil 2.8.2 Apache Software License; BSD License - setuptools-scm 6.3.2 MIT License + setuptools-scm 6.4.1 MIT License six 1.16.0 MIT License tomli 1.2.3 MIT License - torch 1.10.1 BSD License - typing-extensions 4.0.1 Python Software Foundation License diff --git a/docs/_static/compilation-pipeline/torch_to_numpy_flow.svg b/docs/_static/compilation-pipeline/torch_to_numpy_flow.svg deleted file mode 100644 index ea6710b1f..000000000 --- a/docs/_static/compilation-pipeline/torch_to_numpy_flow.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -DataDataAlgorithm/Function/TransformAlgorithm/Function/TransformConstraints checkConstraints checkInputtorch ModuleInput...Post training-quantizationPost training-quantizationQuantizedModuleQuantizedModuleSupported layersWell formed model definitionSupported layers...Representative datasetRepresentative datasetConvert to NumPy equivalentConvert to NumPy equivalentNumpyModuleNumpyModuleQuantize with QuantizedModule parametersQuantize with QuantizedMod...Quantized datasetQuantized datasetNumPy compilation flowNumPy compilation flowExecutable FHE programExecutable FHE programViewer does not support full SVG 1.1 \ No newline at end of file diff --git a/docs/dev/explanation/compilation.md b/docs/dev/explanation/compilation.md index 9256fef01..3d7edfe78 100644 --- a/docs/dev/explanation/compilation.md +++ b/docs/dev/explanation/compilation.md @@ -53,22 +53,6 @@ Here is the visual representation of the pipeline:  -## Overview of the torch compilation process - -Compiling a torch Module is pretty straightforward. - -The torch Module is first converted to a Numpy equivalent we call `NumpyModule` if all the layers in the torch Module are supported. - -Then the module is quantized post-training to be compatible with our compiler which only works on integers. The post training quantization uses the provided dataset for calibration. - -The dataset is then quantized to be usable for compilation with the QuantizedModule. - -The QuantizedModule is compiled yielding an executable FHECircuit. - -Here is the visual representation of the different steps: - - - ## Tracing Given a Python function `f` such as this one, diff --git a/docs/dev/explanation/terminology_and_structure.md b/docs/dev/explanation/terminology_and_structure.md index ce14e86c3..bff4dc826 100644 --- a/docs/dev/explanation/terminology_and_structure.md +++ b/docs/dev/explanation/terminology_and_structure.md @@ -45,13 +45,3 @@ In this section, we will discuss the module structure of **concrete-numpy** brie - np_inputset_helpers: utilities for inputsets - np_mlir_converter: utilities for MLIR conversion - tracing: tracing of numpy functions - - quantization: tools to quantize networks - - post_training: post training quantization - - quantized_activations: management of quantization in activations - - quantized_array: utilities for quantization - - quantized_layers: management of quantization of neural network layers - - quantized_module: main API for quantization - - torch: torch compilation and conversion - - compile: compilation of a torch module, including quantization - - numpy_module: conversion tools to turn a torch module into a numpy function - diff --git a/docs/user/advanced_examples/DecisionTreeClassifier.ipynb b/docs/user/advanced_examples/DecisionTreeClassifier.ipynb deleted file mode 100644 index 6300704c7..000000000 --- a/docs/user/advanced_examples/DecisionTreeClassifier.ipynb +++ /dev/null @@ -1,632 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Decision Tree Classifier" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Trees are a popular class of algorithm in Machine Learning. In this notebook we build a simple Decision Tree Classifier using `scikit-learn` to show that they can be executed homomorphically using Concrete Numpy.\n", - "\n", - "State of the art classifiers are generally a bit more complex than a single decision tree, but here we wanted to demonstrate FHE decision trees so results may not compete with the best models out there.\n", - "\n", - "Converting a tree working over quantized data to its FHE equivalent takes only a few lines of code thanks to Concrete Numpy.\n", - "\n", - "Let's dive in!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The Use Case" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The use case is a spam classification task from OpenML you can find here: https://www.openml.org/d/44\n", - "\n", - "Some pre-extracted features (like some word frequencies) are provided as well as a class, `0` for a normal e-mail and `1` for spam, for 4601 samples.\n", - "\n", - "Let's first get the dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(4601, 57)\n", - "(4601,)\n", - "Number of features: 57\n" - ] - } - ], - "source": [ - "import numpy\n", - "from sklearn.datasets import fetch_openml\n", - "from sklearn.model_selection import train_test_split\n", - "\n", - "features, classes = fetch_openml(data_id=44, as_frame=False, cache=True, return_X_y=True)\n", - "classes = classes.astype(numpy.int64)\n", - "\n", - "print(features.shape)\n", - "print(classes.shape)\n", - "\n", - "num_features = features.shape[1]\n", - "print(f\"Number of features: {num_features}\")\n", - "\n", - "x_train, x_test, y_train, y_test = train_test_split(\n", - " features,\n", - " classes,\n", - " test_size=0.15,\n", - " random_state=42,\n", - ")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We first train a decision tree on the dataset as is and see what performance we can get." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Depth: 29\n", - "Mean accuracy: 0.91027496382055\n", - "Number of test samples: 691\n", - "Number of spams in test samples: 304\n", - "True Negative (legit mail well classified) rate: 0.9328165374677002\n", - "False Positive (legit mail classified as spam) rate: 0.06718346253229975\n", - "False Negative (spam mail classified as legit) rate: 0.11842105263157894\n", - "True Positive (spam well classified) rate: 0.881578947368421\n" - ] - } - ], - "source": [ - "from sklearn.metrics import confusion_matrix\n", - "from sklearn.tree import DecisionTreeClassifier\n", - "\n", - "clear_clf = DecisionTreeClassifier()\n", - "clear_clf = clear_clf.fit(x_train, y_train)\n", - "\n", - "print(f\"Depth: {clear_clf.get_depth()}\")\n", - "\n", - "preds = clear_clf.predict(x_test)\n", - "\n", - "mean_accuracy = numpy.mean(preds == y_test)\n", - "print(f\"Mean accuracy: {mean_accuracy}\")\n", - "\n", - "true_negative, false_positive, false_negative, true_positive = confusion_matrix(\n", - " y_test, preds, normalize=\"true\"\n", - ").ravel()\n", - "\n", - "num_samples = len(y_test)\n", - "num_spam = sum(y_test)\n", - "\n", - "print(f\"Number of test samples: {num_samples}\")\n", - "print(f\"Number of spams in test samples: {num_spam}\")\n", - "\n", - "print(f\"True Negative (legit mail well classified) rate: {true_negative}\")\n", - "print(f\"False Positive (legit mail classified as spam) rate: {false_positive}\")\n", - "print(f\"False Negative (spam mail classified as legit) rate: {false_negative}\")\n", - "print(f\"True Positive (spam well classified) rate: {true_positive}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We now quantize the features to train the tree directly on quantized data, this will make the trained tree FHE friendly by default which is a nice bonus, as well as allowing to see how both trees compare to each other.\n", - "\n", - "The choice here is to compute the quantization parameters over the training set. We use 6 bits for each feature individually as the Concrete Numpy precision for PBSes is better for 6 bits of precision." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 0 0 6 0 3 5 0 0 0 2 0 19 0 0 0 0 0 0 3 0 0 0 0 0\n", - " 4 4 0 7 3 0 0 0 2 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0\n", - " 0 1 0 0 0 0 0 0 1]\n", - "[ 0 0 0 0 6 0 0 0 0 0 0 10 0 0 0 0 0 0 4 0 7 0 0 0\n", - " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n", - " 0 0 0 0 0 0 0 0 0]\n" - ] - } - ], - "source": [ - "from concrete.quantization import QuantizedArray\n", - "\n", - "# And quantize accordingly training and test samples\n", - "q_x_train = numpy.zeros_like(x_train, dtype=numpy.int64)\n", - "q_x_test = numpy.zeros_like(x_test, dtype=numpy.int64)\n", - "for feature_idx in range(num_features):\n", - " q_x_train[:, feature_idx] = QuantizedArray(6, x_train[:, feature_idx]).qvalues\n", - " q_x_test[:, feature_idx] = QuantizedArray(6, x_test[:, feature_idx]).qvalues\n", - "\n", - "print(q_x_train[0])\n", - "print(q_x_test[-1])\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So far so good, we can now train a DecisionTreeClassifier on the quantized dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Depth: 7\n", - "Mean accuracy: 0.8813314037626628\n", - "Number of test samples: 691\n", - "Number of spams in test samples: 304\n", - "True Negative (legit mail well classified) rate: 0.9276485788113695\n", - "False Positive (legit mail classified as spam) rate: 0.07235142118863049\n", - "False Negative (spam mail classified as legit) rate: 0.17763157894736842\n", - "True Positive (spam well classified) rate: 0.8223684210526315\n" - ] - } - ], - "source": [ - "# We limit the depth to have reasonable FHE runtimes, but deep trees can still compile properly!\n", - "clf = DecisionTreeClassifier(max_depth=7)\n", - "clf = clf.fit(q_x_train, y_train)\n", - "\n", - "print(f\"Depth: {clf.get_depth()}\")\n", - "\n", - "preds = clf.predict(q_x_test)\n", - "\n", - "mean_accuracy = numpy.mean(preds == y_test)\n", - "print(f\"Mean accuracy: {mean_accuracy}\")\n", - "\n", - "true_negative, false_positive, false_negative, true_positive = confusion_matrix(\n", - " y_test, preds, normalize=\"true\"\n", - ").ravel()\n", - "\n", - "num_samples = len(y_test)\n", - "num_spam = sum(y_test)\n", - "\n", - "print(f\"Number of test samples: {num_samples}\")\n", - "print(f\"Number of spams in test samples: {num_spam}\")\n", - "\n", - "print(f\"True Negative (legit mail well classified) rate: {true_negative}\")\n", - "print(f\"False Positive (legit mail classified as spam) rate: {false_positive}\")\n", - "print(f\"False Negative (spam mail classified as legit) rate: {false_negative}\")\n", - "print(f\"True Positive (spam well classified) rate: {true_positive}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This simple classifier achieves about a 7% false positive (legit mail classified as spam) rate and about a 17% false negative (spam mail classified as legit) rate. In a more common setting, not shown in this tutorial, we would use gradient boosting to assemble several small classifiers into a single one that would be more effective.\n", - "\n", - "We can see that the accuracy is relatively similar to the tree trained in the clear despite the quantization (to be FHE compatible) and smaller depth to allow for faster FHE computations. The main difference being a higher False Positive rate (legit mail classified as spam).\n", - "\n", - "The point here is not to beat the state of the art methods for spam detection but rather show that given a certain tree classifier we can run it homomorphically." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Homorphic Trees" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we can do that we need to convert the tree to a form that is easy to run homomorphically.\n", - "\n", - "The Hummingbird paper from Microsoft (https://scnakandala.github.io/papers/TR_2020_Hummingbird.pdf and https://github.com/microsoft/hummingbird) gives a method to convert tree evaluation to tensor operations which we support in Concrete Numpy.\n", - "\n", - "The next few cells implement the functions necessary for the conversion. They are not optimized well so that they remain readable.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "# First an sklearn import we need\n", - "from sklearn.tree import _tree" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "def create_hummingbird_tensor_a(tree_, features, internal_nodes):\n", - " \"\"\"Create Hummingbird tensor A.\"\"\"\n", - " a = numpy.zeros((len(features), len(internal_nodes)), dtype=numpy.int64)\n", - " for i in range(a.shape[0]):\n", - " for j in range(a.shape[1]):\n", - " a[i, j] = tree_.feature[internal_nodes[j]] == features[i]\n", - "\n", - " return a" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def create_hummingbird_tensor_b(tree_, internal_nodes, is_integer_tree=False):\n", - " \"\"\"Create Hummingbird tensor B.\"\"\"\n", - " b = numpy.array([tree_.threshold[int_node] for int_node in internal_nodes])\n", - "\n", - " return b.astype(numpy.int64) if is_integer_tree else b" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "def create_subtree_nodes_set_per_node(\n", - " all_nodes, leaf_nodes, is_left_child_of: dict, is_right_child_of: dict\n", - "):\n", - " \"\"\"Create subtrees nodes set for each node in the tree.\"\"\"\n", - " left_subtree_nodes_per_node = {node: set() for node in all_nodes}\n", - " right_subtree_nodes_per_node = {node: set() for node in all_nodes}\n", - "\n", - " current_nodes = {node: None for node in leaf_nodes}\n", - " while current_nodes:\n", - " next_nodes = {}\n", - " for node in current_nodes:\n", - " parent_as_left_child = is_left_child_of.get(node, None)\n", - " if parent_as_left_child is not None:\n", - " left_subtree = left_subtree_nodes_per_node[parent_as_left_child]\n", - " left_subtree.add(node)\n", - " left_subtree.update(left_subtree_nodes_per_node[node])\n", - " left_subtree.update(right_subtree_nodes_per_node[node])\n", - " next_nodes.update({parent_as_left_child: None})\n", - "\n", - " parent_as_right_child = is_right_child_of.get(node, None)\n", - " if parent_as_right_child is not None:\n", - " right_subtree = right_subtree_nodes_per_node[parent_as_right_child]\n", - " right_subtree.add(node)\n", - " right_subtree.update(left_subtree_nodes_per_node[node])\n", - " right_subtree.update(right_subtree_nodes_per_node[node])\n", - " next_nodes.update({parent_as_right_child: None})\n", - "\n", - " current_nodes = next_nodes\n", - "\n", - " return left_subtree_nodes_per_node, right_subtree_nodes_per_node" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "def create_hummingbird_tensor_c(\n", - " all_nodes, internal_nodes, leaf_nodes, is_left_child_of: dict, is_right_child_of: dict\n", - "):\n", - " \"\"\"Create Hummingbird tensor C.\"\"\"\n", - " left_subtree_nodes_per_node, right_subtree_nodes_per_node = create_subtree_nodes_set_per_node(\n", - " all_nodes, leaf_nodes, is_left_child_of, is_right_child_of\n", - " )\n", - "\n", - " c = numpy.zeros((len(internal_nodes), len(leaf_nodes)), dtype=numpy.int64)\n", - "\n", - " for i in range(c.shape[0]):\n", - " for j in range(c.shape[1]):\n", - " if leaf_nodes[j] in right_subtree_nodes_per_node[internal_nodes[i]]:\n", - " c[i, j] = -1\n", - " elif leaf_nodes[j] in left_subtree_nodes_per_node[internal_nodes[i]]:\n", - " c[i, j] = 1\n", - "\n", - " return c" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def create_hummingbird_tensor_d(leaf_nodes, is_left_child_of, is_right_child_of):\n", - " \"\"\"Create Hummingbird tensor D.\"\"\"\n", - " d = numpy.zeros((len(leaf_nodes)), dtype=numpy.int64)\n", - " for k in range(d.shape[0]):\n", - " current_node = leaf_nodes[k]\n", - " num_left_children = 0\n", - " while True:\n", - " if (parent_as_left_child := is_left_child_of.get(current_node, None)) is not None:\n", - " num_left_children += 1\n", - " current_node = parent_as_left_child\n", - " elif (parent_as_right_child := is_right_child_of.get(current_node, None)) is not None:\n", - " current_node = parent_as_right_child\n", - " else:\n", - " break\n", - " d[k] = num_left_children\n", - "\n", - " return d" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "def create_hummingbird_tensor_e(tree_, leaf_nodes, classes):\n", - " \"\"\"Create Hummingbird tensor E.\"\"\"\n", - " e = numpy.zeros((len(leaf_nodes), len(classes)), dtype=numpy.int64)\n", - " for i in range(e.shape[0]):\n", - " leaf_node = leaf_nodes[i]\n", - " assert tree_.feature[leaf_node] == _tree.TREE_UNDEFINED # Sanity check\n", - " for j in range(e.shape[1]):\n", - " value = None\n", - " if tree_.n_outputs == 1:\n", - " value = tree_.value[leaf_node][0]\n", - " else:\n", - " value = tree_.value[leaf_node].T[0]\n", - " class_name = numpy.argmax(value)\n", - " e[i, j] = class_name == j\n", - "\n", - " return e" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "def tree_to_numpy(tree, num_features, classes):\n", - " \"\"\"Convert an sklearn tree to its Hummingbird tensor equivalent.\"\"\"\n", - " tree_ = tree.tree_\n", - "\n", - " number_of_nodes = tree_.node_count\n", - " all_nodes = list(range(number_of_nodes))\n", - " internal_nodes = [\n", - " node_idx\n", - " for node_idx, feature in enumerate(tree_.feature)\n", - " if feature != _tree.TREE_UNDEFINED\n", - " ]\n", - " leaf_nodes = [\n", - " node_idx\n", - " for node_idx, feature in enumerate(tree_.feature)\n", - " if feature == _tree.TREE_UNDEFINED\n", - " ]\n", - "\n", - " features = list(range(num_features))\n", - "\n", - " a = create_hummingbird_tensor_a(tree_, features, internal_nodes)\n", - "\n", - " b = create_hummingbird_tensor_b(tree_, internal_nodes, is_integer_tree=True)\n", - "\n", - " is_left_child_of = {\n", - " left_child: parent\n", - " for parent, left_child in enumerate(tree_.children_left)\n", - " if left_child != _tree.TREE_UNDEFINED\n", - " }\n", - " is_right_child_of = {\n", - " right_child: parent\n", - " for parent, right_child in enumerate(tree_.children_right)\n", - " if right_child != _tree.TREE_UNDEFINED\n", - " }\n", - "\n", - " c = create_hummingbird_tensor_c(\n", - " all_nodes, internal_nodes, leaf_nodes, is_left_child_of, is_right_child_of\n", - " )\n", - "\n", - " d = create_hummingbird_tensor_d(leaf_nodes, is_left_child_of, is_right_child_of)\n", - "\n", - " e = create_hummingbird_tensor_e(tree_, leaf_nodes, classes)\n", - "\n", - " def tree_predict(inputs):\n", - " t = inputs @ a\n", - " t = t <= b\n", - " t = t @ c\n", - " t = t == d\n", - " r = t @ e\n", - " return r\n", - "\n", - " return tree_predict" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "# We can finally convert our tree!\n", - "tree_predict = tree_to_numpy(clf, num_features, classes=[0, 1])" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Results are identical: True\n" - ] - } - ], - "source": [ - "# Let's see if it works as expected\n", - "tensor_predictions = tree_predict(q_x_test)\n", - "tensor_predictions = numpy.argmax(tensor_predictions, axis=1)\n", - "\n", - "tree_predictions = clf.predict(q_x_test)\n", - "\n", - "print(f\"Results are identical: {numpy.array_equal(tensor_predictions, tree_predictions)}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We now have a tensor equivalent of our `DecisionTreeClassifier`, pretty neat isn't it?\n", - "\n", - "Last step is compiling the tensor equivalent to FHE using the Concrete Numpy and it's nearly as easy as 1, 2, 3.\n", - "\n", - "We use the training input data as well as some synthetic data to calibrate the circuit during compilation." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "import concrete.numpy as hnp\n", - "\n", - "compiler = hnp.NPFHECompiler(tree_predict, {\"inputs\": \"encrypted\"})\n", - "fhe_tree = compiler.compile_on_inputset((sample for sample in q_x_train))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And now we can start running the tree homomorphically!" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 10/10 [05:01<00:00, 30.17s/it]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Same predictions of FHE compared to clear: 10/10 (1.0)\n", - "FHE evaluation #1 took 30.765692999993917 s\n", - "FHE evaluation #2 took 30.604038099998434 s\n", - "FHE evaluation #3 took 30.70741419999831 s\n", - "FHE evaluation #4 took 30.64609560000099 s\n", - "FHE evaluation #5 took 29.945520399996894 s\n", - "FHE evaluation #6 took 30.155333900002006 s\n", - "FHE evaluation #7 took 29.776400299997476 s\n", - "FHE evaluation #8 took 30.12118709999777 s\n", - "FHE evaluation #9 took 29.526597299998684 s\n", - "FHE evaluation #10 took 29.392055899996194 s\n", - "Mean FHE evaluation time: 30.16403357999807\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "from tqdm import tqdm\n", - "from time import perf_counter\n", - "\n", - "num_runs = 10\n", - "fhe_preds = []\n", - "clear_preds = []\n", - "fhe_eval_times = []\n", - "for i in tqdm(range(num_runs)):\n", - " start = perf_counter()\n", - " fhe_pred = fhe_tree.run(q_x_test[i].astype(numpy.uint8))\n", - " stop = perf_counter()\n", - " fhe_eval_times.append(stop - start)\n", - " fhe_pred = numpy.argmax(fhe_pred)\n", - " fhe_preds.append(fhe_pred)\n", - " clear_pred = clf.predict(numpy.expand_dims(q_x_test[i], axis=0))\n", - " clear_pred = clear_pred[0]\n", - " clear_preds.append(clear_pred)\n", - "\n", - "fhe_preds = numpy.array(fhe_preds)\n", - "clear_preds = numpy.array(clear_preds)\n", - "\n", - "same_preds = fhe_preds == clear_preds\n", - "n_same_preds = sum(same_preds)\n", - "print(\n", - " f\"Same predictions of FHE compared to clear: {n_same_preds}/{num_runs} \"\n", - " f\"({numpy.mean(same_preds)})\"\n", - ")\n", - "for idx, eval_time in enumerate(fhe_eval_times, 1):\n", - " print(f\"FHE evaluation #{idx} took {eval_time} s\")\n", - "\n", - "print(f\"Mean FHE evaluation time: {numpy.mean(fhe_eval_times)}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Conclusion" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this notebook we showed how to quantize a dataset to train a tree directly on integer data so that it is FHE friendly. We saw that despite quantization and its smaller depth, the quantized tree classification capabilities were close to a tree trained on the original real-valued dataset.\n", - "\n", - "We then used the Hummingbird paper's algorithm to transform a tree evaluation to a few tensor operations which can be compiled by the Concrete Numpy to an FHE circuit.\n", - "\n", - "Finally we ran the compiled circuit on a few samples (because inference times are a bit high) to show that clear and FHE computations were the same." - ] - } - ], - "metadata": { - "execution": { - "timeout": 10800 - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/user/advanced_examples/FullyConnectedNeuralNetwork.ipynb b/docs/user/advanced_examples/FullyConnectedNeuralNetwork.ipynb deleted file mode 100644 index c4840cf3e..000000000 --- a/docs/user/advanced_examples/FullyConnectedNeuralNetwork.ipynb +++ /dev/null @@ -1,421 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Fully Connected Neural Network\n", - "\n", - "In this example, we show how one can train a neural network on a specific task (here, Iris Classification) and use Concrete Numpy to make the model work in FHE settings." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import torch\n", - "from torch import nn\n", - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define our neural network" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class FCIris(torch.nn.Module):\n", - " \"\"\"Neural network for Iris classification\n", - " \n", - " We define a fully connected network with three (3) fully connected (fc) layers that \n", - " perform feature extraction and one (fc) layer to produce the final classification. \n", - " We will use 3 neurons on all layers to ensure that the FHE accumulators\n", - " do not overflow (we are currently only allowed a maximum of 7 bits-width).\n", - " More information on this is available at https://docs.zama.ai/concrete-numpy/main/user/howto/reduce_needed_precision.html#limitations-for-fhe-friendly-neural-network.\n", - "\n", - " Due to accumulator limits, we have to design a network with only a few neurons on each layer. \n", - " This is in contrast to a traditional approach where the number of neurons increases after \n", - " each layer or block.\n", - " \"\"\"\n", - "\n", - " def __init__(self, input_size):\n", - " super().__init__()\n", - "\n", - " # The first layer processes the input data, in our case 4 dimensional vectors \n", - " self.linear1 = nn.Linear(input_size, 3)\n", - " self.sigmoid1 = nn.Sigmoid()\n", - " # Next, we add a one intermediate layer\n", - " self.linear2 = nn.Linear(3, 3)\n", - " self.sigmoid2 = nn.Sigmoid()\n", - " # Finally, we add the decision layer for 3 output classes encoded as one-hot vectors\n", - " self.decision = nn.Linear(3, 3)\n", - "\n", - " def forward(self, x):\n", - "\n", - " x = self.linear1(x)\n", - " x = self.sigmoid1(x)\n", - " x = self.linear2(x)\n", - " x = self.sigmoid2(x)\n", - " x = self.decision(x)\n", - "\n", - " return x\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define all required variables to train the model" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Get iris dataset\n", - "from sklearn.datasets import load_iris\n", - "X, y = load_iris(return_X_y=True)\n", - "\n", - "# Split into train and test\n", - "from sklearn.model_selection import train_test_split\n", - "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)\n", - "\n", - "# Convert to tensors\n", - "X_train = torch.tensor(X_train).float()\n", - "X_test = torch.tensor(X_test).float()\n", - "y_train = torch.tensor(y_train)\n", - "y_test = torch.tensor(y_test)\n", - "\n", - "# Initialize our model\n", - "model = FCIris(X.shape[1])\n", - "\n", - "# Define our loss function\n", - "criterion = nn.CrossEntropyLoss()\n", - "\n", - "# Define our optimizer\n", - "optimizer = torch.optim.SGD(model.parameters(), lr=0.1)\n", - "\n", - "# Define the number of iterations\n", - "n_iters = 50001\n", - "\n", - "# Define the batch size\n", - "batch_size = 16" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Train the model" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def train():\n", - " for iter in range(n_iters):\n", - " # Get a random batch of training data\n", - " idx = torch.randperm(X_train.size()[0])\n", - " X_batch = X_train[idx][:batch_size]\n", - " y_batch = y_train[idx][:batch_size]\n", - " \n", - " # Forward pass\n", - " y_pred = model(X_batch)\n", - " \n", - " # Compute loss\n", - " loss = criterion(y_pred, y_batch)\n", - " \n", - " # Backward pass\n", - " optimizer.zero_grad()\n", - " loss.backward()\n", - " \n", - " # Update weights\n", - " optimizer.step()\n", - " \n", - " \n", - " if iter % 1000 == 0:\n", - " # Print epoch number, loss and accuracy\n", - " accuracy = torch.sum(torch.argmax(y_pred, dim=1) == y_batch).item() / y_batch.size()[0]\n", - " print(f'Iterations: {iter:02} | Loss: {loss.item():.4f} | Accuracy: {100*accuracy:.2f}%')\n", - " if accuracy == 1:\n", - " break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Compile the model\n", - "\n", - "The `compile_torch_model` applies first a quantization to `model` with `n_bits` of precision using `X_train` as the calibration dataset and compile the model to its FHE counterparts. Here we use 3 bits of precision. In some edge cases, the network accumulators can overflow (i.e. extreme quantized values in both input and weights which is unlikely). In such a case, we need to retrain the model." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Training a FHE friendly quantized network.\n", - "Iterations: 00 | Loss: 1.2000 | Accuracy: 18.75%\n", - "Iterations: 1000 | Loss: 0.5623 | Accuracy: 75.00%\n", - "Iterations: 2000 | Loss: 0.3556 | Accuracy: 87.50%\n", - "Iterations: 3000 | Loss: 0.0646 | Accuracy: 100.00%\n", - "Compiling the model to FHE.\n", - "The network is trained and FHE friendly.\n" - ] - } - ], - "source": [ - "from concrete.torch.compile import compile_torch_model\n", - "print(\"Training a FHE friendly quantized network.\")\n", - "for trial in range(10):\n", - " try:\n", - " train()\n", - " print(\"Compiling the model to FHE.\")\n", - " quantized_compiled_module = compile_torch_model(\n", - " model,\n", - " X_train,\n", - " n_bits=3,\n", - " )\n", - " print(\"The network is trained and FHE friendly.\")\n", - " break\n", - " except Exception as e:\n", - " if str(e).startswith(\"max_bit_width of some nodes is too high\"):\n", - " print(f'The network is not fully FHE friendly, retrain.')\n", - " train()\n", - " else:\n", - " raise e\n", - " break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Predict with the torch model in clear" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "y_pred = model(X_test)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Predict with the quantized model" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# We now have a module in full numpy.\n", - "# Convert data to a numpy array.\n", - "X_train_numpy = X_train.numpy()\n", - "X_test_numpy = X_test.numpy()\n", - "y_train_numpy = y_train.numpy()\n", - "y_test_numpy = y_test.numpy()\n", - "\n", - "quant_model_predictions = quantized_compiled_module(X_test_numpy)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Predict in FHE" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 38/38 [03:03<00:00, 4.84s/it]\n" - ] - } - ], - "source": [ - "from tqdm import tqdm\n", - "homomorphic_quant_predictions = []\n", - "for x_q in tqdm(X_test_numpy):\n", - " homomorphic_quant_predictions.append(\n", - " quantized_compiled_module.forward_fhe.run(np.array([x_q]).astype(np.uint8))\n", - " )\n", - "homomorphic_predictions = quantized_compiled_module.dequantize_output(\n", - " np.array(homomorphic_quant_predictions, dtype=np.float32).reshape(quant_model_predictions.shape)\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Print the accuracy of both models" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Test Accuracy: 94.74%\n", - "Test Accuracy Quantized Inference: 89.47%\n", - "Test Accuracy Homomorphic Inference: 89.47%\n" - ] - } - ], - "source": [ - "print(f'Test Accuracy: {100*(y_pred.argmax(1) == y_test).float().mean():.2f}%')\n", - "print(f'Test Accuracy Quantized Inference: {100*(quant_model_predictions.argmax(1) == y_test_numpy).mean():.2f}%')\n", - "print(f'Test Accuracy Homomorphic Inference: {100*(homomorphic_predictions.argmax(1) == y_test_numpy).mean():.2f}%') " - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from sklearn.decomposition import PCA\n", - "pca = PCA(n_components=2)\n", - "X_train_2d = pca.fit_transform(X_train_numpy)\n", - "\n", - "b_min = np.min(X_train_2d, axis=0)\n", - "b_max = np.max(X_train_2d, axis=0)\n", - "\n", - "grid_dims = tuple([np.linspace(b_min[i], b_max[i], 128) for i in range(X_train_2d.shape[1])])\n", - "ndgrid_tuple = np.meshgrid(*grid_dims)\n", - "grid_2d = np.vstack([g.ravel() for g in ndgrid_tuple]).transpose()\n", - "\n", - "grid_test = pca.inverse_transform(grid_2d)\n", - "\n", - "grid_pred_all = quantized_compiled_module(grid_test)\n", - "grid_pred_all_original = model(torch.tensor(grid_test).float()).detach().numpy()\n", - "\n", - "pred_classes = np.argmax(grid_pred_all, axis=1).astype(np.int32)\n", - "pred_classes_original = np.argmax(grid_pred_all_original, axis=1).astype(np.int32)\n", - "\n", - "from matplotlib import pyplot as plt\n", - "\n", - "cmap = 'autumn'\n", - "# Create two subplots and set their locations\n", - "plt.clf()\n", - "fig, axs = plt.subplots(1, 2, figsize=(12, 6))\n", - "\n", - "# Plot original model contour plot\n", - "axs[0].contourf(ndgrid_tuple[0], ndgrid_tuple[1], pred_classes_original.reshape(ndgrid_tuple[0].shape), cmap=cmap)\n", - "\n", - "# Plot the scatter with marker borders\n", - "axs[0].scatter(X_train_2d[:, 0], X_train_2d[:, 1], c=y_train_numpy, s=50, edgecolors='k', cmap=cmap)\n", - "\n", - "# Add title and axis labels\n", - "axs[0].set_title('Original Inference')\n", - "\n", - "\n", - "\n", - "\n", - "# Plot quantized model contour plot\n", - "axs[1].contourf(ndgrid_tuple[0], ndgrid_tuple[1], pred_classes.reshape(ndgrid_tuple[0].shape), cmap=cmap)\n", - "\n", - "# Plot the scatter with marker borders\n", - "axs[1].scatter(X_train_2d[:, 0], X_train_2d[:, 1], c=y_train_numpy, s=50, edgecolors='k', cmap=cmap)\n", - "\n", - "# Add title and axis labels\n", - "axs[1].set_title('Quantized Inference')\n", - "\n", - "\n", - "\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the above plot, we show the decision boundaries for both the original and quantized model. The quantized model has it's decision boundaries (colored regions) slightly shifted compared to the original model. This is due to the low bit quantization applied to the model in post training.\n", - "\n", - "Here we do not compute the contour plot for the FHE inference as this would be really costly but it should be pretty close to the quantized model. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Summary\n", - "\n", - "In this notebook, we presented a few steps to have a model (torch neural network) inference in over homomorphically encrypted data: \n", - "- We first trained a fully connected neural network yielding ~95% accuracy\n", - "- Then, we quantized it using Concrete Numpy. As we can see, the extreme post training quantization (only 3 bits of precision for weights, inputs and activations) made the neural network accuracy slightly drop (~89%).\n", - "- We then used the compiled inference into its FHE equivalent to get our FHE predictions over the test set\n", - "\n", - "The Homomorphic inference achieves a similar accuracy as the quantized model inference.\n", - "\n", - "Disclaimer: post training quantization with such a low bit width (<=3) can yield different results for the quantized model which will mainly depends on the range of the learned weights." - ] - } - ], - "metadata": { - "execution": { - "timeout": 10800 - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/user/advanced_examples/LinearRegression.ipynb b/docs/user/advanced_examples/LinearRegression.ipynb deleted file mode 100644 index 7cc740521..000000000 --- a/docs/user/advanced_examples/LinearRegression.ipynb +++ /dev/null @@ -1,483 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b760a0f6", - "metadata": {}, - "source": [ - "# Linear Regression\n", - "\n", - "Currently, **Concrete** only supports unsigned integers up to 7-bits. Nevertheless, we want to evaluate a linear regression model with it. Luckily, we can make use of **quantization** to overcome this limitation." - ] - }, - { - "cell_type": "markdown", - "id": "253288cf", - "metadata": {}, - "source": [ - "### Let's start by importing some libraries to develop our linear regression model." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "6200ab62", - "metadata": {}, - "outputs": [], - "source": [ - "from copy import deepcopy\n", - "from typing import Any, Dict\n", - "\n", - "import numpy as np\n", - "from matplotlib import pyplot as plt\n", - "from sklearn.datasets import make_regression\n", - "from sklearn.linear_model import LinearRegression\n", - "from sklearn.metrics import r2_score\n", - "from sklearn.model_selection import train_test_split\n", - "from tqdm import tqdm\n" - ] - }, - { - "cell_type": "markdown", - "id": "c8160548", - "metadata": {}, - "source": [ - "\n", - "\n", - "### Now, import Concrete quantization tools. " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "9dc823e0", - "metadata": {}, - "outputs": [], - "source": [ - "from concrete.quantization import QuantizedArray, QuantizedLinear, QuantizedModule" - ] - }, - { - "cell_type": "markdown", - "id": "f43e2387", - "metadata": {}, - "source": [ - "### And some helpers for visualization." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "d104c8df", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import display" - ] - }, - { - "cell_type": "markdown", - "id": "4a5ae7af", - "metadata": {}, - "source": [ - "### And, finally, the FHE compiler." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "05cda814", - "metadata": {}, - "outputs": [], - "source": [ - "import concrete.numpy as hnp" - ] - }, - { - "cell_type": "markdown", - "id": "53e676b8", - "metadata": {}, - "source": [ - "### Let's define our Quantized Linear Regression module that quantizes a sklearn linear regression." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "d451e829", - "metadata": {}, - "outputs": [], - "source": [ - "class QuantizedLinearRegression(QuantizedModule):\n", - " \"\"\"\n", - " Quantized Generalized Linear Model\n", - " Building on top of QuantizedModule, this class will chain together a linear transformation\n", - " and an inverse-link function\n", - " \"\"\"\n", - "\n", - " @staticmethod\n", - " def from_sklearn(sklearn_model, calibration_data):\n", - " \"\"\"Create a Quantized Linear Regression initialized from a sklearn trained model\"\"\"\n", - " weights = np.expand_dims(sklearn_model.coef_, 1)\n", - " bias = sklearn_model.intercept_\n", - " #Quantize with 6 bits for input data, 1 for weights, 1 for the bias and 6 for the output\n", - " return QuantizedLinearRegression(6, 1, 1, 6, weights, bias, calibration_data)\n", - "\n", - " def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None:\n", - " \"\"\"\n", - " Create the Linear regression with different quantization bit precitions:\n", - "\n", - " Quantization Parameters - Number of bits:\n", - " q_bits (int): bits for input data, insuring that the number of bits of \n", - " the w . x + b operation does not exceed 7 for the calibration data\n", - " w_bits (int): bits for weights: in the case of a univariate regression this \n", - " can be 1 \n", - " b_bits (int): bits for bias (this is a single value so a single bit is enough)\n", - " out_bits (int): bits for the result of the linear transformation (w.x + b). \n", - " In our case since the result of the linear transformation is \n", - " directly decripted we can use the maximum of 7 bits\n", - "\n", - " Other parameters:\n", - " weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality\n", - " bias: a numpy scalar\n", - " calibration_data: a numpy nd-array of data (Nxd)\n", - " \"\"\"\n", - " self.n_bits = out_bits\n", - "\n", - " # We need to calibrate to a sufficiently low number of bits\n", - " # so that the output of the Linear layer (w . x + b)\n", - " # does not exceed 7 bits\n", - " self.q_calibration_data = QuantizedArray(q_bits, calibration_data)\n", - "\n", - " # Quantize the weights and create the quantized linear layer\n", - " q_weights = QuantizedArray(w_bits, weights)\n", - " q_bias = QuantizedArray(b_bits, bias)\n", - " q_layer = QuantizedLinear(out_bits, q_weights, q_bias)\n", - "\n", - " # Store quantized layers\n", - " quant_layers_dict: Dict[str, Any] = {}\n", - "\n", - " # Calibrate the linear layer and obtain calibration_data for the next layers\n", - " calibration_data = self._calibrate_and_store_layers_activation(\n", - " \"linear\", q_layer, calibration_data, quant_layers_dict\n", - " )\n", - "\n", - " # Finally construct our Module using the quantized layers\n", - " super().__init__(quant_layers_dict)\n", - "\n", - " def _calibrate_and_store_layers_activation(\n", - " self, name, q_function, calibration_data, quant_layers_dict\n", - " ):\n", - " \"\"\"\n", - " This function calibrates a layer of a quantized module (e.g. linear, inverse-link,\n", - " activation, etc) by looking at the input data, then computes the output of the quantized\n", - " version of the layer to be used as input to the following layers\n", - " \"\"\"\n", - "\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 quantize_input(self, x):\n", - " \"\"\"Quantize an input set with the quantization parameters determined from calibration\"\"\"\n", - " q_input_arr = deepcopy(self.q_calibration_data)\n", - " q_input_arr.update_values(x)\n", - " return q_input_arr" - ] - }, - { - "cell_type": "markdown", - "id": "7945595f", - "metadata": {}, - "source": [ - "### Create a synthetic dataset" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "410b90de", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "X, y = make_regression(\n", - " n_samples=200, n_features=1, n_targets=1, bias=5.0, noise=30.0, random_state=42\n", - ")\n", - "\n", - "# Split it into train/test and sort the sets for nicer visualization\n", - "x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42)\n", - "\n", - "sidx = np.argsort(np.squeeze(x_train))\n", - "x_train = x_train[sidx, :]\n", - "y_train = y_train[sidx]\n", - "\n", - "sidx = np.argsort(np.squeeze(x_test))\n", - "x_test = x_test[sidx, :]\n", - "y_test = y_test[sidx]\n" - ] - }, - { - "cell_type": "markdown", - "id": "75f4fdb7", - "metadata": {}, - "source": [ - "### Train a linear regression on the training set and visualize predictions on the test set." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "2a124a62", - "metadata": {}, - "outputs": [], - "source": [ - "linreg = LinearRegression()\n", - "linreg.fit(x_train, y_train)\n", - "\n", - "y_pred = linreg.predict(x_test)" - ] - }, - { - "cell_type": "markdown", - "id": "a0ba5509", - "metadata": {}, - "source": [ - "### Visualize the regression line and the data set." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "edcd361b", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAs4AAAHSCAYAAAD8EE1RAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAABx/0lEQVR4nO3deXjU5bnG8e9AAhgRwuYGVkAgshi2AKKoqCyiFhUXoFDXigtIiCvBhZFiQq0aYsEq1rpUq9ZWxaUgRUtRETGCRgQRE2yBg4hIRIxAIL/zx8svmZnMvk9yf64rV5j9ncSec8+b530eh2VZFiIiIiIi4lejRC9ARERERCQVKDiLiIiIiARBwVlEREREJAgKziIiIiIiQVBwFhEREREJgoKziIiIiEgQ0hK9gGC0bduWjh07JnoZIiIiIlLPff3113z33Xdeb0uJ4NyxY0dKSkoSvQwRERERqedycnJ83qZSDRERERGRICg4i4iIiIgEQcFZRERERCQIKVHj7E1VVRVbtmxh7969iV6KJFizZs3o0KED6enpiV6KiIiI1GMpG5y3bNnCEUccQceOHXE4HIlejiSIZVns3LmTLVu20KlTp0QvR0REROqxlC3V2Lt3L23atFFobuAcDgdt2rTRXx5EREQk5lI2OAMKzQLovwMRERGJj5QOzol233330bNnT7Kzs+nTpw8ffvghYPpOe2uc3bx587iu76mnnqJdu3b06dOHE088kaKiori+fiCnnHJKopcgIiIiErSUrXFOtA8++IA33niD1atX07RpU7777jv2798fl9c+ePAgjRs3Duq+Y8eOZd68eezcuZOsrCwuueQSjjvuuIhe/8CBA6SlRf6fzooVKyJ+DhEREZF40Y5zmLZt20bbtm1p2rQpYMaCH3vssW73+fnnnxk1ahSPP/54ncf//ve/Z8CAAWRnZzNz5sya6y+88EL69+9Pz549WbBgQc31zZs355ZbbqF379588MEHNG/enDvvvJPevXtz8skns337dr/rbdOmDV26dGHbtm0APPvsswwcOJA+ffpw3XXXcfDgQQCeeOIJunXrxsCBA7n22muZMmUKAFdeeSXXX389gwYN4vbbb6esrIxzzjmH/v37c9ppp/HFF18A8NJLL9GrVy969+7N6aefDsDnn39e81rZ2dls3Lix5j2BOeB322230atXL0466SRefPFFAJYtW8bQoUO55JJLOPHEE5kwYQKWZQXz6xERERGJunqz4zx0aN3rLrsMbrwRKivh3HPr3n7llebru+/gkkvcb1u2zP/rjRgxglmzZtGtWzeGDRvG2LFjOeOMM2pu37NnD+PGjePyyy/n8ssvd3vskiVL2LhxI6tWrcKyLEaPHs3y5cs5/fTT+fOf/0zr1q35+eefGTBgABdffDFt2rThp59+YtCgQTz44IMA/PTTT5x88sncd9993H777Tz++OPcddddPtf7v//9j71795Kdnc369et58cUXef/990lPT+fGG2/kueeeY9iwYfz2t79l9erVHHHEEZx11ln07t275jm2bNnCihUraNy4MWeffTaPPvooXbt25cMPP+TGG2/knXfeYdasWbz11lu0b9+eiooKAB599FFyc3OZMGEC+/fvrwnptpdffplPPvmETz/9lO+++44BAwbUhO41a9bw+eefc+yxx3Lqqafy/vvvM2TIEP+/HBEREZEY0I5zmJo3b87HH3/MggULaNeuHWPHjuWpp56quf2CCy7gqquuqhOawQTnJUuW0LdvX/r168cXX3xRswv78MMP1+wib968ueb6xo0bc/HFF9c8R5MmTTj//PMB6N+/P19//bXXdb744otkZ2fTpUsXbrzxRpo1a8bbb7/Nxx9/zIABA+jTpw9vv/025eXlrFq1ijPOOIPWrVuTnp7OpZde6vZcl156KY0bN2bPnj2sWLGCSy+9tGbH2t7JPvXUU7nyyit5/PHHawLy4MGDKSgo4He/+x3//e9/Oeyww9ye97333mP8+PE0btyYo446ijPOOIOPPvoIgIEDB9KhQwcaNWpEnz59fL5PERERkVirNzvO/naIMzL83962beAdZm8aN27M0KFDGTp0KCeddBJPP/00V155JWAC5OLFi/nVr35Vp+uDZVnk5+dz3XXXebyHZSxdupQPPviAjIwMhg4dWtNmrVmzZm51zenp6TXP27hxYw4cOOB1jXaNc0lJCSNGjGD06NFYlsUVV1xBYWGh231fffVVv+/38MMPB6C6uprMzEw++eSTOvd59NFH+fDDD3nzzTfp378/H3/8Mb/61a8YNGgQb775Jueeey6PPfYYZ511lt/XstmlMIHep4iIiEisacc5TBs2bKjZDQb45JNPOP7442suz5o1i1atWjF58uQ6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nWDxYhg50uMOvnb+IWl2/hWcRURERCQmLAu+/BKysiAnB554AsaN890qONl3/htUjbOIiIiIxMfXX8N550HfvvDf/5rse/XVfkKzLYl3/hWcw7Rz50769OlDnz59OProo2nfvn3N5f379wd8/LJly1ixYkVQr9WxY0e+++47v/cpKCgI6rlEREREYunAAVO73LMnLF9uWgJ36JDoVUVHwynVcN3293Y5RG3atOGTTz4BzBCS5s2bc+uttwb9+GXLltG8eXNOOeWUsNfgqqCggBkzZkTluURERETCsW8fDBkCJSVw/vkwfz784heJXlX0NIwd51KneyuTGI1v/PjjjznjjDPo378/I0eOZNu2bQA8/PDD9OjRg+zsbMaNG8fXX3/No48+SlFRUc1kP1c7d+5kxIgR9OzZk9/85je4zqi58MIL6d+/Pz179mTBggUATJ8+nZ9//pk+ffowYcIEn/cTERERiQV7QHLTpnDuufDSS/Daa/UrNENDmBzor7VJlCbROJ1ODj/8cF555RUWLlxIu3btePHFF3nrrbf485//zLHHHsumTZto2rQpFRUVZGZm+t2lnjp1Km3btuWee+7hzTff5Pzzz2fHjh20bduW77//ntatW/Pzzz8zYMAA/vOf/9CmTRuaN2/Onj17ap7D1/3qK00OFBERSYxFi2DKFHj2WRg8ONGriZy/yYH1v1TDtZXJhuLaXoBRHt+4b98+1q5dy/DhwwE4ePAgxxxzDADZ2dlMmDCBCy+8kAsvvDDgcy1fvpyXX34ZgPPOO49WrVrV3Pbwww/zyiuvALB582Y2btzoNRAHez8RERGRcGzfDtOmwQsvQPfu0LhxolcUe/U/OENteHadPx/l1iaWZdGzZ08++OCDOre9+eabLF++nNdff5377ruPzz77LKzXWLZsGUuXLuWDDz4gIyODoUOHsnfv3rDvJyIiIhKOZ56B3FyorIRZs+D2202ZRn3XMGqc4zC+sWnTpuzYsaMmOFdVVfH5559TXV3N5s2bOfPMM/nd737HDz/8wJ49ezjiiCP48ccfvT7X6aefzl//+lcAFi1axK5duwD44YcfaNWqFRkZGXzxxResXLmy5jHp6elUVVUFvJ+IiIhIpLZvNyOzS0vh7rsbRmiGhhCcPWucx1eb7xuKoxqeGzVqxN///nfuuOMOevfuTZ8+fVixYgUH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dnQiIlKvde5sQqx7GUWtjAyH++G2CNue5eebA3WhhGYwlxO18zxnjgn7559vBppcf73v0AzBh+ZUPRApEg4FZxERqRc8yyhssdrl9XcQMFlau+3eDV9+af49dSq8/jq89BIce2x0nj8Z67dFYknBWURE6o1Qa5BjIfDud3xau736KvToAZdeaoaYtGxpdpujJRnrt0ViTcFZRETqlWQ43Bbv3W9XW7fCmDGmtVybNrBggf+SjHDU7V6i8CwNg/o4i4hIvZOfD2PHJvZwmx2O7YAZj9C8ZrXFGUMdVFWZmuab8yzSm0R3jHWg+m1QJw6pv7TjLCIi9VIydISI1+73zz8DpU56Vd3CrydarF0Ld9xukf5ZHpQ6o/Y6yVS/LZIICs4iIiIxFKgDRyT27jWBPCvLYtfOfaSXFTH/mjxO6GyZAS8biqGqIqRBL/4kS/22SKJEJThfffXVHHnkkfTq1avmuu+//57hw4fTtWtXhg8fzq5duwCwLIupU6fSpUsXsrOzWb16dTSWICIikrRiEST//W/Izob77oOzznJA74LaqYjPN6qdlhjCaPFgJLJ+WyTRohKcr7zyShYvXux23Zw5czj77LPZuHEjZ599NnPmzAFg0aJFbNy4kY0bN7JgwQJuuOGGaCxBRESkQdi3D666Cs46y2wkL10KTz0FrVofmoroKsqh2ZYM3UtEEiEqwfn000+ndevWbtctXLiQK664AoArrriCV199teb6yy+/HIfDwcknn0xFRQXbtm2LxjJERERiw7PUIYFDd5s0gYoKU0tcWgpnn+2yptV57ndenReztSZD9xKReItZjfP27ds55phjADj66KPZvn07AFu3buW4446ruV+HDh3YunVrrJYhIiISmdKZ7gG0utpcLnXGbQnl5XDBBbBpk9lAfvllU6Jx2GGH7mC51DRn5cL46tqyjRiH51jVb4sko7i0o3M4HDhC/FPRggULWLBgAQA7duyIxbJERET8+3Qm/N9rsOsTc7nvQ/BWf3M5K9cE0hiUQtiqqqCoCJxOSEuDtWuhUycvL+lwQHqme02zXbaRnhnTNeogoDQkMQvORx11FNu2beOYY45h27ZtHHnkkQC0b9+ezZs319xvy5YttG/fvs7jJ02axKRJkwDIycmJ1TJFRES8syw48IMJya36mN3bDcXmtlZ9TIiOYSBdtQquvdaU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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.ioff()\n", - "\n", - "plt.clf()\n", - "fig, ax = plt.subplots(1, figsize=(12,8))\n", - "fig.patch.set_facecolor(\"white\")\n", - "ax.scatter(x_train, y_train, c=\"blue\", marker=\"D\", label=\"Train data\")\n", - "ax.scatter(x_test, y_test, c=\"orange\", marker=\"x\", label=\"Test data\")\n", - "ax.plot(x_test, y_pred, c=\"blue\", marker=None, linestyle=\"dashed\", label=\"Sklearn Regression\")\n", - "ax.legend()\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "996fbe05", - "metadata": {}, - "source": [ - "### Calibrate the model for quantization using both training and test data\n" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "06ed91dd", - "metadata": {}, - "outputs": [], - "source": [ - "calib_data = X \n", - "q_linreg = QuantizedLinearRegression.from_sklearn(linreg, calib_data)" - ] - }, - { - "cell_type": "markdown", - "id": "cd74c5e7", - "metadata": {}, - "source": [ - "### Now, we can compile our model to FHE, taking as the possible input set all of our dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "b8f8f95b", - "metadata": {}, - "outputs": [], - "source": [ - "X_q = q_linreg.quantize_input(X)\n", - "\n", - "engine = q_linreg.compile(X_q)" - ] - }, - { - "cell_type": "markdown", - "id": "084fb296", - "metadata": {}, - "source": [ - "### Time to make some predictions, first in the clear." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "e781279a", - "metadata": {}, - "outputs": [], - "source": [ - "# Now that the model is quantized, predict on the test set\n", - "x_test_q = q_linreg.quantize_input(x_test)\n", - "q_y_pred = q_linreg.forward_and_dequant(x_test_q)" - ] - }, - { - "cell_type": "markdown", - "id": "f28155cf", - "metadata": {}, - "source": [ - "### Now let's predict using the quantized FHE classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "2b6da1f6", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 80/80 [00:14<00:00, 5.57it/s]\n" - ] - } - ], - "source": [ - "# Now predict using the FHE quantized model on the testing set\n", - "y_test_pred_fhe = np.zeros_like(x_test)\n", - "\n", - "for i, x_i in enumerate(tqdm(x_test_q.qvalues)):\n", - " q_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8)\n", - " # bench: Measure: Evaluation Time (ms)\n", - " q_pred_fhe = engine.run(q_sample)\n", - " y_test_pred_fhe[i] = q_linreg.dequantize_output(q_pred_fhe)\n", - " # bench: Measure: End\n" - ] - }, - { - "cell_type": "markdown", - "id": "23852861", - "metadata": {}, - "source": [ - "### Evaluate all versions of the classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "7b0f541f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sklearn R^2: 0.8758\n", - "Non Homomorphic R^2: 0.8735\n", - "Homomorphic R^2: 0.8735\n", - "Relative Difference Percentage: 0.00%\n" - ] - } - ], - "source": [ - "# Measure the error for the three versions of the classifier\n", - "sklearn_r2 = r2_score(y_pred, y_test)\n", - "non_homomorphic_test_error = r2_score(q_y_pred, y_test)\n", - "homomorphic_test_error = r2_score(y_test_pred_fhe, y_test)\n", - "\n", - "# Measure the error of the FHE quantized model w.r.t the clear quantized model\n", - "difference = (\n", - " abs(homomorphic_test_error - non_homomorphic_test_error) * 100 / non_homomorphic_test_error\n", - ")\n", - "\n", - "\n", - "print(f\"Sklearn R^2: {sklearn_r2:.4f}\")\n", - "print(f\"Non Homomorphic R^2: {non_homomorphic_test_error:.4f}\")\n", - "print(f\"Homomorphic R^2: {homomorphic_test_error:.4f}\")\n", - "print(f\"Relative Difference Percentage: {difference:.2f}%\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "704b2f63", - "metadata": {}, - "source": [ - "### Plot the results of both the original and FHE versions of the classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "aae3f6da", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAs4AAAHSCAYAAAD8EE1RAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAACPJElEQVR4nO3dd3iTZfvG8W86GGXLUAQVWWWWVYbiQpY4cP4Y4utABQGlVEUpolQEioIWFBRxIQ5wiwtEVEREZVugCNiCAoKyyqpAx/P74yZtmiZt2iZN2p6f4+hRkjxJ7if1Pd6zd6/numyWZVmIiIiIiEiegvy9ABERERGRkkDBWURERETEAwrOIiIiIiIeUHAWEREREfGAgrOIiIiIiAcUnEVEREREPBDi7wV4olatWjRo0MDfyxARERGRUm7nzp0cOHDA5WMlIjg3aNCANWvW+HsZIiIiIlLKRUZGun1MpRoiIiIiIh5QcBYRERER8YCCs4iIiIiIB0pEjbMraWlp7N69m5MnT/p7KSI+UaFCBerXr09oaKi/lyIiIiKU4OC8e/duqlSpQoMGDbDZbP5ejohXWZbFwYMH2b17NxdeeKG/lyMiIiKU4FKNkydPUrNmTYVmKZVsNhs1a9bUX1REREQCSIkNzoBCs5Rq+u9bREQksJTo4FwYycneeZ2DBw/Stm1b2rZtyznnnEO9evWybp8+fTrP565Zs4aRI0cW6P0aNGhA69atad26NS1atGDcuHH57kampKTw4osvFuh9RERERMS1MhWc4+KgUSPzvahq1qzJhg0b2LBhA/fddx/R0dFZt8uVK0d6errb50ZGRvL8888X+D2///57Nm7cyKpVq0hOTmbo0KF5Hq/gLCIiIuI9ZSY4x8XBxInm3xMneic8O7vzzju577776Ny5M4888girVq3ioosuol27dlx88cVs3boVgGXLlnHttdcCEBsby+DBg7niiito2LChR4G6cuXKzJ49m08//ZRDhw5x/PhxunfvTvv27WndujULFy4EYMyYMSQlJdG2bVtGjx7t9jgRERERyV+J7apREPbQnJpqbqemZofomBjvvtfu3btZuXIlwcHBHD16lB9//JGQkBCWLl3K2LFj+eijj3I95/fff+f777/n2LFjhIeHM2zYsHxbkFWtWpULL7yQ7du306FDBz755BOqVq3KgQMH6NKl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FhERERGfsCzYtg3CwyEyEl57DW49azEVou+HpCRz0P/+B1OnwtlnZz8pQHf+S387OhEREREpdjt3wjXXQLt28OefYNu9i8Ff3kyFG/uY0NyiBSxbBvPmZYdmCOidfwXnIti3bx8DBgygUaNGdOjQgauvvppt27axc+dOWrVq5ZP3vOKKKwgPD6dNmzZ07NiRDRs2eP097rzzTi688ELatm1LmzZt+Pbbb3MdExcXR2hoKG+99VaO+9955x0iIiJo3bo1F198Mb/99pvH73vo0CF69uxJkyZN6NmzJ4cPH3Z53COPPELLli1p3rw5I0eOxLIsjh07Rtu2bbO+atWqxahRowCYO3cutWvXznrs1VdfzfO1REREpPDS0017uZYtYflyePqp05w3/xlo3hw+/hgqVTI7zBs2wOWX+3u5BaLgXEiWZXHjjTdyxRVXkJSUxNq1a4mLi+Off/7x6ntkZmbmuv+dd97ht99+Y/jw4YwePdpr7+do6tSpbNiwgenTp3PffffleOytt95i8eLFbNmyhWeffZalS5dmPXbhhRfyww8/sHHjRh5//HGGDBni8XtOmTKF7t27s337drp3786UKVNyHbNy5Up++uknEhIS2LRpE6tXr+aHH36gSpUqWePON2zYwAUXXMBNN92U9bz+/ftnPXbPPffk+VoiIiJSOKdOwUUXwUMPmTZzSa8t44HX2hIU8yicOAG33AK//w4PPwyhof5eboEpOBfS999/T2hoaI5Q2aZNGy699NIcx2VkZDB69Gg6duxIREQEL7/8MgDHjx+ne/futG/fntatW7Nw4UIAdu7cSXh4OLfffjutWrViV45h7TlddNFF7NmzB4ATJ04wePBgOnXqRLt27bJeLzU1lX79+tGiRQtuvPFGOnfu7Lapd37vAbB06VLmzZvHV199RePGjVmyZAnjx4/P2lm++OKLqVGjBgBdunRh9+7dHr/XwoULueOOOwC44447+PTTT3MdY7PZOHnyJKdPn+bUqVOkpaVxtuOfd4Bt27bx77//5vpZFOa1REREJH/p6eZ7+fJw9dXw2Zx9fFbtNs4e0M00bG7cGBYvhg8+gPr1/bvYIig1FwdecUXu+/r1g+HDITXV/BCd3Xmn+TpwwPwC5GjZsrzfb9OmTXTo0CHfdb322mtUq1aN1atXc+rUKbp27UqvXr0477zz+OSTT6hatSoHDhygS5cu9O3bF4Dt27fz5ptv0qVLlzxfe/Hixdxwww0ATJo0iSuvvJLXX3+dlJQUOnXqRI8ePXjppZeoUaMGiYmJbNq0ibZt2+a7ZnfvAdCjRw969OiRdbtOnTr89NNPbs+9T58+WbcvvfRSjh07luu4adOm0aNHD/755x/q1q0LwDnnnONy9/6iiy6iW7du1K1bF8uyuP/++3NN1luwYAH9+/fH5lAT9dFHH7F8+XKaNm1KfHw85513nkevJSIiInlbtAjuvx/efhsu6pjOk7VegofHwdGjUKGCGQk4erT5dwlXaoJzoFqyZAkJCQl8+OGHABw5coTt27dTv359xo4dy/LlywkKCmLPnj1ZQfGCCy7IMzQPGjSI06dPc/z48awa5yVLlvDZZ58xbdo0AE6ePMlff/3FihUriIqKAqBVq1ZERER4tO7Ro0czduxYdu/ezc8//1zg8/7+++957bXXWLFiRdZ9P/74o8fPt9lsOYKv3R9//MGWLVuydrJ79uzJjz/+mGN3ecGCBTlqr6+77joGDhxI+fLlefnll7njjjv47rvvPHotERERce2ff2DUKFiwwJQvV038BYYPM7XLYK4MfP55aNjQn8v0qlITnPPaIQ4Ly/vxWrXy32F21rJly6wwnBfLsnjhhRfo3bt3jvvnzp3L/v37Wbt2LaGhoTRo0ICTJ08CUKlSpTxf85133qFDhw6MHj2aBx54gI8//hjLsvjoo48IDw8v2Im4MXXqVG655RZeeOEFBg8ezNq1az1+bkJCAvfccw+LFi2iZs2aWffnt+N89tlns3fvXurWrcvevXupU6dOrmM/+eQTunTpQuXKlQHo06cPP//8c1bY/e2330hPT8/x1wDHNdxzzz088sgjHr2WiIiIuDZvHkRFmb/qTx1zkOh/xhB8z5mL788/3wTmvn0DqiOGN6jGuZCuvPJKTp06xZw5c7LuS0hIyLWr2rt3b1566SXS0tIAU3974sQJjhw5Qp06dQgNDeX777/nzz//LND722w2nnrqKX755Rd+//13evfuzQsvvJDVFWL9+vUAdO3alffffx+AxMRENm7cmPUat99+O6tWrcrzfe6//34yMzP5+uuvPVrXX3/9xU033cRbb71F06ZNczz2448/5riAz/5lL/3o27cvb775JgBvvvkm119/fa7XP//88/nhhx9IT08nLS2NH374IUd5xfz58xk4cGCO5+zduzfr35999lnW8fm9loiIiLj2zz/QNiKTPx9/lYfnNCX4jVfNxX4xMZCYCNdfX+pCMyg4F5rNZuOTTz5h6dKlNGrUiJYtWxITE8M555yT47h77rmHFi1a0L59e1q1asXQoUNJT09n0KBBrFmzhtatWzNv3jyaNWtW4DVUrFiRhx56iKlTp/L444+TlpZGREQELVu25PHHHwdg+PDh7N+/nxYtWjBu3DhatmxJtWrVABP0zz333HzPc9y4cTzzzDMerWnChAkcPHiQ4cOH07ZtWyIjIz0+nzFjxvDNN9/QpEkTli5dypgxYwBYs2ZNVieMW265hUaNGtG6dWvatGlDmzZtuO6667Je4/33388VnJ9//nlatmxJmzZteP7555k7d65HryUiIiLGqVMwYQLY/9j+YLf1fHe6K+c8fi8cOmRaaCQkwOTJpt1cKWWzSkDj2sjIyFydILZs2aLdQQ9kZGSQlpZGhQoVSEpKokePHmzdupWTJ09y991388EHH/h7iZIH/XcuIiL+9uOPMGSI6SI3esgRnin/OMyaBZmZULeuadrcv3+p2WF2lTvtSk2Ns7iWmppKt27dSEtLw7IsXnzxRcqVK0e5cuUUmkVERMStw4dhzBiYMwcuON9i/UPv0Pbth02dRnCwuTLwySehalV/L7XYKDiXclWqVClQ32YRERERgO++g1dfhafvSOShpOEEP3tmSNjFF8OLL0KbNv5doB8oOIuIiIgIAH/9BWvXwo03wk29jrN/8FOcNfc5M+GkVi145hm44w4IKpuXySk4i4iIiJRxGRnwwgswbhyEVbS4+r+PKf/oKM7avdvULg8dai78O+ssfy/VrxScRURERMqwDRvg3nthzRq4+/I/eJ4HKD9osXmwQwd46SXo2NGvawwUCs4iIiIiZdTu3dCpE5xb4z823TyFFl88je3UKahe3ewwDxliLgQUQH2ci2T37t1cf/31NGnShIYNG3L//fdz6tQpr7/PsmXLWLlyZb7H7dy5k4oVK9K2bdusr9OnTzN37lzuv//+HMdeccUVWRcNNmjQgNatW2c9Z+TIkbleOzY2lnr16tG2bVtatGjB/PnzvXNyDubOnUvt2rVp27YtzZo1Iz4+Ptcxy5Yto3Llyll9ne02bNjARRddRMuWLYmIiOC9997z+H0ty2LkyJE0btyYiIgI1q1b5/K4+fPn07p1ayIiIrjqqqs4cOAAAP3798/67Bo0aEDbtm0BWLVqVdb9bdq04ZNPPsl6LcfPvCC9rkVERLzh99/N9/r1Ycmor0gKa0XLjyaY0HzHHbB1KwwbptDszCoBOnTokOu+xMREP6wkW2ZmptWxY0fr9ddftyzLstLT063BgwdbI0eO9Pp7jR8/3po6dWq+x+3YscNq2bJlrvvfeOMNa8SIETnuu/zyy63Vq1dblmVZF1xwgbV//36P17Bt2zarSpUq1unTpz09BY84rvPAgQNWzZo1rb/++ivr8Y0bN1rNmze3Nm7caPXr18+KjY3Nemzr1q3Wtm3bLMuyrD179ljnnHOOdfjwYY/e98svv7SuuuoqKzMz0/r555+tTp065TomLS3Nql27dtbnNHr0aGv8+PG5jnvwwQetJ5980rIsyzpx4oSVlpZmWZZl/f3331bt2rWzbnvymVuW//87FxGR0uWffyxr0CDLstksa92nf1rWDTdYFpivVq0sa/lyfy/R71zlTjvtOBfSd999R4UKFbjrrrsACA4OJj4+nnnz5nH8+PFcu7zXXnsty5YtA2DYsGFERkbSsmVLxo8fn3VMgwYNGD9+PO3bt6d169b8/vvv7Ny5k9mzZxMfH0/btm1zjfT2hyZNmhAWFsbhw4cBmDp1Kh07diQiIiLH+Tz11FOEh4dzySWXMHDgQKZNm+bxe9SsWZPGjRtnjcves2cPd999N59++imtWrXi3XffZevWrbz++usANG3alCZNmgBw7rnnUqdOHfbv3+/Rey1cuJDbb78dm81Gly5dSElJyTGmG8yutGVZnDhxAsuyOHr0aK6pi5Zl5ZhcGBYWRkiIqYY6efIktlLSGF5EREoey4I33oDmzeGT906z5MoptL21OXz6KVSuDNOmwbp1cOmluZ6bnFz86w1UpaPG2VeBJI+hips3b6ZDhw457qtatSoNGjTgjz/+yPNlJ02axFlnnUVGRgbdu3cnISGBiIgIAGrVqsW6det48cUXmTZtGq+++ir33XcflStX5uGHH853yUlJSVmlAl27dmXWrFkAvPfee6xYsSLrOOc1duvWjeAzf4654447iI6Odvse69ato0mTJtSpU4clS5awfft2Vq1ahWVZ9O3bl+XLl1OxYkU++ugjfvvtN9LS0mjfvn2uzysvf/31FydPnsz6XOrVq8evv/6a9XhwcDDvvvuuy+euWrWK06dP06hRIwCio6P5/vvvcx03YMAAxowZw549ezjvvPOy7q9fvz579uyhbt26WfeFhoby0ksv0bp1aypVqkSTJk2yPlu7H3/8kbPPPjsrwAP8+uuvDB48mD///JO33norK0jbbDZ69eqFzWZj6NChDBkyxOPPRkREpCAsC667Dr78Eh5o9T1Tqw+n/LdnajX69TOT/+rVc/ncuDgYO9aUO8fEFOOiA1TpCM4lzPvvv8+cOXNIT09n7969JCYmZgXEm266CYAOHTrw8ccfF/i1GzVqxIYNG3Ld379/f2bOnJl1+4orrsjx+Pfff0+tWrXyfO34+HjeeOMNtm3bxueffw7AkiVLWLJkCe3atQPg+PHjbN++nWPHjnH99ddToUIFKlSowHXXXefR+t977z2WL1/O77//zsyZM6lQoYJHz7Pbu3cv//vf/3jzzTcJOtNj0lWtdEGlpaXx0ksvsX79eho2bMgDDzxAXFwc48aNyzpm/vz5WbvNdp07d2bz5s1s2bKFO+64gz59+lChQgVWrFhBvXr1+Pfff+nZsyfNmjXjsssuK/I6RURE7NLSICTE7C/e0nUv0/99iMarz1yj1KSJGZvds6fb58fFwcSJ5t/272U9PBe5VGPXrl1069aNFi1a0LJlS2bMmAHAoUOH6NmzJ02aNKFnz55Zf9a3PLwQq0Cyq3O8+5WHFi1asHbt2hz3HT16lH379hEeHk5ISAiZmZlZj508eRKAHTt2MG3aNL799lsSEhK45pprsh4DKF++PGB2VNPT04v+2XhRdHQ0mzdv5qOPPuLuu+/m5MmTWJZFTEwMGzZsYMOGDfzxxx/cfffdhX6P/v37k5CQwMqVKxkzZgz79u3z+LlHjx7lmmuuYdKkSXTp0iXHuh0vmLR/TZkyBTC72bt27co6fvfu3dRz+s3b/stIo0aNsNls9OvXL8cFm+np6Xz88cf079/f5dqaN29O5cqV2bRpU9Z7AtSpU4cbb7yRVatWeXyeIiIi+fnpJ2jbFj5ckA4zZnBnXLgJzRUqmBS8caNHoTk11dxOTTW34+KKZ/2BqsjBOSQkhGeffZbExER++eUXZs2aRWJiIlOmTKF79+5s376d7t27Z4WURYsWsX37drZv386cOXMYNmxYkU/CH7p3705qairz5s0DICMjg4ceeoj777+fihUr0qBBAzZs2EBmZia7du3KCkZHjx6lUqVKVKtWjX/++YdFixbl+15VqlTh2LFjPj2fgujbty+RkZG8+eab9O7dm9dff53jx48Dphb533//pWvXrnz++eecPHmS48eP88UXX2Q9f+bMmTl2v12JjIzkf//7X9YvYvk5ffo0N954I7fffju33HJLjsfi4+Ozgr3j15gxY7LOZ968eViWxS+//EK1atVylGmACbqJiYlZddPffPMNzZs3z3p86dKlNGvWjPr162fdt2PHjqxffv78809+//13GjRowIkTJ7J+nidOnGDJkiW0atXKo/MUERHJS0qKaYZxySUQfnAlvcdFwqhRcOyYqddITITHHoMzG3WuOIdmO4VnL5Rq1K1bNytkVKlShebNm7Nnzx4WLlyYdTHcHXfcwRVXXMHTTz/t9kIs56AS6Gw2G5988gkjRozgqaeeYv/+/fTv35/HHnsMMPXFF154IS1atKB58+a0b98egDZt2tCuXTuaNWvGeeedR9euXfN9r+uuu45bbrmFhQsX8sILL3D48GHWrFnDhAkTvHY+jjXOERERWb8QuPPEE09w6623smXLFrZs2cJFF10EQOXKlXn77bfp2LEjffv2JSIigrPPPpvWrVtTrVo1AH7//XePzvvRRx+lffv2jB07lipVquR57Pvvv8/y5cs5ePAgc+fOBUx7O3u9d16uvvpqvvrqKxo3bkxYWBhvvPFG1mNt27Zlw4YNnHvuuYwfP57LLruM0NBQLrjggqz3AViwYEGuMo0VK1YwZcoUQkNDCQoK4sUXX6RWrVokJydz4403Aman+tZbb+Wqq67Kd50iIiJ5+eIL03Y5Y99+fmk5hs6bzQX0XHABPP889O2b72skJ5uaZndSU83j/ftDw4ZeWnhJ4s32HTt27LDOO+8868iRI1a1atWy7s/MzMy6fc0111g//vhj1mNXXnllVls0dwKxHZ2zn376yTr//POttWvX+nspAePYsWOWZZm2bB06dMj6bK655hrr1KlT/lxaiRFo/52LiEjgem9+hvXUeS9baVVrmKLT0FDLeuwxyzpxokCvM3myZYWFua5jDQszj5dmebWj89rFgcePH+fmm29m+vTpVK1aNcdjNputwK245syZw5w5cwA8bivmTxdffDF/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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.clf()\n", - "fig, ax = plt.subplots(1, figsize=(12,8))\n", - "fig.patch.set_facecolor(\"white\")\n", - "s1 = ax.scatter(x_train, y_train, c=\"blue\", marker=\"D\")\n", - "s2 = ax.scatter(x_test, y_test, c=\"orange\", marker=\"x\")\n", - "p1 = ax.plot(x_test, y_pred, c=\"blue\", marker=None, linestyle=\"dashed\")\n", - "p2 = ax.plot(x_test, y_test_pred_fhe, c=\"red\", marker=None, linewidth=2)\n", - "ax.legend([s1, s2, p1[0], p2[0]],\n", - " [\n", - " \"Train Data\",\n", - " \"Test Data\",\n", - " f\"Clear Reg, R^2={sklearn_r2:.4f}\",\n", - " f\"Quant. FHE Reg, R^2={homomorphic_test_error:.4f}\"\n", - " ]\n", - ")\n", - "display(fig)\n" - ] - }, - { - "cell_type": "markdown", - "id": "c18dbdd1", - "metadata": {}, - "source": [ - "### Enjoy!" - ] - } - ], - "metadata": { - "execution": { - "timeout": 10800 - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/user/advanced_examples/LogisticRegression.ipynb b/docs/user/advanced_examples/LogisticRegression.ipynb deleted file mode 100644 index 2afd188d3..000000000 --- a/docs/user/advanced_examples/LogisticRegression.ipynb +++ /dev/null @@ -1,568 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "9b835b74", - "metadata": {}, - "source": [ - "# Logistic Regression\n", - "\n", - "Currently, **Concrete** only supports unsigned integers up to 7-bits. Nevertheless, we want to evaluate a logistic regression model with it. Luckily, we can make use of **quantization** to overcome this limitation." - ] - }, - { - "cell_type": "markdown", - "id": "7d46edc9", - "metadata": {}, - "source": [ - "### Let's start by importing some libraries to develop our logistic regression model." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "858205d9", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "from sklearn.linear_model import LogisticRegression\n", - "from sklearn.datasets import make_classification\n", - "from sklearn.model_selection import train_test_split\n", - "\n", - "from copy import deepcopy\n", - "from typing import Any, Dict\n", - "\n", - "from tqdm import tqdm" - ] - }, - { - "cell_type": "markdown", - "id": "86b77c19", - "metadata": {}, - "source": [ - "### Now import Concrete quantization tools. " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "94df1602", - "metadata": {}, - "outputs": [], - "source": [ - "from concrete.quantization import (\n", - " QuantizedArray,\n", - " QuantizedLinear,\n", - " QuantizedModule,\n", - " QuantizedSigmoid,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "ff9c1757", - "metadata": {}, - "source": [ - "### And some helpers for visualization." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "67330862", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import display" - ] - }, - { - "cell_type": "markdown", - "id": "d4f43095", - "metadata": {}, - "source": [ - "### And, finally, the FHE compiler." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "3b76a5f6", - "metadata": {}, - "outputs": [], - "source": [ - "import concrete.numpy as hnp" - ] - }, - { - "cell_type": "markdown", - "id": "34959f0a", - "metadata": {}, - "source": [ - "### Define our Quantized Logistic Regression model." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "a12ce041", - "metadata": {}, - "outputs": [], - "source": [ - "class QuantizedLogisticRegression(QuantizedModule):\n", - " \"\"\"\n", - " Quantized Logistic Regression\n", - " Building on top of QuantizedModule, this class will chain together a linear transformation\n", - " and an inverse-link function, in this case the logistic function\n", - " \"\"\"\n", - "\n", - " @staticmethod\n", - " def from_sklearn(sklearn_model, calibration_data):\n", - " \"\"\"Create a Quantized Logistic Regression initialized from a sklearn trained model\"\"\"\n", - " if sklearn_model.coef_.ndim == 1:\n", - " weights = np.expand_dims(sklearn_model.coef_, 1)\n", - " else:\n", - " weights = sklearn_model.coef_.transpose()\n", - "\n", - " bias = sklearn_model.intercept_\n", - " # In our case we have two data dimensions, the precision of the weights needs to be 2 bits, \n", - " # as for now we need the quantized values to be greater than zero for weights\n", - " # Thus, to insure a maximum of 7 bits in the output of the linear transformation, we choose\n", - " # 4 bits for the data and the minimum of 1 for the bias\n", - " return QuantizedLogisticRegression(4, 2, 1, 6, weights, bias, calibration_data)\n", - "\n", - " def __init__(self, q_bits, w_bits, b_bits, out_bits, weights, bias, calibration_data) -> None:\n", - " \"\"\"\n", - " Create the Logistic regression with different quantization bit precisions:\n", - "\n", - " Quantization Parameters - Number of bits:\n", - " q_bits (int): bits for input data, insuring that the number of bits of\n", - " the w . x + b operation does not exceed 7 for the calibration data\n", - " w_bits (int): bits for weights: in the case of a univariate regression this\n", - " can be 1\n", - " b_bits (int): bits for bias (this is a single value so a single bit is enough)\n", - " out_bits (int): bits for the result of the linear transformation (w.x + b).\n", - " In the case of Logistic Regression the result of the linear\n", - " transformation is input to a univariate inverse-link function, so\n", - " this value can be 7\n", - "\n", - " Other parameters:\n", - " weights: a numpy nd-array of weights (Nxd) where d is the data dimensionality\n", - " bias: a numpy scalar\n", - " calibration_data: a numpy nd-array of data (Nxd)\n", - " \"\"\"\n", - " self.n_bits = out_bits\n", - "\n", - " # We need to calibrate to a sufficiently low number of bits\n", - " # so that the output of the Linear layer (w . x + b)\n", - " # does not exceed 7 bits\n", - " self.q_calibration_data = QuantizedArray(q_bits, calibration_data)\n", - "\n", - " # Quantize the weights and create the quantized linear layer\n", - " q_weights = QuantizedArray(w_bits, weights)\n", - " q_bias = QuantizedArray(b_bits, bias)\n", - " q_layer = QuantizedLinear(out_bits, q_weights, q_bias)\n", - "\n", - " # Store quantized layers\n", - " quant_layers_dict: Dict[str, Any] = {}\n", - "\n", - " # Calibrate the linear layer and obtain calibration_data for the next layers\n", - " calibration_data = self._calibrate_and_store_layers_activation(\n", - " \"linear\", q_layer, calibration_data, quant_layers_dict\n", - " )\n", - "\n", - " # Add the inverse-link for inference.\n", - " # This needs to be quantized since it's computed in FHE,\n", - " # but we can use 7 bits of output since, in this case,\n", - " # the result of the inverse-link is not processed by any further layers\n", - " # Seven bits is the maximum precision but this could be lowered to improve speed\n", - " # at the possible expense of higher deviance of the regressor\n", - " q_logit = QuantizedSigmoid(n_bits=7)\n", - "\n", - " # Now calibrate the inverse-link function with the linear layer's output data\n", - " calibration_data = self._calibrate_and_store_layers_activation(\n", - " \"invlink\", q_logit, calibration_data, quant_layers_dict\n", - " )\n", - "\n", - " # Finally construct our Module using the quantized layers\n", - " super().__init__(quant_layers_dict)\n", - "\n", - " def _calibrate_and_store_layers_activation(\n", - " self, name, q_function, calibration_data, quant_layers_dict\n", - " ):\n", - " \"\"\"\n", - " This function calibrates a layer of a quantized module (e.g. linear, inverse-link,\n", - " activation, etc) by looking at the input data, then computes the output of the quantized\n", - " version of the layer to be used as input to the following layers\n", - " \"\"\"\n", - "\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 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\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "0df30d0e", - "metadata": {}, - "source": [ - "### We need a training set, specifically a handcrafted one for simplicity. Let's also define a grid on which to test our classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "caef5aed", - "metadata": {}, - "outputs": [], - "source": [ - "X, y = make_classification(\n", - " n_features=2,\n", - " n_redundant=0,\n", - " n_informative=2,\n", - " random_state=2,\n", - " n_clusters_per_class=1,\n", - " n_samples=100,\n", - ")\n", - "\n", - "rng = np.random.RandomState(2)\n", - "X += 2 * rng.uniform(size=X.shape)\n", - "\n", - "b_min = np.min(X, axis=0)\n", - "b_max = np.max(X, axis=0)\n", - "\n", - "x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=42)\n", - "\n", - "x_test_grid, y_test_grid = np.meshgrid(\n", - " np.linspace(b_min[0], b_max[0], 30), np.linspace(b_min[1], b_max[1], 30)\n", - ")\n", - "x_grid_test = np.vstack([x_test_grid.ravel(), y_test_grid.ravel()]).transpose()\n" - ] - }, - { - "cell_type": "markdown", - "id": "0b209247", - "metadata": {}, - "source": [ - "### Train a logistic regression with sklearn on the training set." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "ec57fede", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "LogisticRegression()" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "logreg = LogisticRegression()\n", - "logreg.fit(x_train, y_train)" - ] - }, - { - "cell_type": "markdown", - "id": "5be6c7d5", - "metadata": {}, - "source": [ - "### Let's visualize our data set and initial classifier to get a grasp on it." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "f7076523", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "y_score_grid = logreg.predict_proba(x_grid_test)[:,1]\n", - "\n", - "plt.ioff()\n", - "plt.clf()\n", - "fig, ax = plt.subplots(1, figsize=(12,8))\n", - "fig.patch.set_facecolor('white')\n", - "ax.contourf(x_test_grid, y_test_grid, y_score_grid.reshape(x_test_grid.shape), cmap='coolwarm')\n", - "CS1 = ax.contour(\n", - " x_test_grid,\n", - " y_test_grid,\n", - " y_score_grid.reshape(x_test_grid.shape),\n", - " levels=[0.5],\n", - " linewidths=2,\n", - ")\n", - "CS1.collections[0].set_label(\"Sklearn decision boundary\")\n", - "ax.scatter(x_train[:,0], x_train[:,1],c=y_train, marker=\"D\", cmap=\"jet\")\n", - "ax.scatter(x_test[:,0], x_test[:,1], c=y_test, marker=\"x\", cmap=\"jet\")\n", - "ax.legend(loc=\"upper right\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "996fbe05", - "metadata": {}, - "source": [ - "### Calibrate the model for quantization using both training and test data\n" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "06ed91dd", - "metadata": {}, - "outputs": [], - "source": [ - "calib_data = X \n", - "q_logreg = QuantizedLogisticRegression.from_sklearn(logreg, calib_data)" - ] - }, - { - "cell_type": "markdown", - "id": "cd74c5e7", - "metadata": {}, - "source": [ - "### Now, we can compile our model to FHE, taking as the possible input set all of our dataset." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "b8f8f95b", - "metadata": {}, - "outputs": [], - "source": [ - "X_q = q_logreg.quantize_input(X)\n", - "\n", - "engine = q_logreg.compile(X_q)" - ] - }, - { - "cell_type": "markdown", - "id": "b608faef", - "metadata": {}, - "source": [ - "### Time to make some predictions, first in the clear." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "97eaf932", - "metadata": {}, - "outputs": [], - "source": [ - "# Test the original classifier\n", - "y_pred_test = np.asarray(logreg.predict(x_test))\n", - "\n", - "# Now that the model is quantized, predict on the test set\n", - "x_test_q = q_logreg.quantize_input(x_test)\n", - "q_y_score_test = q_logreg.forward_and_dequant(x_test_q)\n", - "q_y_pred_test = (q_y_score_test > 0.5).astype(np.int32)\n", - "\n", - "# Predict sklearn classifier probabilities on the domain\n", - "y_score_grid = logreg.predict_proba(x_grid_test)[:, 0]\n", - "\n", - "# Predict quantized classifier probabilities on the whole domain to plot contours\n", - "grid_test_q = q_logreg.quantize_input(x_grid_test)\n", - "q_y_score_grid = q_logreg.forward_and_dequant(grid_test_q)\n", - "q_y_pred_test = (q_y_score_test > 0.5).astype(np.int32)\n" - ] - }, - { - "cell_type": "markdown", - "id": "8fb62d52", - "metadata": {}, - "source": [ - "### Now let's predict using the quantized FHE classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "bc999411", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 40/40 [01:11<00:00, 1.80s/it]\n" - ] - } - ], - "source": [ - "non_homomorphic_correct = 0\n", - "homomorphic_correct = 0\n", - "\n", - "# Track the samples that are wrongly classified due to quantization issues\n", - "q_wrong_predictions = np.zeros((0, 2), dtype=X.dtype)\n", - "\n", - "# Predict the FHE quantized classifier probabilities on the test set.\n", - "# Compute FHE quantized accuracy, clear-quantized accuracy and \n", - "# keep track of samples wrongly classified due to quantization\n", - "for i, x_i in enumerate(tqdm(x_test_q.qvalues)):\n", - " y_i = y_test[i]\n", - "\n", - " fhe_in_sample = np.expand_dims(x_i, 1).transpose([1, 0]).astype(np.uint8)\n", - "\n", - " q_pred_fhe = engine.run(fhe_in_sample)\n", - " y_score_fhe = q_logreg.dequantize_output(q_pred_fhe)\n", - " homomorphic_prediction = (y_score_fhe > 0.5).astype(np.int32)\n", - "\n", - " non_homomorphic_prediction = q_y_pred_test[i]\n", - " if non_homomorphic_prediction == y_i:\n", - " non_homomorphic_correct += 1\n", - " elif y_pred_test[i] == y_i:\n", - " q_wrong_predictions = np.vstack((q_wrong_predictions, x_test[i, :]))\n", - "\n", - " if homomorphic_prediction == y_i:\n", - " homomorphic_correct += 1" - ] - }, - { - "cell_type": "markdown", - "id": "f8c1d98a", - "metadata": {}, - "source": [ - "### Aggregate accuracies for all the versions of the classifier." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "8f3236fb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sklearn accuracy: 90.0000\n", - "Non Homomorphic Accuracy: 85.0000\n", - "Homomorphic Accuracy: 85.0000\n", - "Difference Percentage: 0.00%\n" - ] - } - ], - "source": [ - "sklearn_acc = np.sum(y_pred_test == y_test) / len(y_test) * 100\n", - "non_homomorphic_accuracy = (non_homomorphic_correct / len(y_test)) * 100\n", - "homomorphic_accuracy = (homomorphic_correct / len(y_test)) * 100\n", - "difference = abs(homomorphic_accuracy - non_homomorphic_accuracy)\n", - "\n", - "print(f\"Sklearn accuracy: {sklearn_acc:.4f}\")\n", - "print(f\"Non Homomorphic Accuracy: {non_homomorphic_accuracy:.4f}\")\n", - "print(f\"Homomorphic Accuracy: {homomorphic_accuracy:.4f}\")\n", - "print(f\"Difference Percentage: {difference:.2f}%\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "4810fdaf", - "metadata": {}, - "source": [ - "### Plot the results of both the original and FHE versions of the classifier, showing classification errors induced by quantization with a red circle." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "41b274ed", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAsEAAAHSCAYAAAANGxbcAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAACzbUlEQVR4nOzdZ1RU5xaH8WcKXRAQUBR77w0Ve+/d2BKNJVFTNJqYXm96jylqTExMUxOjxhg19l5RsfeOioIU6X3K/UBmBClSBs4M7N9aWVdmzjmzAa/82bPP+6qMRqMRIYQQQgghyhC10gUIIYQQQghR0iQECyGEEEKIMkdCsBBCCCGEKHMkBAshhBBCiDJHQrAQQgghhChzJAQLIYQQQogyR6vEizq7euHuXT3PY1wci7+OhNi7hN8OxsPLFw9v3wKd66hJM//ZaDBy4+xtVCqo1rhKrufYpSUWulZbFhsWQUp8ArktxqdSqfD0dkdrp8hfR0WlxiUpXYIQQghRqoXbOxIZGZntcUVSh7t3dZ54NzDPY1o1VBV7HakpSRza/jed+j+CSlWw12vsft3857SUdJ5p+iZ2DlrmrX4/13N8bx4qdK22zGgwsPbF97h86gq6dF2W5+wc7Bg5bRgVq1ZUqDplXdl4TOkShBBCiFJtWmxKjo+X6XEIB0dnOg8YZw7AifExGPR6hasqfVRqNX1H96JO09pZur1lPQALIYQQQjllOgRnFnbzMm9M7MCSr15SupRSSaVWmYOwRquRACyEEEIIRZW9Icxc3A2/TcTtYDb88Q0+VWrSb+wMpUsqdUxB2N2zPDUb1aCin4/SJQkhhBCijJIQ/J9GrbvwxFs/8O1bk/htzvN4V65B6y6DlC6r1FGpVQT0aat0GUKIUsboUg7N2PGofCtDAe/xEEKUAkYjxtDb6JctQZWYkK9TJARn0nnAOMJvXWPl9+8w97VxvPXDdmo1bK10WUIIIR5AM3Y83o2bUN7BocA3OgshbJ/RaCTW05OIseMxLPouX+fITPB9Rkx5nS6DHiU1JYnPnh1GROj1B58khBBCUSrfyhKAhSjDVCpVxr8BvpXzfY6E4PuoVCqmvv4djfy7ERMVRtDOf5QuSQghxIOoVBKAhSjjVCpVgcahJATnQGtnz+zPVjD9vV/p//BMpcsRQghhA26FhTHm6ek079uXJr16M/vd90hNS3vwiQW0++AhAo/eW2P8x2XL+H316iJft1GPnkRGR+f4eNvBQ2g/bDjthw0n8Ogxrofcos3gwVmO+2DuPL5e9BMAT7zyKo179jKf03Psww98/X6PTuDoqdMFrvvoqdO88P4HuT4feieccTNnFfi6Ocnta1Rcdh88xMgnniyx1ytON2/fpv+EiXQYPoJ2Q4ayadcuAK6H3MKreQvz35WZ/3s7x/PvxsQw+LHHaN63L4Mfe4zo2Ngi1yQzwblwcXWnU/9HzB8nxsfgXK68dBqEEEJkYzQaeeSZmUwZO5Y/v52PXq/nmbfe4o3PPuez11+z6GvtOXSIcs7OBLRqCcCUsWMtev2crP/tV7w8PMwfXw+59cBz3n/xRYb361ucZQHQqmkTWjVtkuvzvhV9WPrN18VehzXQ6XRotdYZ7T5Z8B0j+vdj6sMPc+7yZR6a9gRnt28DoGa1qhxY/Xee58/54Qe6BbTn+WlT+WLhD8z54Qfee+GFItUkneB8CLl6llfHteGfnz9RuhQhhBBWaGdgIA4O9jz60AgANBoNH7/6Kn/88w8JiYksWfU3s999z3z8yCeeZPfBjF1EZ739Np0fGon/oEG8/81c8zGNevTk/W/m0nHECNoOHsKFq1e5HnKLRX/+ybxff6X9sOHsCwoyd2BD74Sbu2nthw3HrVFjbty6RcTduzzyzEy6jBxFl5GjOHD0KABR0dEMeexx/AcNYvobb2Akl73ti0lySgoTZ8+m1YCBjJ0xg5TUVPNz2/buo8eYsXQcMYLxs54lITERgCOnTtFz7MMEDB1G11GjiU9IzNIt3XPokPnz7zB8BPEJiVm61impqTz56mu0HTyEDsNHsCvwIABLVv3Nw888w7ApU2nety9vfPZZrnV/9eOPtB08hK6jRnPlesZ9Q9dDbjFg4iTaDRnKwEmTuXn7NpDREf974ybzuRVbZdxsv/vgIfo9OoFxM2fRsv8AHnvhRYzGjK//lj17aNl/AB1HjGDNli3mc4NOnqTHmLF0GD6CnmMf5uLVa+baRz/1NAMmTmLgpMlMffll1m7daj7vsRdeZN22bXl+L8ZOn0GnEQ/hP2gQP/253Pz4lj176DhiBAFDhzFw0mQAEhITzV/DdkOGsnrT5jyvbaJSqYhPyFi1IS4+Hl+fgi2T+u+27YwbNhSAccOGsm5r3p9TfljnrwtWJuzGZSJDr/Pnt2/iXbk6Hfs9+G0dIYQQyhnW8E2LXm/1uffyfP7cpcu0bNw4y2Nu5cpRrUoVrt64kee5/3v2WTzd3dHr9QycNJnTFy7QpH59ACp4eLBv1SoW/v473/z0E/Pff5/Hx4yhnLMzsx5/DICdBwKBjI6nqZv2/dKl7D0cRLUqVZj8/AvMmDSRDq1bc/P2bYZOmcrR9f/y0fxvad+6Fa9On87GnTv5deVfudY4YMJENBoNDvb27Fz+JwDXbtyk/bDh5mPuREYya/Jk88dvfPYZn36XcZd+wzp1+OnzrMHyxz+W4ezoxNH1/3L6wgU6jngIgMjoaD75bgFrf/4JF2dn5vzwA3N/+YXnp05l4nOz+fXLObRu2pS4hAScHB2yXPObn35mzltv0r5VKxISE3F0yPr8wqW/o1KpOLR2DReuXmXo41M4vnEDAKfOnWff36twsLenZf/+PDl+PH6+vtm+Fm6urhxau4bfV6/m5Q8/YuX33/HC++8zbtgwxg0fxm9//cWLH3zIsvnzcv16Apw8d47D69bi6+NDr4cf4cDRo7Rq0oQZb77Fv7/8TO3q1Znw3Gzz8fVq1WLz0iVotVp27N/P219+ye9zvwHg+NmzBP6zGk93d/YcOsT8X39jcK9exMbHc/DYMRZ+/FGetXz7wft4uruTnJJCl1GjGNqnNwajkRlvvsWmJYup4efH3ZgYAD5ZsAA313IcWrsGwDyWMOG557h0LTjbtZ+ZNJFHhg3j9RnTGfL4FL5bspSk5GTW/vST+ZjrIbfoMHwEri4uvPXsLDr6+2e7TnhUFJX+C84Vvb0Jj4rK83PKDwnB+eDfbQiPzv6c3754nu/emYKnjx+Ne1RTuiwhhBClwKqNG/l5+XJ0Oj1hERGcu3zZHIKH9ukNQMvGjbN0BfNy4OhRflmxki1LlwCw48ABzl+5Yn4+PiGBhMRE9gUFmUNUv27d8ChfPtdr3j8OAdnfwv5gbtbQ96BxiH1BQTz56HgAmtSvT5P69QA4fPw45y9fodcj4wBIS0+nXYvmXLwWTCVvb1o3bQpk/JJxv4BWrXj1408YPWgQQ/v0ppyLS5bn9x89wpPjM16zfq1aVK1cmcvBwQB0ax9AeVdXABrUrsON27dzDMGjBg40/+8rH30MwMHjx81fy4eHDOGNzz7P9fM2ad2sKVUqVQKgacMG3Lh1i3LOzlT3q0KdGjUAGDtkMD//15mNi49n2iuvcOX6dVSoSNfpzNfq0aEDnu7uAHRu25bn3n2XiLt3+WfzZob26fPAEYkFi5eYu8e3QsO4cv06kXej6ejvTw0/PwDz9XfsP8Avc74wn2v6e/Pbl1/m+Ror/l3P+OHDmfnYZA4eO8aUl1/m8No1VPLx5tz2bVTw8ODY6TOMnTGDw+vW5vj9NVFZ6EZYCcH51P/hmYTfusbGZfP44vkRtF75JzVr185yjMFgRK/To9FqFKpSCCEEPLhza2kN69Rm9eZNWR6LS0jgTmQEdWvW5OylSxiMBvNzKWkZb/0Hh4TwzU8/s2vFcjzKl+eJV14lNfXezXT29vYAaNQadDr9A+sICw/n6dffYPm3880B0GAwsOPPZdm6otbKaMwIdZmDFsDpCxcfeO7z06bSt2tXNu/eRa9HHmH1jz/iaJ+/z9vezt78Z41anevXO0v4ekAQ02o15u+7wWAgLT3d/JxDltd78Pf3va+/oUvbdiybN4/rIbfoP2GC+TlnJ6csxz4ydCh/rlnDyvUbWPBh7jcNQsZoxs4DB9i+7A+cnZzo9+gEUlILfkPngzrBv/61ktU//ABAu5YtSU1NJTI6Gp8KFXD47+95yyaNqVm1KpevBWeb8/apUIGw8HAq+fgQFh6Ot6dngWu8n8wEF8Cjz32Of9chJMbH8PSUKUT914q3c9DiU8MLfbqeoH9P5np+aFXZKU0IIUqjbu3bk5ycYl6lQa/X89onn/DEuHE4OTpSrUoVTp07j8FgICQ0lCMnTwEZXVlnJyfKu7pyJzKSzXv2PPC1XF1ciP9vRjaz9PR0Hn32Od574Xnq1qxpfrxnx458t2SJ+eOT584B0NHfn+Xr1gGwefdui9xtXxAd/f1Z8d/rn7l40Rxy27RoTuCxY+Z528SkJC5du0a9mjUIi4jgyCnT1y4RXaZuKMDVGzdoUr8es6dOpVWTply8ejXra7b2Z/natQBcunaNkNDbWb5W+fHX+ozxib82bKBdixYABLRsycr16wH4c+06OvhnzP5Wq1KF42fOAPDv9u2kZwrBOalXqxY3bt02j9CsWPev+bnYhHgqV6wIwJK/876JbNzw4cz/7TcgYxQF4PadO+a53sziEuJxd3PD2cmJC1evcvjECSDj+7AvKIjgkBAA8zhEj44dWPj77+bzTX9vfvvySw6s/jvbf48MGwZAVd/K5tGd81eukJKairenJxF376LXZ/wCcO3mTa5cv06Nqn7Z6hzQowdLV2csW7t09T8M7Nkjz69BflhtCD56rmQH9PNDrdEw/f3fqNWoNbdu3mT39u1Axm+Ffad1BWDDdzswGAx5XUYIIUQpo1Kp+GPeXFZv2kzzvn2pFtAelUrNS09m3LDVvlUrqvv50XrgIF744AOaN2oEQNMGDWjesCGt/rs5yrTiQ176d+/G2q1bzTfGmQQeO8bR06f5YO48881hoXfC+eyN1zl6+jTthgyl9cBB/LgsY6b31elPs+9wEP6DBrFmyxaqVs7+1n9RvPHZZ1lu1Eu7b7m4KQ+PJSEpiVYDBvL+3LnmmWpvT0++++hDJj//Au2GDKXH2Ie5eO0a9vb2/PrlHF54/30Chg5jyOOPZbmZDmD+r7/RZvBg2g0Zip1WS58uXbI8P/WRhzEYjLQdPISJs5/nuw8/Mnch8ysmLpZ2Q4by7W+L+fjVVwD4/I3XWbzqb9oNGcofa9bw2WsZK4JMGjWKvYcPEzB0GIeOH8fF2TnPazs6ODD33Xd46Ikn6ThiBN4VKpife+7xx3l7zhw6DB+BTq/L4ypQ0cuL+rVqM37EvZntsPAItJrs71T37twZnV5PqwED+d8Xc2jTvDmQ8X2Y++47PPLMTAKGDmPi7Iz55JeefJKY2DjaDB5MwNBh7D54MB9fNfjw5Zf4ZcUKAoYOY/LzL/D9Rx+hUqnYdziIdkOH0n7YcMbPmsXXb79tHr2Y/sYb5mXzZk+dwvb9+2nety87Duxn9tSp+XrdvKiMptsRS1DlWq154t3AfB3bqqH1LUkWExnGuaO7mTK6nfkxXZqON3p9RnRoLE/MG0+rvjkv1+J781BJlWl10g7vV7oEq3Rl47EHHySEyJPmzfeok8P8plICjx5j8gvP88fcubS474Y5IYpbUnIy7YYMZe+qv8xzzt8tWUrVyr4M7FH0Dqo1uxwaiv69rDfGTotNISjTL4wmVtsJtmbuXpVo32c0Z2KqA5AQH4/WXkvfqRnd4PXfbie33y1kJEIIIUq/gFYtObd9uwRgUeJ27N9P64EDeXL8OHMABnhy/LhSH4ALyupDsDWORWR2/uxZhvXrx8ply+g4qg1uXuW4efY2Z3Y/eIC/rLFv00HpEqxS7X4PfvtTCCGEyI/uHTpwbvt2pk+cqHQpVs/qQzBYdxDeFnSH8Dt3+OB//+PwoQP0fjxj/ki6wUIIIYQQ1ssmQrA16zZkEo8/+SR6vZ4XnnkG39buuLg7c+XodS4euvrgC5Qx0g3OmXSDhRBCiJJlMyHYmrvBPR77mn4DB5KYmMjsZ56mzUMZM2Drv92R6znSDRZCCCGEUI7NhGCw3iCsVqt579NPadGqFXfCwvhrz89ondWc33+Zq8fy3i6zLJJucM6kGyyEEEKUHJsKwdbMwcGBr7/7jqrVqnHxwnkqd3IDYP2C7bmeI91gIYQoPT797jv8Bw2i3ZCMNU9Nmw406tGTyOjobMdXbNW6pEvMIre6HuTf7dv5YuEPuT5/9NRpXng/713K8qukv0ZLVv3N7HdLdrfB4nLq/Hl6jBlL28FDGPXkU8QlJJif+/z7hTTr05eW/fqzdc/eHM8PDgmh2+gxNOvTlwnPPZdtnefSwOZCsLV2g8/EVMfD05P5ixbxxbx5zHx3BnaOdpzacZ6bZ28rXZ6wEdINFsI2HTx2jI07drJv1SoOrvmHtT//hF+lklm32LTbVkkZ2KMHz0/LfaOCVk2b8Pkbr5dgRcq5f8c6azL9jTd55/nZHFq7hsG9e/HVokUAnLt8mZXr13N43Vr+/vEHnnv33Rz/Dr35+RdMnziBk5s34e5Wnl//+qukP4ViZ3MhGKw3CAPUqFmT3v364VqhHF3GtkVv1LHhO5kNvp+MRAghSpOwiAgqeHiYdx/z8vDAt6JPlmOSU1IYPnUaPy9fnu38rxYtosvIUbQbMpT3v5lrfnzs9Bl0GvEQ/oMG8dOf986r2Ko1r378CQFDh3Hw+HEqtmrN219+RcDQYXQfM4Y7kZHZXiMqOpohjz2O/6BBTH/jDYzc+1m6bM0auo4aTfthw3nmrf+ZQ9GWPXvoOGIEAUOHmbfczdwtXbVxo3nnsD7jxwOw++AhRj6RsVPe3ZgYxk6fQbshQ+k+ZgynL1wA4IO583jqtdfp9+gEmvTqzbe/Lc71a/vyRx/hP2gQAydNJuLuXSBj6+fuY8bQbshQxs6YYd66t9+jE8w7jEVGR9OoR09zzQ8/8wzDpkyled++vPHZZ+brL/5rFS369qPrqNEEHjtqfnz99h10Gz2GDsNHMGjyZPPX9IO585jy0kv0evgRprz0Mn3GjzdvRQ3Q+5FxnDp/PtfPJyExkYGTJtNxxAjaDh7Cum3bzM/9vno17YYMJWDoMKa89BIAdyIjGTtjBgFDhxEwdBiBR/O3wdLl4GA6tWkDQI8OHfhn8xYA/t22nZEDBuBgb08NPz9qVatG0MmTWc41Go3sCgxkeN++AIwbNpR1W7dR2thkCLZWps0zTKoEeHCMjWzdsJnQy+EKVSVsjXSDhSi6ig0a5vrfb5nC5G9/Ls/z2Pzq2bEjIWFhtOjbj2ffeYc9h7LuDpqYmMSop55i1MABTB49Ostz2/bu43LwdXatWM6B1X9z/MwZ9h4+DMC3H7zP3lV/sWflShYsWUzUf+MLiUlJ+DdvRuA/q+nQujWJSUm0bd6cwH9W09Hfn19WrMhW40fzv6V961YErVvH4F69uHk7FIDzV67w1/oNbP19KQdW/41Go+bPtWuJuHuXGW++xdJvviHwn9Us/urLbNf8eP63/PPjjwT+s5rl336b7fkP5s6jWcOGHFzzD/977jmmvvyK+bmLV6/yz6If2bliOR/Pn096enq28xOTkmjVpAlB69bRqY0/H82bD8DUl1/h3eef5+Caf2hcrx4fzZ+f5/cH4NS58/z65RwOrlnDXxs2EBIaSlh4OB/Mm8eWP35ny9IlnL98xXx8+9at2PHnMvb/vYqRAwbw1Y+LzM+dv3yFtT//xC9zvmDiQyNZ8vffAFy6do2U1FSaNmiQax2ODg78MW8u+1atYv1vv/LaJ59iNBo5e+kSny74jn9//YXAf1bz6X9bL7/4wQd0btOGwH9Ws2/VXzSsWweA3uPGZ9mW2vTfjv0Zu7M2rFPHHLD/3riJW6EZ3+/bd+7g51vJXE+VShW5fSdrRomKicHdzQ2tVvvfMZW4HX7ngV9jW6NVuoDCOnrOaJVbKmd29sJJ0knhojGQXz9Zyis/PJfjcaFV25bJ7ZTt23SQrZSFEKVCORcX9v61kn1BR9h98CATZz/Pu7NnM37EcADGTJ/Oc1MeZ8zgwdnO3bZvH9v37aPD8BFARvC7cv06ndq0YcHiJazduhWAW6FhXLl+nQoeHmg0Gob16WO+hr2dHf27dwOgRePG5iCU2b6gIH6f+w0A/bp1w6N8eQB2Hgjk2JkzdBmVEc5TUlLw9qzA4eMn6OjvTw0/PwA83d2zXTOgVSueePVVRvTrx5DevbM9f+DoUZZ+8zUA3QICuBsTY55N7dutKw729jjY2+NVoQLhUVFUqVQpy/lqtZqH+vcHYOyQITzyzExi4+OJjY+nc9uMd1LHDRvGo88+m+2179etfYB5B7UGtetw4/ZtoqKj6dy2Dd6engCM6N+fy8HBANwKu8PE52YTFhFBeno61f/7OgAM6NEDJ0dHAIb368snCxbwwYsvsnjVKsYNH55nHUajkbfnfMm+oCDUajW379whPDKSXYEHGdavH14eHsC9r/euwIP88MknAGg0GvPnsGXpkjxf59sPP+DF9z/gk28XMKBHD+zt7B74NSprbDYEg3UG4TMx1Wnsfh2AKU89xcUzF9i06V9W7FjE8BP9qd88598ObXUsoiyG95JQu19LrmzM31teQojs7pw/9+CDgAljRjNhzOgHH5gPGo2GLu3a0qVdWxrXq8fvq1ebQ3BAq5Zs2bOH0YMGoVJl/bllNBp5fto0Hh87Jsvjuw8eYueBA2xf9gfOTk70e3QCKakZNyc5Ojig0WjMx9rZ2Zmvq1Fr0OnyPydsNBoZN2wY7zw/O8vj67fnPspn8s07b3P4xAk27tpF54dGsuevlfl+XQc7e/OfNRp1vmq+/2t3P61Wg8FoACA1NTXLc/aZX0/94Nd74f33eWbyJAb26MHug4f4cN4883MuTk7mPzs7OdG9Q3vWbdvOqg0bH/g1+HPtOiKj77L3r5XY2dnRqEdP8/e1IHqPG09CYmK2xz986UW6d+hA/Vq1WPNTRvf60rVrbNq1C4DKFSsSEhpmPv5W2B0q3ze6U8HdnZi4OHQ6HVqtllthYVT2qVjgGq2dIuMQyQml7w7DnKhUKj788jN8XPxIJ4Xnn51BfHy80mVZVFHDu8wGCyFKg4tXr5k7iAAnz5+jauXK5o/fmDkTd7fyPPfuu9nO7dWpE4tXrTIHmtt37hAeFUVcQjzubm44Ozlx4epV82oThdXR35/l69YBsHn3bvMcbbf2AazevInwqCggY473xq1btGnRnH1BQQSHhJgfv9/VGzdo07w5b86ciZenJ7fCwrI836F1a/5cuxbICPUVPDxwK1cu3zUbDAb+3rQJgOXr1tG+VSvKu7ri7ubGvqAgAP74Z4159rV6lSocO3MGgNX/nZeXNs2asffwYaKio0lPTze/FkBcQoI5+P2+enWe15k0chQvfvABrZo2MXfYg06eZOrLL2c7NjYhHm/PCtjZ2bEr8CA3bmfcPN81oB2rN240j7yYvt7d2gfw4x9/ABk3Qcb+lyO2LF3CgdV/Z/uve4eMn6um76fBYODT774z/5I1oEd3Vq5fT2paGsEhIVy5fh3/Zs2y1KhSqejSrp3567F09T8M7NnjgV9PWyMzwcXMzt6ejtUH4oQrN24GM3v6dNJL4TIjQghRliUmJfHEK6/SemDGEmnnr1zhtWdmZDnms9dfIyUlNctNWQA9O3Vk1KCB9Bj7MG0HD2H8zFkkJCbSu3NndHo9rQYM5H9fzKFN8+ZFqvHV6U+z73AQ/oMGsWbLFqpWzli9omGdOrw5axZDH59CuyFDGfLY44RFRODt6cncd9/hkWdmEjB0GBNnz852zdc/+4y2g4fQZvBg2rVsmW0W9rUZ0zl+5izthgzlf3O+YOHHHxWoZhdnZ46cPEWbwYPZFXiQV6Y/DcDCjz/ijc8+o92QoZw6f55Xns54fOZjj/HjH8voMHxEvpZ/q+Tjw2vTZ9Bz7MP0emQc9WvVylL7o88+S6cRD1HhvxGF3LRs0hjXcuV4dMQI82M3b4fi6OCY7dgxgwdz7PRp2g4ewh///EO9/16zUd26vPjkk/SbMIGAocN49eOMEYhPX3uN3QcP0XbwEDo9NJLzly8/8PMCWPHvv7To249W/Qfg6+Njrq1R3bqM6N8P/4GDGD5lKnPeetP8rsKIadMI/W8++L0XnmfeL7/SrE9f7sbEMHHkyHy9ri1RGY3GEl9qwcOnKc/OOfrgA/PB2sYhAPM4hMkHw+dy8dRFrpYPJD4+lu9++YWADqWnA1rUkQiZC86ZjEMIkX+aN9+jjm/JLEkmxP1C74TTf8IEjm5Yj1qd0V98/dPPeHjoEJrUr69wdWXL5dBQ9O+9meWxabEpBP33zkFmNj0TbEscVS689fpH2JVXlaoALIQQQpRlv69ezTtffc1Hr7xsDsAAH7z0ooJVifyQEFyC6tZtQI2m9+4uTU5OxinTcL0QQgghbMsjw4bxyLBhSpchCkFmghUSdOgQA3v0IOjgQaVLEUIIIYQocyQEK2Tntm1ERkQw66mnuHblyoNPEEIIkTujEQVucRFCWBGj0QgF+HdAQrBCnnvpJbr36kV8XBxPT5lC1H9LmQghhCg4Y+htYlNTJQgLUUYZjcaMfwNCb+f7HJkJVohGo+GjOXN4fNw4zpw6xcxp0/hxyRKZERZCiELQL1tCxNjxRPpWhgdsqCCEKIWMRoyht9EvW0J+/wWQEKwgZ2dn5i5cyPiRIzl14gSvPf88n8+dm2UXICGEEA+mSkzAsOg7pcsQQiisIL8CyziEwry8vZn/44+4urqya/t2Tp88qXRJQgghhBClnnSCrUDtunX5asEC9Ho9zVu2VLocIYQQQohST0KwlWgTEJDl45SUFBwds2+3KIQQQgghik7GIazQ/j17GNC9O2dOnVK6FCGEEEKIUklCcAlKT0nP13Gb1q8nMiKCGVOnciskpJirEkIIIYQoeyQEl4AaTTK2St6xeH++jn/jnXdo2749UZGRzJgyhbi4uOIsTwghhBCizLH5EHz0nPUtjH4mpnqWjwc83QM7By1HNpzi2ombDzzfzt6eOfPnU6t2ba5cvswLzzxDenr+ushKCK3aVukSSqXa/eQmSSGEEKK4KBaCjwcGK/XSJc7Dtzw9JnYEYNWn6/O1o5GbmxvzFy3Cs0IFAvft4/233iq1OyHZt+mgdAlCCCGEKGNsvhMMttEN7vdEN1zcnbl46Bqnd17I1zWq+Pkxd+FCHBwcWLd6NVcuXSqOUi2iqN1gCcI5k26wEEIIUTxKRQgG6w/Czm5O9H+qOwCrPt+AQW/I1zWaNm/Op199xQ+LF1OnXr1iqdNSJAgXDwnCQgghhOVZJATHxMQwcuRIGjRoQMOGDTlw4EC+zitLIxEA3ca1p0IVd25fvMOBv4/m+7zuvXvTyt/f/HF6WlpxlCeEEEIIUWZYZLOMWbNm0a9fP1auXElaWhpJSUmWuGyBFXc3uFXDguxIneFMTHUau18HwM5By9Dn+vLTC3+y9usttBnUHHtHuwJdb8eWLXzy/vv88NtvVK1e/cEnlLDQqm3xvXmo0Ofbt+lA2uH8raJRltTu15IrG48pXYYQQghRahS5ExwbG8vu3bt5/PHHAbC3t8fd3T3f55e1bnCbwc2p2qgy0WGxbP91X4HONRqN/LV8Obdv3eLpKVOIjYkpniKLSMYihBBCCGHtihyCr127hre3N5MnT6Zly5ZMmTKFxMTEbMctXLgQf39//P39SU2+W9SXVURhO82ZZ4PVajUjXuwPwMbvd5IQnf1rlRuVSsUnX35JvQYNuH7tGs8+9RRpqamFqknYHpkNFkIIISxHZSziultBQUEEBASwb98+2rVrx6xZs3Bzc+O9997L9RwPn6Z0G73K/HGLgBpFKaFEFWYkAjCPRJh8NWkR5/ZdotdjnRj16qACXSssNJTxI0cSfucOg4YO5YPPP0elKlxdxakoYxG2TMY5cibjHEIIIZQwLTaFoKCgbI8XuRPs5+eHn58f7dq1A2DkyJEcPZr/m77AtkYiLNENBhjxYj8Adi4+QGRIwTrjlXx9mbtwIU7Ozqz75x8WfPNNoWoqbmV1Ew0Z58iZdLKFEEJYkyKH4EqVKlG1alUuXMhY+3bbtm00atSoyIWVdtUaV6HtkBbo0vX88+XmAp/fsHFjPvv6a9RqNb/8+CNhoaHFUKUoLAnCOZMgLIQQwlpYZIm0uXPnMm7cOJo1a8bx48d57bXXCnwNW+oGF9b93eChz/VBa6fh0Jrj3Dhzq8DX69K9O//74AN+WrqUSr6+lirTospqNxgkCOdGgrAQQghrUOSZ4MK4fybYpCzOBi//cB3bft5LHf8adBvfvsh16fV6NBpNka+TX47ODjTuWg+1Ou/fp8rqfDDIjHBuZEZYCCFESchtJtgi6wSL/Mu8bjDAgKe6s39lEJeDgrkcFFyka0cabxLCORrTFTuVQxErzb+x/xtKdwsE+NJK1j7Omax9LIQQQklWFYKPBwbbTDf46DljobvBmZXzcOHxL8YS+M9RitKTNxgNrNy3n+TYOG66H2N4wGS0moJtxFFQx7ecQZ+uJ/xaxAOPLeomGrZOgrAQQghhXawqBJdVTbs3oGn3BkW+zkPhfRg/ciSht0O4bDzE51/OfeCYQlFs+2Uvyz9YV2zXF0IIIYQoLsWXkESJ8/bxYf6PP+Lq6srWTZuY88knSpckhBBCCGGVJASXMnXq1WPOt9+i1Wr5bdEili1erHRJQgghhBBWR0JwKdSufXv+9+GHAMz98ktiY2KULUgIUSwio2O5fitM6TKEEMImyUxwKTV0xAji4+Jo064d5d3dlS5HCGFhEXdjOHzyHEYgTaejbnU/pUsSQgibIiG4FBs/aZLSJQghioEpAOsNBgAuXbsJIEFYCCEKQEJwGXc0KAiNRkPNWrVwK19e6XKEEA9wfwAG0BsMEoSFEKKAJASXQUajkQvnzrFx3Tq2bNxIm4AAjgYF8feGDSW625wQomAio2OzBWATUxBWqVTUqVZFgeqEEMK2yI1xZdCRw4dZvXIlRuC35ct5+8MPad6yJRvWrlW6NCFEHhKSkslrTx0jEBefWFLlCCGETZMQXMbEREez6s8/8alYkWdmz6aClxdht29z9fJlPCtUULo8IUQealSpRL0afmhy2ARHo1bj7VmeFg3rKlCZEELYHhmHKEOMRiOff/QRDo6OPPbEEwDcCQtj/969tGnXjjYBAQpXKIR4kLo1qgJwKTjEPBahUavx8iyPf5OGqNVF385dCCHKAgnBZYher0etVvPwhAkAXDh3jmNHjnD+7Fm6du+OnZ2dwhUKIfIjcxA2ggRgIYQoBAnBZYhWq6Vegwa88txztPL3JykxkfIeHvQdMID2nTqZj0tNTeXo4cNUrFSJWnXqKFixECI3dWtURaVSE5eQSIuGdSUACyFEAUkILmPGT5qEb+XKxMbE0KR5cypUqEAFLy/z83FxcRzcv5/1a9dy8tgxfl22DL9q1fK8ZkJMUnGXLYTIQZ3qsgqEEEIUltwYVwb17NOHEaNHE3HnTpYAnJSYyKZ//2X3jh2MGDWKqU89xSuzZ5OSkpLjdeq2qYlKreLQmuOc3H6upMoXQgghhCgyqwvBxwODlS4h346ey2uxotydialu4UoK50ZwMDeCg80f29nbk5iYyMH9++ncrRtjH32UvgMGcGDPnhzPr9a4CsNm9wXg5xf/JPLm3TxfL7RqW4vVLkqH2v1aKl2CEEKIMsrqQjDYVhAuLGsIwmMffZRqNWpw+eJFAOzs7Jg0ZQodOndm7pw5ADz62GN06NIl12v0mdqFZj0akhSXwvczl5Keml4itdsi+zYdlC7BKkkQFkIIoQSrDMG2pLDdYFA+CKtUKpKSkvh+3jx2bttmfrxDp064uLiYP3ZwcMj1Gmq1mkmfjsLLz5Mbp2+x/IN1eb6mdIOFEEIIYQ2sNgTbUjfYloOws7MzYx99lLlz5hC4fz+RERH88uOPuc4B58SlvDNPzBuH1l7L7j8OEvjPsTyPL8tBWLrBOZNusBBCiJJmtSEYyk4QVlrrNm14Yvp0lvz8M6+98AJ169fn6VmzCnSNao2rMObNwQAsfXMVty6GFUeppYIE4ZxJEBZCCFGSVEajscTTm4dPU7qNXpXv41sE1Ci+YiyoVUPbXqczLSWZ1JQkXN0ztk82Go2oVHl/To3dr5v/bDQa+eXlFQT+fZSKtbx57a8ZOJbLfZTC9+YhyxRug9IO71e6BKt0ZWPe7yIIIYQQBTUtNoWgoKBsj1t1J9jEVjrCttwNBrB3dCpQAIas4xwqlYpx7wyjcr2K3LkawW+v/4UCv2PZBOkG50y6wUIIIUqKTYRgkCBc0vITgE0yB2F7J3uenDceRxcHjqw/yY7fcu94luXZYCGEEEIoy2ZCMEgQVsqudb+xeM4LeXZ1MwfhijW9mfDRSABWfPwvV45dz+20Mh2EpRucM+kGCyGEKAk2FYJtSWkJwlFhN1n04dOs//1rNi6bm+exmYNw6/5N6TmpIwadgR9m/k58VEJxlypKEQnCQgghipvNhWBb6QaXFhUqVeWJt34AYPGcFwjauSbf5454sT+1WlYjOiyWn174E4PekONx0g0WQgghREmzuRAMthOES0s3uGO/hxn15DsYjUbmvfEoV88dyfXYzN1grb2WaV+Po5yHC2f3XuLf+dtyPa8sB2GRM+kGCyGEKE42GYJBgnBJG/74q3QdPIHUlCQ+e3YYkWE3cj02cxD28C3P43PGolKp+Hfeds7suVgS5doU6QbnToKwEEKI4mKzIRgkCJcklUrFlNcW0Mi/GzFRYRzdnff2yJmDcKNOdRk8sxdGo5GfX1yOPl2f4znSDRZCCCFESbHpECxKltbOnuc+Xc7MD5fSZ/TTBTq3/9PdcXJ1JD4qgeT4/G/JXFZINzh30g0WQghRHCQEiwIp5+ZB+z6jzR8nxN7N14YYarUatVb+ugkhhBDCOkgqEYV24/IpXn6kNat+/EDpUoQQQgghCkRCsCi0iNvXiY64zcrv32HP+qVKlyOEEEIIkW8SgkWhte4yiAmzvwDg+3encjZol8IVCSGEEELkj4RgUST9xs6g/8Mz0evSmfPiSG5dO6d0SUIIIYQQDyQhWBTZ+Gc/xb/rEBLjY/hk1hBiou4oXZIQQgghRJ4kBIsiU2s0TH//N2o1ak3E7WBOHtisdElCCCGEEHnSKl2AKB0cnVx4cc5qLp48QNsew5UuRwghhBAiT9IJFhbj7lUpSwCOi41VsBohhBBCiNxJCBbF4srZIIb27cuyJUuULkUIIYQQIhsJwaJYhFw9S1RkJB+/+y67d+xQuhwhhBBCiCwkBIti0XXQBJ6YMQODwcCLs2Zx9vRppUsSQgghhDCzyI1xNWrUwNXVFY1Gg1arJSgoyBKXFTbu6VmzuHXzJuv++Ydnpk2jvr4zoFK6LCGEEEIIy60OsWPHDry8vCx1OVEKqFQq3v7wQ0Jv3+bI4cMka7bR0NiV9FSd0qUJIYQQooyz+XGI44HBSpcg8mDv4MBXCxZQo1YtEvQxxBLO/r/knQIhhBBCKMsiIVilUtGnTx9at27NwoULLXHJUufoOaPSJZS4MzHVASjv7s63P/7IS7PfpoKqClt/2UtyfEqO54RWbVuCFQohhBCirLJICN67dy9Hjx5lw4YNzJ8/n927d2c7ZuHChfj7++Pv709q8l1LvKyZrXSDy2IQNvGrVo1xT42jjn8NkmKT2fSzrBhxP/s2HZQuwWrV7tdS6RKEEEKUMhYJwVWqVAHAx8eH4cOHc+jQoWzHTJs2jaCgIIKCgnBw8rTEywobYOoGQ8Y7BgOn9yTOGMl7c19g3d//5HiOdINFTiQICyGEsKQih+DExETi4+PNf968eTNNmjQpcmEFJd1g29CwYx3sKxtIN6by1isvc+zIEaVLsirSDRZCCCFKRpFD8J07d+jUqRPNmzenbdu2DBw4kH79+lmitgKzlSBc1tzfDX7+nRepSC10eh0zn3iCG8HB2c6RbrDIiXSDhRBCWIrKaDSWeGvSw6cp3UavKumXLbIWATUsdq1WDcvWermN3a+b/2w0Gnl/+DdsOb2CGMKoXqMGi1eswN3DQ8EKLcv3ZvaRoIJIO7zfQpWUPlc2HlO6BCGEEDZkWmxKjntY2PwSacI23N8NHjyjF/Voh6vWk+vBwTz79NOkpaYqWKGwFdINFkIIYQkSggvAkuMWZX02uHnPRlRvWJW6uva4u3lw4uhRjpai+WAZ5xBCCCGsm4RgBZW1IHx/N3jA0z1wUDnR1LEbX337HQEd5KYwE7lBLm/SDRZCCFFUEoKFYlr2bYJvHR90ERo0US7mx5MSExWsynKkG1y8JAgLIYQoCgnBBWTpFSjKcjdYrVYz4KkeAGxYsBN9up7A/fvp360bgfv2KVWi1ZBusBBCCFF8JAQLRfkPbEbFml5Ehtzl0NrjHNy3j+joaGZPn86lixeVLq/IpBtcvKQbLIQQorC0Shdgi44HBlt0uTRb7gYXZqm3MzHVzUumqTVq+j/ZnV9eXsH6BTv43/rnuHH9Ols2bmTGlCksWbkSbx8fS5ddokKrti30kmn2bTrIcmlCCCFEMZAQLIrk6Dljkdc8bju4BevmbSM8OJLTOy/wweefE37nDieOHeOZadP46fffcXZ2tlDForQp7m6wrEsshBClk4xDCMVp7DQ06VofgLuhMTg6OvL1d9/hV7UqZ0+f5uVnn0Wv1ytcZdHIWITtkpELIYQonSQEC+twXzPZs0IF5i9ahFv58uzbs4ezp08rU5cQQgghSiUZhxBWq2atWny1YAFGg4GmzZsrXY4QQgghShEJwcKq+bfNOkaQnJyMk5OTQtUIIYQQorSQcQhhM3bv2EH/bt1kNEIIIYQQRSYhWNiMbZs3czcqihlTp3L71i2lyxFCCCGEDZMQLGzGG++8Q9v27YmMiGD6lCnEx8crXZIQQgghbJSEYGEz7OztmTN/PrVq1+bKpUs8P2MG6enpSpclhBBCCBskIVjYFDc3N+YvWoRnhQoE7tvH+2+9hdFouzvuCSGEEEIZEoKFzani58fchQtxdHRk/Zo1BF+9qnRJQgghhLAxskSasElNmzfnk6++wrNCBWrWrq10OUIIIYSwMRKChc3q3qtXlo9TU1NxcHBQqBohhBBC2BIZhxClwuYNGxjUsyfXg4OVLkUIoQCDwSD3BwghCkRCsLB5RqORNatWcScsjOmPP0703btKlySEKEHJKals3X+Eo2cuShAWQuSbhGBhVeKjEgt8jkql4tOvvqJBw4bcuH6dZ596itTU1GKoTghhbZJTUtkTdIK09DTuRN6VICyEyDcJwUIRZ2KqZ/m4Uce6AGz6YRdXj90o8PWcXVyY+8MPVKxUiWNHjvDmyy9jMBgsUqvS7Nt0ULoEIazSvQCcjtEIeoNBgrAQIt8kBBfS8cBgpUuwGkfPFf2HTfOejej+aAf06Xq+f2YJsREF3w2uYqVKzP/xR1xcXNi4bh1z58wpcl2WFFq1rdIliEKq3a+l0iWI+9wfgE0kCAsh8ktCsFDM/d3gUa8OpI5/DWLuxLFw5lJ0aboCX7NegwZ8PncuGo2G33/7jbDQUEuVqyjpBguR1aETZ0lNyxqATfQGA2ERUVy9ebvkCxNC2AwJwUUg3eB7CtsNzhyENXYapn0zDveKblwOCmbFx/8W6podu3ThvU8+4efff6eSr2+hrlFcitINliCsLOkGW5f6taujVuf8I0ylUuFgb0eVit4lXJUQwpaojAq8X+Th05Ruo1eV9MsWixYBNZQuwWq0aqgq9LmN3a+b/3z12A0+H/c9+nS9JcoCwGDUo1ZpLHa9vGjtNAyc0ZMBT/fI9Rjfm4dKpBZrknZ4v9IlWMSVjceULkH8JyzyLkdOX8gy/69SqXC0t6OTf3McHewVrE4IYS2mxaYQFBSU7XHpBBeRdIPvKcpscOaOcK2W1Zjw4UM4ulhm44sI43WOs5k0Y7JFrvcgunQ9/3y5mcP/nsj1mLI4HyydbGFplbw8ad2kvrkjLAFYCFEQsmOcsKij54xF6gibBAxrRbuhRX/7Wa/X8/j48aQGJZLQ8Co///47LuXKFfm6edmxeD9/vreW315dSeU6FalSv1Kxvp4oWbX7tZRusBUxBeEjp87jIAFYCFEA0gm2AOkGW8b9N8qpVKoi/6fVavnq22+pVr06F86d4+XnnkOv11vk2rn91/3RDgQMa0lacjoLnl5MYmxSjp+vdIOFsIxKXp50bN2Uzm0kAAsh8k9CsLA4S41FWIqHpyfzFy3C3cODPTt38vG77xbr0kkqlYpx742gaqPKRNyI4qfn/8x1zWIJwrZJbpKzPu5urjjYSwAWQuSfhGALkW6w5RRHEK5eowZff/cd9vb2LP/9d35btMjir5GZvaMdT84fj4uHM6d3XWDdN1tzPVaCsG2SICyEELZNQrAoFkXdQKM4gnDL1q354LPPAPhhwQJioqMt/hqZefl5MvXLh1GpVfw7fzvHt5wp1tezNaUhCAshhLBdskSahcmSafdY4ga54rBl5fc0aNmJqrUbF9trZF7ybdPCXaz6bAOOLg68+td0KtX2yfGcsrhsGtj+0mlyk5wQQlg3WSJNlDhLbKdcHHqPfCJLANalpxXr6/WZ2oXW/ZuSkpjKgqcXkxyfkuNxZXEsAmy/IyxjEUIIYZskBFuYzAZnZa1B2GTbqh945RF/4mIiLXrdzOMcKpWKCR+NpHLdioRdjeCXl1fIjXL3sfUgLIQQwvZICBZlli49jS0rv+fWtXPMef4h0lJz7tAWVuYg7OjiwJPfPoqTqyPHt5whaN1Ji75WaWDLQVi6wUIIYXskBBcD6QZnZa3dYK2dPS99vQbPin5cOLGf796ZkmuHtrAyB+GKNbzoOi4AgDvBEbmeU1a7wUIIIURJkhAsyjRP78q89NU/ODqX48DmP1nx3dvF+npaO02xXl8IIYQQ+SMhWJR51es2Y9bHy1BrNKz+6SN2rvlF6ZKEEEIIUcwkBAsBtOjQl0kvfg3An/PfJCU5UeGKhBBCCFGctEoXIIS16D3yCVKTE2nddTCOTi5KlyOEEEKIYiQhWIhMBj06O8vHep0OjVb+byKEEEKUNjIOIUQOjEYj65d+xTvTupOakqR0OUIIIYSwMIuFYL1eT8uWLRk0aJClLimEYpIT49mwbB6XTgby7ZuTLL50mhBCCCGUZbEQ/PXXX9OwYUNLXU4IRTmXc+Plr9fgXK48h3b8zR9zX1W6JCGEEEJYkEVCcEhICP/++y9TpkyxxOWEsAp+tRrx3KfL0Wi0rFs8hy0rv1e6JGEFdOk6Lp++itFonZvACCGEyB+LhOBnn32WTz/9FLU698stXLgQf39//P39SU2+a4mXFaLYNWnbgymvLwDgl89mcXz/JoUrEkrSpev4a+Fq/l2ygd1r90oQFkIIG1bkELxu3Tp8fHxo3bp1nsdNmzaNoKAggoKCcHDyLOrLClFiug2ZxLDHXsWg1/Pr58+h1+mULkkowBSAw29FYDQYOXXwjARhIYSwYUVe+2nfvn2sWbOG9evXk5KSQlxcHOPHj2fJkiWWqE8IqzD6qXfAaKTHiCmyZFoZlDkA63V682OnDp4BoMvgTkqWJ4QQohCK3An+6KOPCAkJITg4mGXLltGjRw8JwKLUUalUjJn+Ht6+1c2PyYoRZUNOATjzc9IRFkII2yTrBAtRQEajkdU/fcwXzz9U6NGI9FQZqbAVkaFRhN24ky0Am5iCsF5+KRJCCJti0RDcrVs31q1bZ8lLlml6vYG/v1/L5VNXzY+lp+lYMe9vrl+4qWBlZVtMVBj/Lv2So3vW8esXzxWoA1i9iR8Ah9YclyBsIypVq0jPh7qjtct5DMbO3o6RTw5Hq9GUcGVCCCGKQjrBxeR4YHCRr5GWksadm+H88eUKLp+6SnqajuXf/MWZg+eICpMVNpTi4eXL81/8hdbOni0rvmP90q/yfW6TbvWpUr8S0WGxBK4+mutxoVXbWqBS22PfpoPSJeSoSdtGdBvaJVsQtrO346EnhlGpakVq92upUHVCCCEKQ0JwMSpqEHZycWTCyw/jXdmLJZ8u46Npn3PpxBUGPz6AVl2bW6bIEnL0XOmal2zQohNPvf0TAEu/fpmD21bleuyZmHtzxGq1mv5Pdgdg4/c7c32LXVif+4Nw5gAshBDC9kgILmZFDcLOrs488vxoAAx6A43aNqB1txZFL0wBpS0Id+g7hrEzPsBoNDL/rYlcPHkgX+e17t8UnxpeRN68y+F/T+Z6nHSDrY8pCNs72ucYgKUbLIQQtkNCcAkoShBOT9OxdtF688cXjl3KMiMslDVk4ov0GP446akp/PLprFzng7N0gzVq+j3RDYCNC3bIKhM2pknbRjz19lTpAAshhI2TEFxCChOETTPAphGIl759Fu/KXuYZYVtU2rrBKpWKyS/Ppf/DM3nhy9WoVKp8nRcwtCWeld0JvRLO8c1nirlK22PN3WAAlTr377N0g4UQwjZICLZiBr2elORUBj8+gNbdWuDs6syElx+mop8PKYkpSpdXaKUtCGu1dkx4/gs8vSubH8upu5u5G6yx09BvWjcA1i/YkWsHuayORAghhBDFTUJwCSpoN9jByYHJr4/PMgPs7OrMlLcn0iSgkWWLExZhNBr589u3mP/mxAeOOXQY2ZryPq7cPHub07sulFCFtsPau8F5kW6wEEJYPwnBJaygQVitzv4tyukxW1PausEmEbeD2bhsLvs3LWPFgv9lez5zN9jOwY7ej3cB4N/526UbLIQQQpQg209TNsgSawiXBqUxCPtUqcmsj5eh1mhY/fPHbF+9KM/ju4xth4uHM9eO3+BC4JUSqtJ2SDdYCCFEcZEQrBAJwqVXiw59eezluQAs+mg6JwO3ZHk+czfYwdmeXpM6ARmzwbmRbrAQQghhWRKCFSRBuHR2gwF6jpjKkEkvYdDr+erlMVy/lPt6wN0f7YCTqyMXDlzhyrHrJVilKG7SDRZCCOslIVhhEoRLbxAe8/R7tO8zhuTEeH797Lksz2XuBju5OtJtfHsANnwr3eD72fJIhBBCCOulVboAYdtBuEVADaVLsFpqtZon//cjbh5ejJjyep7H9prUiW2/7OXUzvPcOHOLao2rlFCVorjV7teSKxuPKV2GEEKI+6iMud2SXow8fJrSbfSqkn5ZUUwsFYRbNczfRhO2zmAwmFf4aOx+b/xhxUfr2PrTXgKGt2Lyp6OVKq/Y+N48VKTz0w7vt1AlJU9CsBBCKGdabApBQUHZHpdxCGE1SutYhIlBr2fxnBf4+dOZOS6H1qRrAwCiQ2NKuDIhhBCi7JFxCCFKyK1r59iy8jvS01Kp5FebgeOzzgnnc8dlIYQQQliAdIKFKCFV6zThqbd/AmDp1y9zeMdqZQsSQgghyjAJwUKUoPZ9RjNm+vsYjUbmvTGBUydOKF2SEEIIUSZJCBaihA2d9BLdh04mLTWZZ6ZN41ZIiNIlCSGEEGWOhGAhSphKpeKxV+fTpG0P7kZF8fmHHypd0gOlxcVh0OmyPJYaE4PRYFCoIiGEEKJoJAQLoQCt1o5nP/mTkWPH8s5HHyldTp70qalsHjeOfS+8YA7CSeHhbBw1iiAbCPBClCapaWlERccqXYYQpYKEYAXpdfp8PSZKJxdXd956/33cypcHwGg05rh0mtI0Dg7UHDyY4HXr2PfCCyTevs2WceNICg+ner9+SpcnRJmRkprGnsMnOHD8DCFhEUqXI4TNkxCskMjb4WxZsoaY8Lvmx8KCb7F16Vrio+MUrEwoQafT8cMvc7nFeaVLyVHjadNo9dJLBK9bx6ouXUi8fZueixbh4++vdGlClAkpqWnsDTpBSlo6RqORE+cvSxAWoogkBCvEqZwzarWafWu2ERN+l7DgWxzcsAcHZ0ccnB2VLk+UsBPHjrFtxwZucoarkeeULidHNYcNM//ZwdMTrxYtFKtFiLLk/gAMGTtPShAWomgkBCvExa0cHYf1xM7enp0rNhL47y7Ke7nTYUgP7B3slS5P5ENEWBzPT/yZ00euP/jgB2jdpg2PPjwFgL2X13PsyJEiX9OSksLD2TJuHFoXF/x69iQpNDTLjLAQonjkFIBNJAgLUTQSghXk4laOOi0bmj9uHNBCArCNiAiL44VJP3PlXCjvzPrTIkF4YL8RVKQ2BqOeWU8+yY3g4KIXagH61FS2PPooSeHh9Fy0iO7ff28ejTj87rtKlydEqXYn8m6OAdjEYDBwKfhmCVclROkgIVhBYcG3OLX3KPZODtg52HFo054sM8LCOpkCcGx0IgaDkdSUdIsEYZVKRU2aU8W9FjHR0UyfMoWY6GgLVV14GgcHGk2enGUGuPG0afi/+SZ1x4xRuDpRUPGJSSSlpGZ5LDY+kdS0NIUqEnmpVrkiVSv5oFFn/3GtUoGDvR0BLRorUJkQtk9CsELCb4ZycMMeynu502vcYLqN7o+dvT371mwjLipG6fJELrIEYP29zozlgrCabvUGU79hQ64HB/PNF18UtWSLqDt2bLab4BpOnIhnY/nha0uMRiNBp85z4OgpcxCOiU/gwLHTHD93WeHqRE5UKhXNGtSmSkXvLEFYpQJ7Ozs6+zfHydFBwQqFsF0SghXi5ulOlTrVzDPAphnhSjWq4OxWTunyRA5SU9J5MYcAnPn5t2cu48aVos3nGVLVzF24kKEPPcTsl18u0rWEyEylUtGyUV3SdDoOHD1FWEQUgcfOoNVqaFqvltLliVzcH4QlAAthGVYbguOj44gOj1K6jGLj6OKEf+8OWWaAXdzK0bpXB7R2WgUrE7mxs9dSva4PWjtNjs9rNGrcPJwp7+lcqOtXqVcJeyc7gk/eJDE0jfc++YRyrq4AVrl+sMi/2v1aKl2CmbubK+1bNCYpJZXDp86TrtPRoWUTnJ1kVRprZgrCfpW8sbezlwAshAVYZQiOjYph18pN7P17KxEhd5QuRzzA8cBgpUsoEWq1ijfmjKZxy2rYO2b9RUWjUePhXY4vfplMeQ+XfF/zTEx1859dK5Sj1+TOAPz16Xpz8E1PS+P1F1/k7xUrLPBZKCe0atsinW/fpoOFKimdbobeIS4hMX8Hq1R5fyysUkYQrkPvjv4SgIWwAKsLwbFRMexZtQVdWjp6nZ7Af3dKEC5Djp6z7o6nnZ0mWxDOHIA9vIo2ytJnShfKebhw5ch1Tmw9C8CeXbtYt3o1777xBvv37Cny56CkshyEi7MbfPHaTU5euMK+I6ceGIRj4hMIPHYGJ0cHWjepj1aryTIjLKyfSn5pEcIirCoEZw7AJhKEyx5bCsJaO02RA3DmbrCTqyMDZ/QA4O/PN6LX6enRuzeTp01Dr9fz/IwZXDxvnbvK5VdRg7DI6uK1m1y+HoLBYESn1+cZhI1GI0fPXESr1dChZRMq+3jRvkVj0nQ6Tl24UsKVCyGEsqwmBOcUgE3uBeEwBSoT+VFWRiJMTEF49OMdLdIBzqzL2HZ4V6tA2NUI9q0MAmDWCy/Qd8AAEhMTmT5lCnfCyu7/F6QbfI8pAOsNBvNjeQVhlUpFmyYNsswAu7u50r5lE5o3qGPR2oQQwtpZTQi+ef4aBr0+1+cNBiNXTl4swYqEkqy9GwwZQXjslM4WCcCZu8Faey1DZ/cBYO03W0lNSkOtVvP+Z5/RsnVr7oSF8czUqSQmJBT5dZUiYxFFdyk4JFsANskrCLuWc852E5y7azkcZaMeIUQZYzXLEDQKaE7c3Rgib4dj0GUNw2qNGleP8rTu1V6h6kR+HA8MpkVADYtdzxaCcE5aNSz6vF7r/k3Z+tMegk+GsPXnPQyc3hMHBwe+WrCACaNHc/7cOb6bN4/nX3nFAhUrI7RqW3xvHir0+fZtOpB2eL8FK7It4Xej81w1xGA0EBufiFu5/N+oKYQQZYnVdILVGjUBA7riVdkHtVaT5XFXj/J0Gt4LO3s7BSsUIn8KG94zd4PVajUjXuoPwOYfdhMfldH19fD0ZP6PPzJs5EienjWr6MWWkNSkNFKTZEcyE0t0g9s1a0Q5F2fUOdwkpVarqVezGlV9fYr8OkIIUVpZTQiG7EFYArAoy+q3q02TrvVJSUzl3/nbzY9Xq1GDdz/+GCcnJ8C61xBOSUjlwKojfPP4Tyx46jcOrDqS5fmyPBZRVFqtho6tmmYLwhkBuCp1q/spWJ0QQlg/lVGBn6AePk3pNnpVrs8b9AYObthNSlIKnYb1lABsYyw5EmHLCjsW0dj93tbLty6E8d7gr1Fr1byzYTbe1StkOTYlJYU3XnyRPgMG0Kd//yLVa2lxkfEcWHWUq8ev0/3RDrh5ubJo9jKmfv0IlWp5Zzm2KGMRxc3aRy7SUtNYsWAVd+9Eo1KraNerDW26ty7W17yy8VixXl8IISxpWmwKQUFB2R63qk6wiVqjJmBgV7qN6isBWJRpVepXImB4K/TpelZ/uSnb85s3bGDzhg28/sILnDh6VIEKc5aWks7BNceJDLnL4Jm9adC+Dr51fLBztCM9NfsKMNbM2rvN9g72jHpqBL7VKxLQu22xB2Cwrh3whBCisKwyBEPGUj6yILhtKmvLpeXGErPBAEOe7Y2dg5agf08SfPJmlucGDxvGyLFjSU1NZeYTT3Dz+nWswYUDVzi79xKdRrfBr4Ev6ak6Dq89QbXGlanasHK246197WBbCMIjnxyBf7dWJfaaEoSFELbOakOwECKDp6873Sd0BOCvTzdkmQFWqVS89vbbdOzShejoaJ6eMoWY6GilSgUyxpkC/zlKwNCWVG/iR3pqOpcOXeXayZv41fcFcp5jliBseyQIC2E7jEYj569e5+SFy1Z9L0lJspol0oQojY6eMxZqNvhMTPUss8H9nujK3uWHuHjwKuvmbsXT1z3L8SN7TOL6petcv3aNyaPGM+ux17CzK751XzuOapP7kyqwc7AjOSEFgIP/HOP25XDKeTjTcZR/xiEqFXqdnnP7LpOemk7LPk2KrVZLKuvLsuWkdr+WMiMshJUzGo2cu3Kd4JBQAPR6Ay0a1i3z77hb5Y1xonSQG+QyWOIGOYAti3az8uP1uR6fakziFNtJJ4WqNMZP1bBQr5sf31/6OM/nw65G8ONzf+Do4oCXnwf129fGf0Az7BwyZvwNBgPn913myKbTBK07waCZvej9WGfAum+SA+u/UU4pEoTLltS0dJJSUvBwc1W6FPEAmQOwaXMdjVqNr0+FMhOEc7sxrsid4JSUFLp06UJqaio6nY6RI0fyzjvvFPWyQpR593eDuz/agcTYZGLD43I9p3FMbY5fCaRHiyFoNcq90VOpljfPL5lGSmIq5Tycs4RftVrNpcPXOLT2OA061KH/E9346YU/cXC2p8vYdkXeRKO4STc4Z9IRLjtS09LYE3SS1NQ0WjepTyXvCg8+qRSLionj7KVrtG7aAGdHB6XLySKnAAygNxgIDY8CKDNBOCdF/inp4ODA9u3bKVeuHOnp6XTq1In+/fsTEBBgifqEEP/R2msZNrtvPo60jk00nFwdcXJ1ZPuv+6hU24dGneqiVqsx6A2oUHFyx3ma92qEV1VPnl4wgQsHr5KeqsPOQaa0hLBWpgCckpqG0WjkyJmLtG5cr8wG4aiYOA4eP4PeYGBv0Ak6+Te3qiB8+fotroWEYshhe3VTENZqtTStV0uB6pRX5BvjVCoV5cqVAyA9PZ309PQy+xuFENYkKTGR555+msD9ynYtO45qQwU/DyBj6TS1Rk29drV4cv54tvy0h7CrEZTzdKF1/6YSgIWwYvcHYMh4d+fImYuERUQpXF3JyxyAAdLS0tkbdIKklFSFK7vH1cXpgce4uTiXQCXWySKrQ+j1elq0aIGPjw+9e/emXbt2lrisEKII/vrzT7Zt3szz06dz5dIlxepwcLanYg0vTmw7y5ZFu82P12tbi4o1vLB3krXAhbB2OQVgk7IYhO8PwABGrC8IV/KuQMuGGe/C3U+jVtOoTk2qV6mkQGXWwSIhWKPRcPz4cUJCQjh06BCnT5/OdszChQvx9/fH39+f1OS7lnhZIUQexk2aRM++fYmPj2f6lClERkQoWk/DDnU4f+AKq+dsQq/Tc2rnec7uvUR6qk7RuoQQD3brTmSOAdjEYDBw/uqNEq5KGfGJSQTeF4BNMgdhnV5f8sXloHJFr2xB2BSAa/iV3QAMFl4n2N3dne7du7Nx48Zsz02bNo2goCCCgoJwcPK05MsKIXKgVqv58PPPadK8Obdv3WLmE0+QnJysWD32TvbMXDSZkHOh/PziclZ+9C9j3hxCxRpeitUkhMifmn6++HpXQJNDR1EFONjb0bZ5o5IvTAEO9nY42NuR2+CnSq3G1cU5x6+VUjIHYbUEYLMif4ciIiKIiYkBIDk5mS1bttCgQYOiXlYIYQFOTk7M/f57Kvv5cfrkSV57/nn0CnYn7BzsePr7CTz8v6HMXjyVVn2byKLtQtgAlUpFq8b1qOjlmSXcqQB7ezuruyGsONnb2dHZvxmOjg7ZgrBarcazvCttmzeyuvujKlf0onXjejRvUFsC8H+KHIJDQ0Pp3r07zZo1o02bNvTu3ZtBgwZZojYhhAVU8PLi2x9/xNXVlW2bN7Nq+XJF61Gr1bi4O1Pexw3A6n5QlKRzRy+w8Y/Niv5iIkR+3R+Ey2IANnGwt88WhDMHYGvqAmdWybsCfpV8lC7DahT5VuxmzZpx7JisDSmENatVpw5zvv2WzevXM2zkSKXLEcC5I+fZtmonRqOR5MQUhkweiEajUbosIfJkCsLHzl4kMjq2TAZgE1MQNt0waO0BWGQn6xEJUUa0a9+edu3bK12G4F4A1qVn3BR469pt1vz8rwRhYRNUKhUtG9XDaDTmuOpAWWIKwsG3wqhT3U8CsI2R75YQZVBcXByznnySc2fOKF1KmXN/AAbQpevMQVhGI4QtUKlUZT4AmzjY21O/ZjUJwDZIvmNClEE/L1zIjq1beWbaNMJCQ5Uup8y4fPoK21btyBKATUxBeP2STQpUJoQQZY+EYCHKoKeeeYbWbdoQfucOz0ybRmJCQrG+3paf9rB27laS41MKfY2cVpGwtZUlnMs5Q64LK2Vw83AtmWKEEKKMkxAsRBlk7+DAVwsWUL1mTS6cO8cLM2ei0xXfphVbf9rDum+2kpJYuF2Urh0+yarXPyc90y5MF3cfYs07X6PPoatqrSrX8GXoY4PQ2mW/HUNrp6Vxm4Z0GdxJgcqEEKLskRAsRBlV3t2db3/8EQ8PD/bt3s1H775b7J3V+KjCdZyTYuK4EnjcHIQv7j7EmnfnEh8ZjS493cJVFq+qtf2yBWFTAO42tEuZXjJOCCFKkoRgUez0egNJCcrtVCZyV7V6db7+/nvs7e1Z8fvvbMlht0dLaNixLgC/vLyCtOS0Ap/fuHcnBr7yJNePneXL/pNZ/b+vqNSgFqM+eRkHZydLl1vsMgdhrZ1GAvB/9HoDB0+c5cK1srH9rhBCWRKCRbE5HhiMTqdn6WfL+Hr2fMJDIpQuSRFHzxW+u3omproFK8lZi1at+PDzz3lkwgR69ulTLK8x5o3B+NTw4taFMH7/3+pCdZwb9+lM3Y6tzR8Pf3c2Di7Oliwz3+zbdCjyNarW9mP4lCG07dmm1ATg2v1aFvrcjAB8hsjoGK5cv8X5K9ctWJkQQmQnIVgUG4Nez/dv/sqNSyGkJqex6L3fJAgXQkkE4T4DBvDKW28V2xq1Tq6OPDlvPHaOdhz4+yj7VhzO8/jQqm2zPXZx9yGuHLi3Mc+6D+ZlmRG2RVVqVqZtD/9SEYCLwhSAo+MSMBiM6A0Grt68XaQgbDQaOXs5mKTkezdjGgwGzly6Rkpqwd+NEEKUPhKCRbEw6PUcWLeLqLAIdGkZNy6lJqVKELYB0XfvMuvJJ7kRHGzR61apX4lx7w4D4I931nDjzK08j88chE0zwJUa1GLWuh8Z+OpTXD92NtvNciXJEt3g0qig3eCsAdhw7/EiBuGk5BRu3L7D/mOnSUpOwWAwcOTMBa7evE3E3ehCXVMIUbpICBYWlzkAG3RZF/6XIJx7EDakp3Hl+Ye4/f072Z4riW6wybyvvmLH1q1MnzKFmGjLhoX2w1vTeUxbdGk6vp+xlMTYpPydqFJRuXHdjBlgF2ca9+nMwFeeRK3RQBnvogJcPHGZi8cvKV1GoRw5c4HouPgsAdhEbzBw5eZtgkMKvpa1i7MTAS0bo9Pp2XfkFHuCThIWcZfGdWtS1beiJUoXQtg4CcHC4q6eukTk7fBsAdgkNSmVP7/+q4Srsm6G9DSuzBpCXOAWwhfP4fa3b2U7pqSC8OyXXqJ+w4ZcDw5m1lNPkZpq2U7rmDcHU61xFSJD7vLryytyDD8mpm5wvc5tePirN7PMADfu05mRn7yMnYO9ResrCGvoBp87cp7Ny7eyecVWzhw+p3Q5QMG6wd4e5VHlsXayCijvVq5Qdbi7lqNd80akpKURl5BIzaq+1KpauVDXEkKUPiqjAqvNe/g0pdvoVSX9slZNmxiOzqkCqItnJrMkpaels2fVFhJi4jDoswccOwc7xr0whhoNqilQnXVo1fDeD31TAE44sR9jasYqGmpHZ3wemUXlp99VpL6oOyG8Oakj0RG36dB3LNPf+7XIW6Q2dr/3tnbkzbt8MOwbkuJSGPFif/pO65rreb43DxXpdUtC2uH9irzu/Vswa+00dB/WjcZtGipSz/2ubDz24IPAPPagv+8XIo1aTUDLxniWdyvU65tGIMIi7gLg5OhAh5ZNcHZyLNT1hBC2aVpsCkFBQdkel06wFXC5HUSjxT2ouWE6GGxn4f/c2Nnb0XlEb8q5u6HWZP0rptFqynwAhntjETkFYABDShLhv3+dY0e4JFSo6MdLX/2Do3M59m9axorv3i7yNTN3sr2qejLp09EArJ6ziYsHr+Z6Xk43yYnsARhAl65nx+qdVtMRzq9aVSvToHZ1NJl+0bJkAG5ctyad2zRHp9ObZ4SFEEJCsMJcbgdRa+3jqPVplAvZT80NM0ptENZoNbQf3J2YmNzf/i5Ljp4zEvzmRBJO7MsSgE0MKUncWfoV4X9+q0B1UKN+C2Z99AdqjYbVP33EqYPbinzNzEG4ec9G9HuiGwa9gR+e+4PY8Lhcz7P2IFzSYxEXjl3MFoBNTEH43JHzJVpTTgoyFmEKwmq1usgBGCAlNY3o2Hga161JraqVcXctlzEjrNcTHRdf6OsKIUoPCcEKMgVgjS4jAGl0KaU6CLcf3B2vyj5AxhrCAlxbd4W85iFVapzrNy+5gu7TomM/Jr34NUMmvkjjNt0tfv0hz/amXrtaxEXE8+Nzf6DPZY5cZHU34kE3LKq4G24dKyAUNAg3b1CHDq2aFCkAAzg7OdK9XassM8DuruXoGdCaKhW9i3RtIUTpIDPBCrk/AGem1zqS4NeBa/3ngVqbw9m2RZeuIyUxmXLurtmeaxFQo+QLsjJ+J77l1txXs3WD1Y4u1P5mLa6tOitUWfHJPB8cGxHPB8O+ITY8nr7TujLixf65npfbfLA+XYfGTvvAx4pbSc0GG41Gdq3Zw+nDZ81LEJpo7bQ09m9It2HWswFHfmeDhRCiOMhMsJWpsfGZHAMwZHSEXW/sxvPCmhKuqnho7bQ5BmCwro6wTqfnyulrJf66Ic2fpsozH6FyuLf9r7UG4JjIMOa8OIroyIIvWZVZ5rGI8t6uTPnyYdQaNZsW7uLEtrO5npfTWETUjdv88Ohsrh0+aX4s7OI1fnh0NrfOXCxSndZKpVLRdUhnmrRphNb+XtC3xgAMRdtJTgghiouEYIVc7/05em3OdygbNI4k+rYhut6gEq5KGdYQhHU6PX/MWc6ST5exdfnOEn/9e0HY0WoDMMCvX8zm8I7VfPbcMFKSE4t0rcxBuF7bWgx7vi8AP7+4nIgbUfm+jnN5VxzLubDq9S+4dvgkYRevsfyFDwEoV8GjSDUWVEnOBt8fhK01AAshhLWSEKyQhKoduTZgQbYgbNA4klipJVcH/4BRY9n1T6Nuh+d4I42tSk1J4/qFG0W+jikAX79wE6PRyMFNhxUJwj5jp1P9zR+o8+0GqwzAAJNf+hqfKrW4du4oc18bj0FvuRnePlO60KJ3I5LjU/j+maWkp6bneNz93WCn8q6M+eI1KlSrzIqXPua3J17H3tmJh796k/KVSvfspykIN+/QjGbtm1h1AJZusBDC2kgIVtD9Qbg4A/DV0xfZu3obe1dvtcogXNBucGpyKj+/v5hfPlxK0PbCzxtmDsCm2cr0tHRFgvDRc0Y8+42lXLP2Jfq6BeHm4c3LX6/Bxc2Do3vW8duc54t0vczdYJVKxcSPR+FdrQI3z95my097830dp/KudHvyEfPHAeOGKhaAS3qlCJVKRecBHegyqJPVBmATCcJCCGsiIVhhpiBsUGuLNQCf2XcMo9FIXFSMzQfh1ORUfv5gCRG3IzEajGxcuqVQQTinAGxiCsLbVuws8HWLIq9tla1F5Rr1ef7zv9Da2bPpz/ls+OObIl0vcxB2dnOiz5QuAETmMRJxfzc47OI11rz7DXZOjjiUc2bb3N+yzAgLIYQQ95MQbAUSqnbk/CMbizUAm5aeMugNVh2EHyRzANanZ3xOujRdoYJwWHAYV08HZwvAJulp6exff5DUlLQi113aNGzVmSfe+gGAxXNe4MrZ7HfdFpZaW7B/liKu3mD5Cx9i7+zEYz99wrQlX1KhWmVWvf4FN47nfpOdEKLk3L9BidFolE1LhOIkBFuJtPLVij0Am9hqEM4pAJvcC8JH8309vzpV6De+N3b2OS+jZedgx6MvP4yDo2W/L6VFp/6PMOrJd3ho2lvUatja4tc3AkeORvCgVRzdKnpRw7+peQbYNCNcp2NrPKv6WrwuIUTB3AwNZ8fBo4RHZaxdbTQauXDtBjsPHSMhMUnh6kRZJiG4lDIajZwLPJHr5gMGvYHYiGgiQsJKuLLCu3L6GuEhEdkCsIkuTce2FbseGJoya9fHn15jemQLwnYOdrK9cz6MmPIaD019o1hmUY8cCWfMmE18+NHRPL+nDi7ODHlrZpYZYKfyrgz938wSXx1CCJFdRS8Pyjk7c/jUOcKjorlw7QaXgkOo4uOFi7PTgy8gRDGREFxKqVQqOg7tiTaXzQI0Wg11WjakUo0qJVxZ4TX0r09A37bY2dvl+LyDkwOTXhtX4EB2fxCWAFw4UWE3+frVR0iMjynahf4LvDduJGA0wh9/XHxgELZV6Wnp/PntX2z7a0ep/PyEALC3s6N9y8aUc3bm4ImzXAoOoZqvD80a1LH6mzlF6SYhuBRz9/ak0/Be2YKwRquhVrP6NApoblP/AKlUKnqP7U6bXq2zBWEHJwcee/NRKlb1KdS1TUFYa6eVAFxI3783jcAtK/jypdHo0gs3R200Gvn776sA6HQGAJKT9aUyCKenpbPyu78JDwnn3NELbF0pQbigEpKSOXs5OMvXLTY+kQtXb8jX8gF0en22r5HOgkse3s9Oq8Wj/L1Nkyr5eNnUzx9ROkkILuXuD8K2GoBNcgrCRQ3AJu36+PPKwueLFID1Oj13w6OzPJaepiMmMrZItdmCqW98R/kKFTlzeAc/fvhUgUOI0WjkjTcOcux4ZLbnSlsQNgXgyLAo9Do9unQdF45flCBcQGERUVy5cYuT5y9jNBqJjU/kwPHT3Ai9Q5oN3e9Q0tJ1OvYdOcX5TL8spKSmsvvwca7cuGXx1zPNAF+/FUYlb0/cyjkT9N9ohBBKkhBcBpiCsMZOa9MB2CRzELZUADbRajVFOn/j0q38+PYv3LkZDmQE4D+/XslP7y8mrZSvMuHtW50X5/yNvYMTu9b+xt+LPirQ+cHB8fy5/DLp6YYcn09O1vPzz+cIC7PtG2nuD8AmEoQLrna1KtSt4ceN0HD2Bp3kwLHTaNRqOrRsgkMuY1MCtBoN7m7luHw9hPNXb5CSmsr+Y6dJSU3D3S3nLe6L4sbtO+YRCP8mDWjfsol5RjhebowTCpIQXEa4e3sy4PGHaNy+hU0HYBOVSkWfh3vw4rfPWiwAW0JA3zZotFp+/Wgpt67e5vcv/uTyyat0G9YJ+zKwykTtxm145oMlqFQqVnz3P/Zu+D3f59as6cYH77fDzi7nf5YcHTV8Oacjvr4ulipXEeuXbCQyNDLHm1Z16TrOH7tA0M78r3JSlqlUKurXrEZFL09i4hNI1+lo37KJ3Gz1ACqVimb1a1OtckUuXw9hy74gUlLTaNe8MRXc3Sz+elUqetGwdnXzDLBpRrhBreqUk++VUJCE4DJEoylal9MaFbVzaxIfk5Ct+xYfk1Dg61So5Mmk18ZhNMIP//uFa2evo1KpSMtlC+DSyL/bEB6d/TkA3787ldDrF/N97pgxdRk8qEa2xx0dNXz8UQCDB9e0VJmKadCqPip17v/0ajRqatSvnuvzIqu4hCTuxsaZP75yPUQ66fmgUqmoV7Oq+ePy5crhWd7yXWAArVZLnep+WRow9nZ21K5WRdGmTHRsPCmppfsdOpE3CcHC5qSlprNz1R50mTppKUkp7Fq9F70+57fS8xIbFceC135g2/Kd5h+eIVduM++l7zm8reAducjbURgy1WY0Gtm6fAeBmw4X+Fq2qv/DM+n38DOMefo9KlWrW6Bz27StCGSEQShdARigfot69B7VI8eVW+wd7Bj55Ai8K3spUJntMc0Aa9RqegS0Mo9GmGaERe5SUlPN4yOuLs7cjY3LMiNc2t2JvMv+o6fYffg4ySmpSpcjFJLz+llCWCmj0ciVU1fZ+fcebgeHMXrmCHRp6Sz+dBmhwWHUbloLv9qVC3RNVw9XGrVpwN51BwBo4F+fxZ/8gbOrE/Va1CnQtc4fucifX/+V/a7rNB1bl+8AMkYmyoKJz88p0vktW3hx9piqVAVgk/ot6gGwZcV284Y1pgDsU8U7r1NFJndj48wzwC7OTtSvmXFT6+07kaSl62QuOBfpOp15Brhdi8Z4lnfl5IUrXL4egkatztIhLo3uRN4l6PQFDEYjaenp7Ak6QWf/5jg5OihdmihhEoKFzYgKu8vP7y8moF9bBkzsy/pfN7H4kz9ITU4lPCSC0c+MKHAABlCrVQyY2A+AvesOsHfdATx83Jn02njKV8j/fNylE1f485vsAdjEFIRVKmjXp2wEYZM7IVf4a+F7THltAfaO+ZsBrFXLjWOLhlKuXOkMMqYgvHn5NjQatQTgQqjp54tfJW/stBk/ykwzwrWrVTE/JrLTajRU9vHC29PDPAPcrH5t7LQaKnqV7g1mzAHYkPGuodGIBOEyTMYhhE2ICrvLj+/8SkJcIrv+3ktaSho9R3fj+vkbhF2/w0NPDaVB63qFvr5araJF52bmj/3qVMHNM//zceYAbMj7rUR9up696wLLzFuOkNG9/+a1cexZv5T5b00y//DJj9IagE3qt6jHkEkDGT19pE0F4Kg7d9m4bAupVrDiyf1hV6VSSQB+AJVKRYNa1bPcBKdSqWhUpyblXcspWFnxuj8Am2QOwjIaUbZICBZWzxSAkxOTwZixxNTOVXs4vPXevO6JfaezzAgXVMiV2yz+dBnu3u7UbVGHU/vPZJkRzkvwuev8+c1f6NIevC6pg7M9k18fXypW6MgvlUrFk/9bhHO58hzavorfv3klX+elJZeNmwmr16uGV6UKSpeRb1F37rJ8/kouHr/E8m//soogLER+XLh2M9dfwo1GSE/XcetORAlXJZQkIVhYtfsDsIkuXUfc3TiadWrCgIl9uXjsEsu/WVWoIBwTGWueAZ78+ngefm4U/j1asnfdAQ5uzsfNbCoV+Y20Gq0WDx/3Atdo66rWbsxzn61Ao9Hy75Iv2bLyu1yPrdksYx7x+LazxEUVfIUOUXxMATg1JQ2DwUBMZLQEYWEz2jZriKODfY5NCI1aTSVvT2pXq6JAZUIpEoKFVTkeGGz+s9Fo5Kf3FmcLwJmdPnAWd6/yDJjYl6tnrhH+3yYVBVG+ghtdhnY0zwCbZoR7jOxKk4DGDzy/RoNqjJ71EFq7vJdr09pp6TyofZnqAmfWpE13pr75PQA/fzqLY3s3ABD17xIOv/++uetepX4latVzJT0lnS2LditWb1HYt+mgdAkWlzkAm+h1BQ/Ctfu1LK4ShciTo4M9nf2b42Bvl+XfYY1aTUUvD1o1rl9m/30uqyQE24DbV29m223s1uUb6NJK99vFKpWKdn39sbPLeS5Uo9Xg4eNO1bpVaNurNTM/f4rKNX0L9Trt+7fjxL7ThN/KeCtMrVbRZWhHypXP38YMdZvVZsyzI3MNwlo7Lb1GdyOgX9sHXuvoudI7L9x10ARGTHkdo8HAN689wt2I2yRfPsX5X37h8HvvZWyvungxrhdWA7BraSAJdxNzvFZo1Qd/LYVlxN6NyxaATTIHYb2+8CNJQpSEzEEYJACXdRKCrVxyYhJBm/ezf812cxC+cuIChzft5fLx8wpXVzwyd4O7DOlI5yEdsLtvqSONVoO7d3kef2siTi4Zqw24eRRuoXeDwcjqhWvZtXoPi979jfCQws2E5RaEtfb5D8AmpTkIj3zif3QeOJ6Hn/kQT+/KVJn5MT7jnuXCb7+xtEEDDr3zDg17tqJRpzqkJqWx9Ze9SpdcKKWxG5ybgt7nKd1goSRTEHZycKCil6cE4DJMQrCVc3Jxpm2/TsRFxbB/zXbOHzrFqb1H8K3lR73WD36rvjToMjRrEM4cgJ3LFW3LTVMAPnf4Avp0PalJqSx6r2hBuOeY7mQeEnZ0dqBFl2a5n1TGqFQqnnr7J/qMesr8cZVnP0Xl4Ijxv05il2++YdDM3gDs+G0/ibFJOV5LusElo7ynG6Onj8Qhh62/1Ro1Ht4ejH5qRIF2pZQgLJTk6GBPj/atadW4ngTgMkxCsA2oVKMKbft3JibiLucPn8KrSkXa9OmEWmP93z5DDju45fTY/TJ3g+FeEFZr1MUSgNMzjZYUJQjfvHyL7St2ZZlhTk5I4af3F5OSlFKga5XmbnDmHzq3rp1j/pRu6FPvfX2OfPIJtVpUo0GHOqQkprL9t/1KlFlkpakbXKGiZ7YgnDkAOzjJ+qrCtqjVKgnAZZzKqMCCpR4+Tek2elVJv6xNu3LiAqf2HgHArYI7nYb1wj6Hrow1uX3lJmcPnqDjkB44lXMG4Pq5K1w5cYGOQ3vg4ORY4GuGXguhgq+PRT73o9sOcOvyDfS5rCihtbej26i+lHPP34YZd8Mi2ffPdvS67EulabQaKvh68tgbj+LonP/Pu1XD0v0PtF6n47n+1YiIjqCtb3VmrjxN2qJZnPvpJ+pPmIB7v/F8MW4hzm6OfLjzFZxcC/53pqh8bx4q0vlph20zwOfGdINcerrOIgH4ysZjFqxOCCGyMhqNPBGXSlBQULbnrL+VKMwB2LeWH237dyYhOi7LjLC1cnRxIiUxmb1/byU5IYnr565wbPtBHJ0d0doVbjF735p+FgnARqOR2KiYPNcBNuj1GStT5NOxHQdzDMAAep2eqNtRHN52NMfnc1Oau8GQsWTc2K6D0arUHAq9zsYVC2j96qs0fOwx0mJiqNO6OnXb1CQpLoUdSw4oUqOMXGRl6gjXa1ZHOsBCCKsWFRPHpj0Hc31eQrCVS0lM5mzgCXxr+dGmTycq16pK2/6diYuK4drpi0qXlyfPSl50GNyd1JRUNv26mmPbD+JTtRLtBnRBU4AdnYxGI7GR0RatTaVS0WlYL8q5u+U4VqLRamjdqwPeVSrm+5pt+nbKdgOfidZeS5U6VQjoW/BAVdqDcIc3vuep934FYOnXL/Pr6uO0fvVVOnz2GWqNhoEzegKw9ec9pCTa3m5OpWkkwqRCRU/6PdzHIgFYZoOFEMUhKiaOg8fPkJ7H/gFFDsE3b96ke/fuNGrUiMaNG/P1118X9ZIiE0cXJ7o81DvLDHClGlXoMrIP9Vo1Uri6B/Os5IVf3ermj5t1aVPgAHxm/zF2/LmBswdPWLQ2O3s7Oo/onS0Ia7QaWvVsT5U61Qp0PTfP8nR+qE+2IKy111K5pi+PvvQwdvaynWtOOvQby5jpGWsFz3tjAqdPnkT9301WDdrXplbLaiRGJ7Hr90BF6pNucPGSIGx7DDlsEZ/TY0IowRSA9bnsEGhS5BCs1Wr54osvOHv2LIGBgcyfP5+zZ88W9bIik/JeHtm6le7enqjU1t/Iv37uCsFnLqO1t0OlVnNg7Q6SE3K+0/9+pgB87fQlAK4cP1+8QVitLnQANrk/CFsqAJf2bjDA0Ekv0X3oZNJSk3l62tPEx8cDGV17Uzd4y6I9pCVb9xhQTkpjN1iUXbHxCew8dJTY+HtreN+NiWPnoWMkJOV/hEyI4pDfAAwWCMG+vr60atUKAFdXVxo2bMitW7eKellRCty8cM08AtF/8nA6D+9Fakoqe//eSsoD/qHMHIBNN67pdfpiDcJeVXxo1avwAdjEFIQdnBykA1wAKpWKx16dT4uO/Rk7/X1cXe+t+9y4cz2qN/UjPiqB3cuKdqNaYUk3uHhJN9h2aDUa9HoDB46fJjY+kbsxcQSeOAMY0drAqkWi9EpNSyfw2Ol8BWCw8OoQwcHBdOnShdOnT+PmlvWO+oULF7Jw4UIAzl4Ipe/EnZZ6WWGlYiOjuXT0LC17tDOPQNwNi+TamUu07NYu1yXecgrAmWm0Gmq3aECjds2Ltf6iSktJpXWnOmi0+V879UFK+2oRkPH9Ny1b1Nj9uvnxE9vO8u2Tv1Hex5UPtr+EnUPO89fFSVaKKF6yUoTtSExKZv+x06SkZrwz4+LsSIeWTXB0kBslhXKMRiPHzl4kLOJuliC8tnXrHFeHsFgITkhIoGvXrrz++uuMGDEiz2NliTSRl4SYOLYuXffA4/pMGIqza/62NVZKi4AaSpdQaKkpaWjttGgs1NkpTIDXhm5k/dq1PPviiwB8MPQbbp4LZez/htJ9fHuL1FVQRQnCEoIfTIKw7bgZGs7xcxnjau2aN8KngofCFQmRcxDOLQRb5D3a9PR0HnroIcaNG/fAACzEg5Rzd6Npp9acDTyeaye4aadWVh+AbVl0RAw/vv0r3pUrMP6lsYVe0q4o0lKSeXLSFGKj7uDs7MwTM2YwYHpPvp+xhK0/7VEsBIdWbVvoIGzfpoME4Qew5bGIshTg78bEceriFUx7TRw9e5H2LZpQXv5dFgpTqVS0bFQvx47w/Yrc4jEajTz++OM0bNiQ2bNnF/VyQgBQu3l9GgW0yDZKYArANRrXVaiygrl/5ztbkBGAfyEpPomQq7dZ8ukydOk5r39cnOwdnZjy2gJUKhXzv/qKdatX06RLPQBiw+NKvB4hHsSWA3xBRMfGE3jiDI4O9vTq4E/3dq3QqNUcOH6auITEB19AiGJmCsKVvD3z3BWwyCF43759LF68mO3bt9OiRQtatGjB+vXri3pZIbIFYVsLwLboXgBOxmg0okvTKRqE/bsO5qU33gDgrVdf5UjQ4RKvQQiRlaODPRXcy5tngF2cnejQsgmebq44Olj3Tqai7DAF4ZaNcs8MRQ7BnTp1wmg0cvLkSY4fP87x48cZMGBAUS8rBHAvCAMSgIvZ/QHYROkgPG7iRMZNmoQuPZ0XZ80k2Vg6u8AXjl8kJSkly2Pnjpy3+p0hRdnj5OhAu+aNstwE5+LsRNvmjbC3K/kbVoXIjUqlokpF71yfl7VMhNWr3bw+/SePkABczFbO+5vE+KQct5LWpem4efkWe9cps3XxC6++SvdevYiLi+Uc+zAYc98ByBbFxySwefk2/lr4jzkIH95xhE1/buX4vpMKVyeEEKWThGBhExycHZUuodTrO653rts+qzVqXD1c8e/RqoSryqDRaPhozhxatm5NNZqgVllu2Tlr4OpejsETB3A3/C5/LfyHPf/uY9+GA9RvUQ//bsp8zYUQorQrVSHYaDQSei0Eg750dYmEKAnV6vllbOxx3/q7ao0aN083pvxvIuXKK3fnt7OzMwt/WYyXqqpiNRSnGvWrM3jiACJuR3Bk1zGq169G3zG9cl1PWwghRNFY3b+uKYnJD9xNLCdGo5GTu4M4tGEPgf/uliAsRCHcH4StJQCbZL7L99SJE3w/b16+zjPo9dnGPKzx34iI25HmPyfGJpKWKvPAQghRXKwqBCfGJbD9z/VsX7aexLiEfJ9nCsA3zl/FaDQSFRouQViIQjIFYa2d1qoCcGY6YxpPTprE/K++YtnixXkea9Dp2Pvssxz77DNzENanprJj2jROLVhQEuXmy+EdR8wjEEMnDyI6MibLjHBRpaWmER8Tn+WxlKQUEuOTLHJ9IYSwNVYTghPjEti1chNpKWmkpaSxa+WmfAXhzAHYtLGCXqcvs0E4JSk5W8crJTH7Y0LkpVo9P5784HGmvTtZsQAcF7gFXUxklsdu7dwJgFZlz8tvvQXAx++9x67t23O9jkqtxsHDgzMLF3Lss8/Qp6ay8+mnub1rF44e1rHDVUJsAoe2BVG/RT36julFzYY1zDPCpw6eschrbPxjCysWrCIuOmN1jZSkFFb98A+rF63BkMdi8kIIUVpZbNvkgrh/2+TMARhTOSoV9o72dB3ZFxe3cjleJ6cAnJlGq6GCrzcBA7ui1pSuG2lykpaSxo4/1+NT1ZcW3duiUqmIvxvL3tXbqNm0Lg3aNFW6xDLJlrdOtqSCbJusi73L6SF1UFWoxNEavly4coW27u5UPXueAwzHzkHLvNPvs+Cbb1jwzTc4Ojnxyx9/0KhJkxyvZzQYOPT221z8/XfzYwHvv0/dsWML9bkUx9bJUWFReHh7ZJkBjgyNpELFCqjUBd9y+n53bt7hrx/+wdHJgUETB7B1xXaiwqIYNGEANRvWKPL1RYaytGucELZiWmxKjtsmK94JzjEAAxiND+wIp6emcf3slRwDMGR0hMNDwoiPLp3rit7PzsGOag1qcf3cFY7vOERcVAx7V28DFVSpU03p8oTIN215T5xnfkzqjUu03L2bd27dosaZM4SQNQw++cwzDB4+nJTkZGZMncrtW7dyvJ5Krab1a6+ZP7YvX546Y8YU6+dQUBUqVch2E5yXr5dFAjBAxaoVeWjqUOKi4/n9qz8JvxUhAVgIUaYpHoLPBZ4g/f4AbGI0kp6SxrnAEzmea+/oQIch3bNtrWui0Wpo1SOA8l7W8ZZncVOpVDRo25T6/k24fu4K25dl7NzXaVhPXD3KK1xd/sRHx2b52Gg0ZntMlA3/7t+IK2AAzgAVgfrGrG/bq1Qq3v7gA9oEBBAZEcEz06ah02Xf0EOfmsqu6dMzztFqSYuNzTIjXFaUr5D134EKlTwVqkQIIZSneAhu2rk1Tq4uOXY7VGoVTq4uNO3cOtfzvapUpP2gbtmCsEaroXnXNlRrUMviNVszlUqFX93q5o/LubtSzt0NyAiY0XeiSrwmg95AVGjEA4+7eTGY7X+sJ+RSMJARgM8dPMn2ZRuIiYgu5ipt06FtRzh/9KLSZRSLmxdP0iDTx+lAhxyOs7O358tvv6VJs2bMeO45tFptlucN6enmGeCA999n3Nmz1HvkkYwZ4c8/L85PwaqYZoA1GjUBvdti72jPyu/+Ns8ICyFEWaN4CHZwcqTryD44lcsahFVqFU7lXOg6sg8OTnlvlHB/EC6rARgwzwA7ODtSpU41okIjOL7jEDERd9m1cjN7V28lIiSsxOox6A0Ert/FnlVbuHTsbJ7H+tb0w9PXm6AtBwi5FMy5gye5eOQM1RrUpLyXe8kUbEP2rt3Plt+3sXL+as4cOqd0ORbXurwHx4CawFggDtiWy7Fubm4sWbmS7r16ZXtOpdXi2bCheQZYpVbT9u23qf/oo3g2alR8n4CV2fjHFvMMcEDvtjw0dSgpyan88/M6uTFOCFEmWcWNcQCpySnsWrmZ5IREgHwH4Mwib93hwLpdNO/qXyYDcFpKGtt+XweqjBGIcu5unD90igtBp1Gp1Rj/+0Gn0WoIGNgVb79KxVqPKQBH3g7HoNOj0Wpo0LYpdVvmHjx06ToOrNtJ1O1wAKo3qk2Lbm2zrA9ra4rjxri9a/eza/Ve0tMy3vrX2msZ/sRgGrdtaPHXsqS8bo5Lj7qD1tMHlUqFLvYupwbXJiQ5iXpGAz2BzUAcag6pRqDWqPlk32u4Vcj5ptkjhw9z+sQJJk6ZUiyfR3HcGFfcosKiiItJoGaDe+8U3bl5h/Q0HX61qyhSU2kkN8YJYX1yuzFOm8OxijB1hHet3AxQ4AAMGR3hQVNHolIr3uBWhL2jPQ3aNsWrio95Bti3lh+Xjp3FoL/X6dHr9AT+u6tYg/D9Adj0uucPnQLINQhrtBrcvT3MIdirSkWbDsDF4f4ADKBL0/H392sBrD4I5yQt4jbnx7dF06Efm2OjuHnpFK2q1aVh8AXOV6/KL4mJ1GnQgHGDBhH9ewiXDgfzw8ylPPvLFDR2WUehoiIjeWryZFJSUqjg5cWgYcOU+aSsTIVKFahQqUKWxypWrahQNUIIoTxFO8FGozFbwNGl/9fZsrOafG6zYiOj2fP3VnRp6Tk+X1wd4ZwC8P2vm1NH2DQDfPHIGfzqVic5IYmosEj8e7fHr24Ni9ZYkizZCT6w4SDbV+7KEoAz09prGTl9GA1a1bPYa1pSbp1go9HI2VcfIXXrSuyBAGA3EIcKv2/WMHTAvWAfcyeOD4bPJS4inp6TOzH6tUHZrvf7b7/x8bvvYmdnx/e//op/27YW/1xssRssip90goWwPla3RFpsZDR7/95KSmLWLZK1dloJwBZy80JwnpuFGAxGrp6y/E1Vd+9EEn4zNMcADBkd4XOBJ9Hfdxf/zYvBXDxyhuqNatO6dwfaD+5Ohf9mhGMi7lq8zpJyPDDYYteKjojJ83kVEHc3Ps9jlHT0XM6/c6tUKjanpWAHpJERgMsDlTCy7q8fOBNz7y1894puPDF3HGqtmm0/7+XQmuPZrvfIhAk8MnEi6enpPPvUU1y7etXin0toVcsHa2H7avdrqXQJQoh8UiQE63U69v2zncS4BHPnV1heo4DmePtVynEJObVGjZtneVr3bG/x1/Wq7EODNs3yXLqu/eDuaO67i79KnWo06+JvngHW2mlpP6gbzbv4l5ll7h6k3/g+NGrbEDt7u2zP2dlr6TK0I2175b6aijW7eekUmf82+gE9gZuXTgJkCcJ1WtdgzBuDAfjt9b8IPhVCWnJalv9mPfcCnbt1Jy42lqcfe5w7t8KyHZOf//K6aaywQdi+TU7rXIjSQoKwELZBkZZrfHQsji7q/27eclWihDJBrVHTrn8XDm7YTeStO+ZNRdQaNa4e5ek8vBfaHMKUJTRok7Fz16WjZ7JsZpIxgtENb7/ss4gajYZaTbO+ja+101KzSd1iqbEkHQ8MtshYhFqtYujUjLf/zx46T/p/oy6mANx5SMciv0ZxO3rOmONYREPfauwOvU55wJuMtYEvAlXr5LzTYddHAgg+GcKBVUf4aMS8HI/RG8vjgju3Qm4youswGtO1wDPmlWp5M+uXx/H0dS/QeQ9iy0FYxjmEEKWBYuMQrXq2N69fK4qPKQh7VamIRqspkQBs0qBNE+q2apxl6brcArDIP1MQbtS2AXb2WpsKwCb3j0Wkhd/C/8IJ4lCRDjQk4zf0dKBv1drm4zJ3g1UqFePeHUaL3o2wc7TL8T9HJyeaOHbFWVWe6vZNsHeyz/XYnP7T2GkIuxrBd9OXkJ6a82x9WRyLsOUAX1KkGyyE9VPkxjiVqjLOrrPpOKwnLm45L3EkLMugN3Bo4x5SEpPpNKxnsQfgzM4fPs2FoNN0GNy9zAdgS94kZzAY2bB4Mx7e5ekwIMBi1y0pmbvBRqOR8MVziK/TmHV//cDNSyepWqcpfSr6UffxV7Hz8s1ybmP36wV6Lb1ej0aT83hOXhKiE/lwxDyiQqLpONKfRz98KNdOclFulLN2RoOBmNAIPKrc+/+vQa8nausWyntKMyM3cpOcENYhtxvjFAnBrp4NSU16FDt7ezoN64Wzm0tJl1Ammb7VSiw5pkvXyQ2PFM+awbYsr3WDH6SgQdgkcN8+wsPDGTJ8eL6Ov3HmFp+OWUB6qo5H3hlG10dy/4WjtAbhfb/+xeEV6xn96StUblQXg17Pvx8tIDjoFI/OGo1zOSelS7RaR5bv4erN2zSrXxttLvdJCCGKl1WtDqHVaukwpAf2Tg4Zt7OLEqFSqRRbc1cCcAZLrhRR1mUejciva1ev8vTjj/P2q68SuG9fvs6p1rgK4z94CIA/31/LlWOFC9+2rGn/bjiXd2P5Sx8TcuoC/360gHPb9tNm1ADcu/dUujyrdScknMBTZ7kdHsn+Y6fQ5bJijhBCGYrNBHv4VKDryL44u0oXWJQslT6NmuueoFLgl1Dyb4SITHJbMq241KxVi/GTJ6PT6Zg9fTqXL+ZvicCAoS3pMaED+nQ9389YQmx4XI7Hldb5YDefCoz98g2cXMvx+8x3OLdtP12mjCFg3FCgdMwIx8fEc/XsNYtd705IOCu//5v01HSMRiPxCUkShIWwMopurSY7gYmSptKnUWvtFFxD9uN94hd8A78o8SAs3WDLKUw3+NkXX6RX374kJCQwY+pUoiIj83XeyFcGUrdNTWLD4/l+5lJ0uWxYUlqDcLkK7rj63Ntxrtp9m90UNAiHh4STlJD84ANLQFx0HH98s5x/F2/k2N4TRb5e5gBsYpAgLITVUXTHOFtjNBrR6/RZ3to36A0YjcZc18QV1sMUgF3CjqPWpwCg1zoR2exRQgOehxL8pUxmg7MqymxwYaSlJPPek724fPoQtRv58+bCbTg4Oud4bObZ47jIeD4YNpeYO3F0G9+eh/83NNfXKE3zwaYZ4HPb9tNyaG+uHT5JUmyceUbYJL9LpwVfuM7aX9bj7OrE2GdG4aLgO4Jx0XEsm7uC5MQUjEYjWjstHfu3p2Wn5oW63t07d/lj3opcVxNRq1S4lnOms39zaQTlIDY+gfR0HV6e7kqXIkoRq5oJtlXHdx5i/9od5rVZDXoDQVv2cXDDbox5LKgvlJdTAAbQ6JLxOrm4xDvC0g3OqqTHIuwdnXj+i1V4V67BlbNBfP/utFyPzdxtdvNy5cl549Haadi55AD7/8r+j2ppcP+/Z/t++cs8AtH72cmM/fINnMu7seKlT0iKuTcakp9ucPCF66z7bQN6vZ7EuCSWzV1BYnyixT+H/Lg/AEPGTbz7NhwodEdYpX7AvRcqeRc0N9Gx8ew7eoqDJ88RFhGldDmiDJAQXAA+VX2JDovkwLqdpKWkEbRlH7ev3MSnqi8qtXwprVn1zbNxCTuWJQCbaHTJeJ/4Fa+Tv5VoTRKEleVeoSIvf70Gnyq16D3yiTyPzRyEa7aoxtj/OsBL31pN8KmQHM+x1bGIqOu3mDf8SQ4sWW1+rNWIvvR9Yap5Btg0I9xr5kSc71vvPa8gbArApp1CDQaDYkE4Ljo+WwA2MQXh4/sKHoQ9vD0Y9dQI7B3tsz2n0WpwK+dC+5ZNJAjfJzo2ngPHT6PXGzAYDBw5c1GCsCh2ktwKoEqdavj36cjd0AjWL1rJ7Ss3adKxFXVaNFC6NPEAcdU65znuYFSpSapYuLc/hWWUdDcYoErNhnzx12katupcoPM6j2lLpzFt0aXp+O3VlbkeZ2tBOOr6LZbMeJvk+AQOLFltDsIuHuVpPrB7lmPdfCrQuE/OX7ecgvD9AdhEqSB869ptUpNTswVgc116A6cPnS3Utb19vbIFYY1Wg3dlL9q3bIK2EGtWl2aZA7CJBGFREiQEF5BvTb8sH1dvVDuXI4U1udt4DLc6voJB65jtOb3WiatDfiKpUouSL0woTqu9t3HMkd3rOLZ3Q47H3X8T3ogX+wEQGXK3+IorQaYAnJqYCEbQpaZlCcIFdX8Q3rfhQJYt1DMzGAwkxidx/uiFQr1WYTRsVZ9WXVvmuHyjWq2mXHkXRkzNfeb7QcxB2MEetUaNd2UvHpo2jPoD/YtSdqkTHZc9AJtIEBbFTUJwAZhmgAE8K3mhUqk4sG6neUZYWLeoJo9kC8KmAJzo21rByoQ1uHz6IF+88BBfv/ow184ffeDxhdmBzlrdH4BNihqEMxsyaSBO5ZxQqbO/I6O101KzYQ1adm5R5NcpiI792tOyc/MsQdgUgMc+MwrncjnfLJlf3r5ejHp6BA1bNeChacOwy8dOnSmpaew6dJyQsIgivbatuBNxN9dufAYjt+6Uja+FKHkSggvg6PZA8whEl4f64N+nI9FhkQSu2yk3xtkIcxDWOEgAFlnUbtyWjn3HkpqcyKfPDiMy7IbSJZUIo8HA77PezRaATXSpaez7ZSVXAou2BbCruysPPzMKJ5esQVhrp6V6/WoMHN8PtQL3VmQOwpYMwCbevl70HtUj3wF4b9BJ4hMSOXH+cpkIwvVrVcPX2wtNDt97tVqNu6srLRrWU6AyURZICC6AqvVq0LTTvRlg04xw1Qa1ytyNcQaDgdtXbz7gN3jrFNXkEa73/IQrw37LEoDvhkWSnJikYGVCSSqViife+oFGrbsSExnKp7OGkJQQq3RZxU6lVtN2zCC09tlv5ALQ2GnxrFYFvyb1i/xa9wdhpQOwScd+7WnVpQXlK7hZNAAXhCkAp6SlYSTj39iyEIRVKhUtG9WlkneFLEE4IwCXI6BFYzSasvXzVZQc+ZtVABWrV6Z286w3wVWpU40aZWwu2GAwcHjTXg5v3EvQlv02GYRj6w7IciNc6LUQ9q7eys7lG0lSaLkmoTytnT3PfbaCyjUacPPKGb58aTS69DSlyyp27R4eTMeJD6F1yBqENXZaPPx8GffN/3CwUDA0BWHncs7UsIIAbNKhbwATXxyvfADO9O9pWQ3CEoBFSZG/XaJATAE4/EYoRqORsGshNhuETUKvhRC0eR8GvYG05FR2rdwkQfgBYiJj2f3Pvizf98jQKPavP6hgVZZRzs2Dl79eg5unD6cPbefXz59TuqQScX8QzhyAt540MOSlEOISLbPTmau7K5NfmcDAR/tbRQAuDKPRyK61e1n61TJSklOLdJ19R06SkprzShUGg4ET5y4RGV2635UwBWFfHy88y7tKABYlQv6GiXzLHIBNd3nrdXqbDsKmAGz6fIxGowThfDi57zTbV+5i09KtGI1GIkOj+OXDpez79wCJcbb/dfOpUpOXvlyNd+UadB08QelySowpCKs1anMA3nLCwIR3Q9l9LIleM29aLAhrtRqbXSvXaDSye+1eTgWeJurOXZbPX1noIKxSqfDy9Mj1lwEVYGenxcU5+8o2pY0pCLdv2UQCsCgR2deGESIHxhwCsEnmIOzfu4PN/GC7PwCbZA7CXUf2xVnBLV2tVechHUiKTyJw02Giwu4SGhyG0Whk4mvjcHErma+XwWBEVYy7b9Vu3IY5q85mWUKtLGj38GC8a1ejSqO6bDlhYOJ7oSSnZvyCe+lmGr1m3mTrN1Vxcyk9q2MUhDkAHzxjXvM4NiqO5fNXMnr6SBydHAp8zWb1awFGboVFoM90k7UKsLe3o7N/c5wcCn5dIUTe5FctkS9J8YmEXbuV6zqfep2e25dvkJKYXMKVFd7FI2cw5LA2JWT8oEtPTePW5eslXJVtUKlU9B3Xi7rNa3PpxBUSYhOZ+Oo4fKp4l8jrJyel8cLEn3nz6d9JT9M9+IRCyhyAA7eu5HBgYLG9ljWp1bZ5tgAMkJp+LwhbqiNsS3IKwAB6vd4chAvTEVapVDSrX5sqlbzNN4dlCcCOEoCFKA4SgkW+uJR3pW3/zmi0OXd/NFoNAYO64qTATSWF1a5/Fxxdcl63VKPV4FvTjzrNZTfA3Jg6wCZHdx4vkZGY5KQ0Xpu2mOtXwjl/MoS3Zy4r1iAMcPrwDr5+5WGeffpprl6+XKyvZQ32nUxiwrtZA7CJKQj3eTbEJkegiuLkgdOcDDydbdc7yAjCMVGxrPl5XaGunTkIq1UqCcBClAAJwSLffGv64d+nY7YgrNFqaNu/MxWrVVaossJxdHGi68i+ODpnDcIarYZKNapkjHbY6E07xc00A2w0Gnn6o6kE9G1D4KbD5hnh4mIKwDeuRpCepictVceFU7eKPQg3atUF/65DiI+L4+kpU4iKjCy217IGPh5aHOxVue40rlGraFTD3mZGnyylWl2/jI01cvm01Wo1jds0LPT1TUG4Ud2aEoCFKAHyE14UyP1B2FYDsEmWIKxSSQDOp4vHLmM0GJj42jh8/LzpO64XAX3bcDboAknxxbPW8v0B2KQkgrBao2H6+7/RpFkzboeE8MwTT5CSbDujPwVVt6o92+dWxc1FnS0IOzuoGNjRhR9fraRMcQry8PZgzPSRODg6ZAvCWjst3YZ2oXGbRkV6DZVKRU0/XwnAQpQA+SlvYwx6A3fDlO1C3QvCWpsOwCaOLk50HdUXp3LOVKrpJwE4HzoMaMfTH001zwCbZoSfeHdysd0YN/fddVy/HJ4lAJukpeo4dyKEn77eViyvDeDo5MLchQup7OfH6RMneOv1V0r1OEDDGg7ZgrApAP/8ui/qHMaIygJPn+xB2BSAm7QtWgAWQpQs+UlvQwx6A4Hrd7H7r81cOXFB0Vp8a/oxaNoomw/AJo7OTvQeP1gCcAHcH3ZVKlWxrgwx+OG2aO1yX5HA3l5Ln2Etiu31ASp4efHtjz/i6ubGzu1bucHpYn09pWUOwg52EoBNMgdhjVYjAVgIGyU/7W2EKQBH3g4H4GzgccWDcGmbB1Sp1aXuc7JFEWFx2TqsEWFxNGzuxzvzHsHRKfuSZc4uDnz046PUrFux2OurVacOX86fT+UqfnhRFX26AV0x35inJFMQfmm8pwTgTDx9PHj4mVEMerS/BGAhbJSEYBuQOQAbMm1SYQ1BWBTN8cBgpUuwKreuRzFjzPf8sXC3OQifOHSNpx5awLZ1J3MMwiUZgE3atm/Pms2bqFOnHro0HQfXHM/12NCqbUusruLSsIYDr06oIAH4Pu5e7tRsWEPpMoQQhSQh2MrlFIBNcgvCYcG32L5svex4ZiMkCN8TmuBBhx4NWPbDXv5YuJsTh67x3nPL8fXzwL9jbYAsQbikA/CZmOrmP9s7ONDvyW4A/PD5Qs6eKt2jEUIIUdpYJAQ/9thj+Pj40KRJE0tcTmQSFRpO+M3QbAHYRK/Tc+bAMfT6jOfDgm9xaNNe4u7GyNa/wuao1So6jhxIz8HNWPbDXt58+nd8/Tx4/7txlPe4N2/csLkfnyyayOe/Ti7RDvD92gxqTrpnHEcjt/PkpMcICw3N8bjS0A0uDPs2HZQuwSrV7tdS6RKEEFgoBE+aNImNGzda4lLiPt5+lajXqnGem1S0H9QdjUZjDsAGnR6MmLf+lSBs/aQbfI9araJL38bmjxu1rIqbe/ZNWGrWq4hfjQolWRqQtRus0WoYP3McbngRE3uXGVOmkBAfX+I1CSGEKDiLhOAuXbrg6elpiUuJHDQKaE7t5g1y3KQiYGA3vP0qZg3A/zEajRKEbYgE4QxXz1zjvedWULWWF20712XDyqNZZoStTeeR7Wjj0xdHXLl44QIvzpqFTld6b5QrKOkG50y6wUIoT2aCbcT9QThzAI4Oj+LQhj05jkyYgvDulZsw6HMeqRDWo6wH4cjQKH7/YgWeFT0Y+/w4XvtilHk0YvPq40qXZ5a5G2znoGXwE31oSEccNE7s272bj959N1toL6sjEUIIYa1KLAQvXLgQf39//P39SU2+W1IvW6qYgrBKrTYHYABnVxccnB2zbP2bmVqjxtPXW9a/tRFlOQhXqORJ94e6MPHVR3Bxc0GtVvHMm4OYMKM7/2/vzuOirvM/gL++c3LLJYiAIKKIoOGBB3kraWmmma5Z25opu2btWtuxZndWdripaYfpZqWpmeKRiveNhOSRJ5KCHIIiyn3N9fuDHxPIoDgz8J3j9Xw89vHYGWY+88p5CC8/vOfz7R9r/OVom1v/idFo7eWLjpq+kMvkWPfjj1i5YoXYsSwGd4MN424wkbharBXFxcUhJSUFKSkpUDpydMJYXfreh4eeGa8vwACgdHT489K/txVhqUwK36C2iH7gfp6Ba0XstQgLgoD7R/XVX3Tj+HkdJBIBj02JgbOLg8jp6qt3UoSjAsOnDoCr4IV+QSPhHxiImP79GzyHu8FERJaDW4NWSK5oeLEAB2fHBkW4XgHmLrDVsdcifLvj5y1zFvh2gyb3hVMrR6guK/HJe1+iQ8eOYkeyKNwNNoy7wUTiMUszevzxx9GvXz+kpqYiICAAy5cvN8eydI/qFmGJRMICbANYhC1b3d1gR1cHDH2qpujtXnZEf3/8unXIzsrS3+ZuMBGRZZCZY5HVq1ebYxkyAwdnRwyaMALZFzPQoVsYC7ANOJmUgai+wWLHENXx8zr0CLf8cZ6hT92PXf87hPNH0pB+KgsnLybhrdmzEdKhA77/6Se4tWoldkSyQB1GdselhBNixyCyO2xINsjByRGhUeEswDaEO8KWOxZRdzfY2d0Jgyb3AwBs/3Ivho8cidBOnXD50iW8+NxzUFVXA7C+3WB1dTX2fP4dyouK9fdVlVdgz+ffoaqsvMnrcCSCiCwJWxIRkRnFTu0PuVKGU3vOoyinFEuWLYN369ZIPnoU7735psWed3wn1//IxKlf9uKnf3+I8qJiVJVXYP1/Psbxjbtw9dwfYsezCZwNJmp5gk6E78gePl0xeOKGln5ZIhKRuUY6LHEsIsL9Sr3ba9/bjL3fJ6LXqG6YvmAyzp4+jacffxyVlZV47sUXEffsswAAv6xkMeIaJf3Y74h/fT5cvD0glctxMysXD7/xHDoP7nvPa1UfS2yGhNaPIxFEzSOuqBIpKSkN7mcJJqIWYc65ZksvwrdyizBn2MfQqrWYd2g23H3dsG/XLsx69lnodDp8vHAhRo4aJWJa42Tu2IEDM2cCAGJfmIruY4YbtQ5LcONYhInMr7ESzHEIImoR9jTX7OHXCn4dWkOn06GkoBQAMCQ2Fi+99hqCgoMRERkpcsJ7pyotxflvv9XfPrV5T70Z4XvB2WAisgQswUQiUavU9W7rdLoG95E1a7hb/denn8bazZsRGBRk4PGWS1Vair3TpiH/xAkMXLQIw779FjezrupnhImIrBFLMJEILp1Kxf6ftqPi/z9Zr9PpcCH5NA6u3wlVVbXI6ag5OTk5Aah5z9etXo2CGzdETnR3xRkZKLx4EQM++wxBDz2EtgMGYNzcf6PoWj5upGeLHY+IyCgswUQicPfxRGVZBQ7H70FFWTkuJJ9GasoZuPt4QmbgioBke3743//w3htv4Pm//x0VFRVix7kjr8hIjNu/H0EPPaS/r310N/x99UK0i+oiYjIiIuOxBBOJwMuvNfo9PARV5RXYsWIjUlPOoG2HQHQf0geCUPNrdHW1CmXFpSInpeYy6pFH0DYgAGdOncJr//43NBqN2JHuSOHm1uA+BxdnEZIQEZkHSzCRSDzbeMPD11t/Oz/7GkpuFgGoKcBHtx7A4Y17oFFbdjki43h5e+OLZcvg6uaGPTt34rOPPhI7EhGRXWEJJhJB7QxwfnYeXDzcIJVJoa5W4VD8btzMu4GjWw+gIDcfEf2iIJVJxY5LzSQkNBSfLVkCmVyO7//3P6z+/nuxIxER2Q2WYCIRpB0/h9SUM2gXHoJhj49CzJihkEglUFVV4+D6nSi4eh29YmMQ0NG6ThGge9e7Xz+8/cEHAICP5s7Fof37xQ1ERGQnZGIHILJHPu3aoqqiEpH394AgCPDya40+Dw3Er9sOQaOuOSbNzbOVyCmppYwZNw45WVnYuX07Qjt1EjsOEZFd4E4wkQjcW3uga/+e9T4El5pyFhqNBuF9usHB2RGHN+5BcUGhuEGpxfzj+eexav16+LVtK3YUIiK7wBJMZAFOHUxBQW4+esXGIKxXJPqPHQaJVIKjv+znB+PshCAI9c4QXrtqFUpKSkRORURkuzgOQWQBwvt0g19IANqGBAIAXNzd0H/sMJQVl/KDcXboy0WL8NXnn2PPzp1YsmwZ5HKeHU1kr9QaDW7cLISvt6f+t4dkHtwJJrIATq7O+gJcy8XdDb7t+Ktxe/TI+PHw9PJC0pEjmPvmm9DpdGJHuqsj363HL+8vgdbCzzsmsiZqjQZHj59ByplUnP/jilV8L7AmLMFERBbGPyAAny9dCgcHB8SvW4dlX34pdqQ7Orh8LX5dswUXDx3DprcXsggTmUFtAS4uK4NOp0NGTi6LsJmxBBMRNaPKsiqjntf1vvsw77PPIAgCPv/vf7FtyxYzJzOPg8vXIuXn7VBXVkNdVY30Y7+zCBOZqG4B1mprSq9Gq2URNjOWYCKiZhAaHQwA2LX8oNFrDI2NxUuzZwMA3njlFRxPSTFHNLOpW4BrsQgTmcZQAa71ZxHOECecjWEJJiKrc/y85e2CnC2sf2GTUc8OhdJJgVN7ziPtWLrR6z759NOY9OSTCO3UCYHt2pka02z27D6LlHX1C3Ct2iKc8Ok3jT5fER3TnPGsVoeR3Zt1fZ1OhwuXruBS1tVmfR0yXnFJGQpLShsU4FoarRYZV/Og0WhbOJntYQkmohZzMilD7AjNqm4RdvN2RewzAwAA6z/ebvSvLwVBwCuvv45vV69Gax8fs+Q0B48mXNTDr3OHFkhCTaXT6XDuj3RczrqK1EtXkHYlW+xIZICnuxu6hnWARGK4osmkUtzfoyukUlY4U/FPkIiskiXuBt8udupAuHq5IP1kJk7sOGP0OjKZTH+GsFarxdpVq1BVZdyssbn43X8/Bn7xJWRKRYOvyZQKDJw+Cd0fib3jGtwNNqw5doNrC/CVnGvQaLXQaLVIS89iEbZQwf5tENGxfYMiLJNKEdMjEq1cXURKZltYgonIalliEa67G+zgosTo54cBAOLn74BGZfqM7KcffID333oLr7/8MrRacX8d6j9oUIMiXFuAe40f2aQ1WIQNM2cRvr0A12IRtmy3F2EWYPNjCSaiFmXrIxG3GzCxN3yCvXE94wYO/3TM5PXGTpgAZ2dn7Ni2DZ/Pn2+GhKbxHzQIY999ATKl4p4LMLWM1MuZDQpwLY1Wi4vpWUjPzhUhGd1NbRGWy1iAm4OgE+GcDQ+frhg8cUNLvywRWYiovsFiR2iyHuHGXaEpwv2K/v8fTziNr59fBVcvF8zd/TIcXJQmZUo8dAgzp02DRqPBm3Pn4rFJk0xazxyqN32LkvwCRI4YaNzzjyWaOZFtuJRwwvQ1ruQgNT3TYAmu1TE4AJ1Dan6LodFocOrCHwgNCoCbi7PJr0+m0+l0vFqcCeKKKpFi4HQd7gQTUYuztN3girJK/Dj/J1zPztffV1pUhpWfrEVu9i2T1+8+IhLto9qhpKAUu/5n/JFptWIGDMCcd94BALz/1ltIPHTI5DVNpXjkaaMLMMCxiObUIcgfoUEBkBr4oJUgCFDIZUjPysWtohJoNBocO30BOdduoKikTIS0ZAgLcPNgCSYiu1deWo6rGXlY8cEqXM/OR2lRGb6f9yMyLlxBYnKRUWvWnQ0WBAHjX3kQALBr+SEU5ZeYnPmxSZMwNS4OGo0G/37uOaRdvGjymmJjEW7IXLPBndoHNijCEokEXcNCMDA6CgqFHEeOn8bOw8eQf7MQ93UORaCf5ZxGQtQcZGIHIBJLYf5NHN+ThJ6xMWjl5S52HBKRl68nprz2BFZ8sApfzP4GEACZXIbJL05A+/Cguy/QBB2j26Pb0HD8vvc8ti7eg8nvjDV5zX++9BJysrORl5sLLy8v00OaKDewN/yykk1aw1qLcFPHOS6fS8fe+AN4ZOpotPbzbtJzzFWEO6A7kvccQ/Le3wAAg8YMQNc+EQCAgP6dseLjlVBrNIjsE4Eh44eY5TXNMc5B1Fy4E0x2qTD/Jg7H70ZxQSEObdiFooJCsSPZHUsbifD288KE58bV3NABsX8ZgpCI9gCMP4Xi9gtojHtpJASJgENrk3EtPb+RZzWdRCLB3E8+wTc//ABPCyjBQE0RtkdNKe+Xz6Vj26odKC0qxbovNyA/90YLJKuv97Bo3P9gPwx9dLC+AKtVauzdeED/mIsnLyL3Sp5ZXq+5L/5BZAqWYLI7tQVYrVIDANTVKhZhQmlRGbauSNDfPrDxSL0ZYXNo29EXMeN7QqvRYuP8HWZZU6lUwsHBAUDNB5rWr10LDS9XLIo7FeHaAlz7fae6slq0Ity9/33o0rMzgJoCvPm7rchMy0LshKF45rUpcHRxQvyyTSzCZPNYgsmu3F6Aa7EIi8NSdoNrZ4BvXr+Fp/7zOJ77+O+QSCX6GWHAfLvBD/8zFnIHOY7vOIPLJzJNzl7XO3Pm4J05c/Dx3LlGX6HOHOx1NxgwXIRvL8C1xCzCtVTVKpSXlCP2saGIiO4CV3cXPPb3cXBu5YLS4lKzvQ6LMFkiHpFGdqOitBy7V22BRt34LplMLsPwJx+Gg5NjCyazb+Y6Ls3QEUJNPVao8EYRVn26Fg/+NVY/AnEjtwBrFvyMsdNHIyDUH4B5jksDgI3/3YHtX+5Du0h/9BvXw6g1DbmceRFLfvgIGo0aYx+YjMF9R5ht7bocXRzQ++EoSOXSOz7O1Plga1Y7I3wj9wZ+XPjTHS9sIlfKMe21KVA6mnZ0nrE0Gg2kUuld7zMHzgiTGBo7Io0fjCO7IVcq4OLuhpJbRdBqGv5AkkglcPFoBbmi4WVgybLlZuRh8/JtmPjPR+HR2h0AkHkxCwmrduPxFybA1f3OB8y7e7fCjA+m1btEqbefF579cHqDy5Ya42xhUL0iPGL6IBxc8ysyz+Qg80yOyevXFaLrgTQkY+POH3F+Zzo8BX+zrl+rrKgCw5/uf8fHmOODctZKER2D6mOJcPN0g1cbT9y8fsvgP8BlchmCw4KgMHD56ZZiqOw2RwEGanaEWYTJUrAEk92QyWXoP244DsfvblCEJVIJ3Lw80H/sMEhlzfPNnww7mZRh8m6wDsCt/EKs+GAVprz2BEpulWDlJ2vh6u7S5LEAQ2X39vuOn9cZvRtcl6OrA2Ys+St+234aOpj7l3H9cOR3Lxw8uR2XpCnoO6I32nq3M9vqyZtOoLy4ErfyCs22pq1SRMcAxxIx4R+PYt1XGxoUYZlchvbhwXhw8gMQJDwHlqilsQSTXZEr5A2KcN0CLJPzr4Q1ahvcBk/9ZzK+n/cjFr74BQDAq40n/vbaE3DzcBU5nWEdo9ujY3T7Zll7km4M3p49G/E//4wtv67EqvXr4R8QYJa1vf098PO8bWZZy14oHBQNinDdAmyO3zYQ0b3j3zyyO7VF2NWjFQQJC7CtaBvcBkMeHaC/PTZutMUW4OYmCAJef+899ImJQYeOHeHqap9/Dpaktgh7+nhAIpWwABNZAP7UJ7tUW4Qv/56KDvd1tvoCzOvK18wA71n351mnP3+xCVNee0I/I2xv5HI5PluyBEqlknPuFqK2CKeeSkNEdDgLMJHI+DeQ7JZcIUdYr0irL8D5WXnYtnw98rPMc6anNcq8mK2fAX5x0fOIe28qqiqqsOKDVSi8Ydxlj22Bi6urvgCrVCps2rBB1KPTqKYId+0TwQJMZAH4t5DIiuVn5SFp2wGoqqqRtO2A3RZhB2cl/IJ89TPAtTPCHq3doVDKxY5nEV6ZNQtvvPIKvvniC7GjEBFZBJZgIitVW4BrP22uUWvstgj7+LfGlDlP1psBbhvcBn+bPRlOrk4iJrMcY8aOhSAIWPzZZ9i6ebPYcagZ6HQ6FBXY728+iO4VSzCRFbq9ANey5yJsaCba3uek6xoSG4uX58wBALz56qtISbbP83ttlU6nw6GtR/DtRz/g9K9nxY5DZBVYgomsTHlJGRJ/2dfole80ag0Sf9mH8pKyFk5Glu7JKVMw+amnoFKpMGvGDKRfvix2JDKD2gL8+9EzAIADmw+xCBM1AUswkZVxcHaET6Bfoxf1kMqk8An0g4MzL/1MDb08Zw4GDR2K4qIizHzmGdwsKBA7EpmgbgFWq9QAALVKzSJM1AQswURWRiKRoM9DA+Ht79ugCEtlUnj7+6LPgwP56XMySCqV4qMFC9AlMhLtO3SAUqkUOxIZyVABrsUiTHR3ZvkpmZCQgLCwMISGhmLevHnmWJKI7sBQEa5XgKUswNQ4JycnfPXtt1j41VdwdnEROw4Z6XpOPo4fPNmgANdSq9TYs34fKssrWzgZkXUw+SelRqPBzJkzsX37dpw7dw6rV6/GuXPnzJGNiO6gbhEWJAILMN0Tdw8PyGQ1Z2RXV1Vhx9atIieie+Xj3xo9BkY1eta5TC7DsPGD4eDk0MLJiKyDyVcJSE5ORmhoKEJCQgAAkyZNwqZNm9ClSxeTwxHRndUW4Zy0K/APDWIBpnum0+kwc/p0/JqYiJKSEjw2aZLYkaiJBEHAgFH3A0CDkQiZXIZBY/qja59IseIRWTyTf2Lm5OQgMDBQfzsgIAA5OTmmLktETSSRSBAY1p4FmIwiCAIeHD0aAPD+W2/hyMGDd3sCAKDwWnFzR6MmqC3C3fr9efVLFmCipmmxn5pLly5Fr1690KtXL1RV3GyplyUiort4dOJETJsxAxqNBi89/zwuXrjQ6GO73N8RUrkUKVt/x7Gtp1owJTWmbhEGwAJM1EQml2B/f39kZWXpb2dnZ8Pf37/B4+Li4pCSkoKUlBQoHT1NfVkisiEnkzLEjtBkx8/rjHre2cIgMycxr+deeAEjR49GWVkZZk6bhmt5hi+44h/WBhNmjwIA/DBnPfIuXb/jurmBvc2elRqqLcJPv/pXiy/AHUZ2FzsCEQAzlODo6GikpaUhPT0d1dXVWLNmDcaMGWOObERkR6ypCBvLkouwRCLBex99hO69euFaXh6enz4dZaWlBh87+Ml+iB51H6rKqvH186tQVV7dwmmthyI6psVeSxAEtPJq1WKvZwoWYbIEJpdgmUyGxYsXY8SIEQgPD8fEiRMRERFhjmxE1Ey0Gg3OHT2J6qo/y4tapcbZo40ft0R/MnY3GLDsIqxUKrHwyy8RFBwM/3btIJEaviCLIAh48v1H0SakNa6mXcPKNzZAp2v8z4S7wURkiQTdnb5zNRMPn64YPHFDS78sEf2/m3k3cCh+N1p5uyNmzFBIJBL8uu0g8rPz0HfUILQJbjjS1FKi+gaL9tr3qke4YPRzI9yvmDGJeRXcuAEPT8+7XnDlato1fDh+MaorVJj8zlgMmtz3jo/3y0o2Z0yrUn0sUewIFulSwgmxI5AdiCuqREpKSoP7+XFyIjvk2cYbfR4cgKIbhTi0YRcOb9yN/Ow89BjWV9QCDFjXWISt7gh7eXvrC3BFRQX27d5t8HFtO/rir3PHAwB+mrsFGaezWyyjtWnJsQhrwrEIEpPJ5wQTkXVqE+yPXrExOLbjMACga/+eaNc5RORUNU4mZVjNjvDx8zqjd4QtuQgDgFqtwrtxQ5H2exJmfbQWfYY9CqD+LnbvMVH443gGDqxKwtLnV2LOxn/C2d3J4Hq5gb3tejeYiCwLd4KJ7JRapUbG2T/0t7MuptebERabNe0IN8bQtJkIE2hGk8nk6DWw5oPOS978G9JOJwFoWN4nzB6N4G4BKMgpxLcv/wStVtvomvY8H8zdYMO4G0xiYQkmskNqlVo/A9xjWF/0HTUIRTcKkbh5L4uwEQyNRWg0Wix4awvWr/hzFlSl0mDeK+ux7effWjKeSR7+20sYOu4ZqKoq8emLj+Ja9iUA9YuwXClD3KIn4NTKEaf3X0DC1wfEiktE1GQswUR2qORmEW5du4Eew/qiXecQtAn2R58HB6DkZjEKrxeIHa8eqy3COkCr1eK7xfuwfkUiVCoNPnktHkf3pUKraXyn1NIIgoCnX/0c3fo9gOJb+fjon2NQWlRzwaO6RdjL3wNTP/0LAGDzgp04n/iHwfUA7gZTQ9wNJjHwdAgiO1VVUQmlo8Nd77ME1jIfDNQ/MUKj1mLB25txIOGs/r64lx/A6L9EixHNJOWlxXhn+mBkpp1G5+798dqSBMgVSgD1Z4Q3fbYT277YC1cvF8zZ+Dw82hg+t9aeZ4N5UkTjeFoENQeeDkHUTAzNP95pJtJSGCq7lliAAevZDb6dVCbBc6+P0t/29nW1ygIMAE4ubnhlwWZ4tG4LrzaBjT7u4X8OR+eYUJQUlOKbWT9Co9IYfBx3g4lIbCzBRCbQqDU4umU/UlPO6O9TVatwOH43Lv+eKl4wG2QtRbjuWIRKpcH8Nzbpb9+4VlJvRtjaePkGYO53iZj57nf6XWCg/liERCrBM/+dBHdfN1z67Qri5yc0up49F2EyjGMR1JJYgolMIJEIcHB2xPlff0dqyhmoqlU4umUfbl0vgIOz4WOiyHjWVITV6poZ4KR9qYh7+QHEJ83GoJERNTPC3x0VO6LRPH38IQg1Ix8VZSU4mbgDQP0i7OblgukLJ0Mik2DX8kM4vuOMwbXsGXeDG8ciTC2FJZjIBIJEgh5D+yAwrD3O//o7tn6zDreuFyD6gf5o26HxXxmT8aylCJ9MFeDu6ayfAZbKJJj19hgMfjAS7l7OYsczWXVlBd6ZPgSfvPAITh3dCaB+EQ7tGYzxrzwIAFj5+gaORRCRxWEJJjKRIJGg64Ce+ttuXu4swASJRMCM/4ysNwMslUnwwrtjMGx0NxGTmYfCwRH3xYyAVqPBwv9MQuYfpxs8ZtiU/nBwVqKssBxVFZZz9J6l4G5w47gbTC2BJZjIRKpqFZJ+2Q8AkMllKMq/VW9GmOxX7djA3e6zVn959j30jZ2AirISfPyvR3Az/2q9rwuCAEFiO/+9RGRbWIKJTKBWqfUzwL1HDsCoaY/pRyMu/nb27gtYGa1Gi992J+J6Zq7+Po1ag2M7DqMgN1/EZCQGiUSCGW8tR8dufVFwLQufvjAW5eXlYsciImoSlmAiE0hlUnj4eulngGtnhIO7hMK9tafY8cxOrVKj6EYhkrYdwPXMXGjUGiRvP4icPzJReqtY7HgkAoWDI16avwE+/iFIv3ACr86aBY3G8PwvEZElkYkdgMiaCYKArv171r9PIkHUENv8sI/CQYH+Y4fh8MY9SNyyD4IgQKfTIWpIbwR16SB2PBKJm0drvLpwM96cOgCeXl7QarWQSqVixyIiuiOWYCK6JwoHJfqNHowd322ETqdD2w7tENwlVOxYJLK2wWGY92MKBoZpbWrumYhsF8chiOieaNQanNz3q/52XkZ2vRlhsl/ebdrpC3BxcTGOG7hMKRGRpWAJJqImq50BvpaZi6ghvfHQM+Ph4u6mnxEmAoCiwkI8NWECZkydihJNgdhxiIgMYgkmoibTarVQqdSIGtIbwV1CoXBQov/YYXDzdEd1Fc+BpRpurVqhS9euqCgvx6ny/ajS8cQIIrI8nAkmoiaTK+QYMHYYBMmf/35WOCgx6LEH6t1H9k0QBLz9/vvIu3oVKcnJuIAjKCsthZObo9jRiIj0+FOLiO6JobLLAky3UyiV+OyLL+AkcUM5ijDnPy9BrVaLHYuISI8/uYiIqFm0cndHN6chkEGBpMQj+PCdd6DT6cSORUQEgCWYiIiakZPEFZ1xPxQKBeQKBUswEVkMzgQTEVGzchW8sOqneIRFhokdhYhIjzvBRETUbGqvm+Hj3UZ/382CAlw4d06kRERENViCicjqnEzKEDsCNVFI9yAAwMHVNRdYuZaXh79OmIC/T5mC7MxMMaMRkZ1jCSYiaibHz9vf/OvZwqB6tx96digAYO/3R1BWVA5PLy8EBgXh1s2beHbaNBQVFiI3sLcYUYnIzrEEE5FVspbdYHsswnV16BGEzjGhqCytwt7vEyGXy/HpokXo1LkzMi5fxqwZM1BdVSV2TNEoomPEjmCxOozsLnYEsnEswUREZFa37waPqt0NXnEYFSWVcHF1xeJvvoGPry9+O3YMb86ejasB0WJEJQvHIkzNiSWYiKwWd4OtQ6c+IegY3R7lxZXYt/IoAKCNnx8Wf/MNnJydsW3zZixZsEDckCLibjCROFiCiciqWUsRtjcNdoNn1uwG7/72ECrLasYfOnfpgk8XLYJUKoVGo+FuMBnE3WBqLjwnmIisXnMW4ai+wWZZ5/h5HXqEC2ZZyxp1jglFSPd2uHwiEwd+TMKI6YMAAP0HDcKG7dvRPiQEAKz6Q3J+WclGP1cRHYPqY4lmTENEd8OdYCIiahZ1d4MFQcComcMAALuWH0J1RbX+a7UFGADyr1/HpbS0lgtJVoG7wdQcWIKJiO7AnLvM9j4bHDGwE4K6BqCkoBQH1zTcNc3OzMQT48fjH08/jevXromQ0DTWvIttDViEydxYgonsmKpahdOHj6OqvFLsKHbD3opww93gmtngncsOQFWlqvfY1r6+aOPnh2t5eXhu2jSUlZa2aFax8QNyRC2LJZjITqmqVTgcvxuXT1/EgZ93sAhTi+g2NBwBnf1QdL0Eh9el1PuaUqnEwq++QlBwMC6cP49XZs2CWq0WKalxuBvcvLgbTObEEkxkh2oLcMmtIui0WlSUlrMI34G5P3jH3eCa3eAdX++Hqqp+yfXw9MSSZcvg7uGBQ/v348N334VOZz9/XtwNJmo5LMFEdqZuAdZqtAAAnU7HIkwtJuqBCLTt6ItbeUU4Gv9bg6+3Cw7Goq+/hkKhwLoff8SKZctESGk87gY3L+4Gk7nwiDQiO6JRaxoU4Fp1i/DgiQ9C4aAQKaVlOpmUYbbj0gDr3g025qi3s4VBiHC/AgCQSCR4aOZQLJu1Gglf78f943tBKpfWe3xUjx744NNP8fK//gWVSmVoSYuWG9jb6CPTeFwaUctgCSayIxqNBpVlFdBpDRcwnU6H6soqqFUqlmBqlDnOPO45siu2hOzGtcv5OLXnHHqM7NrgMQ889BBCO3VCSGioSa9Ftsfad4MvJZwQOwKB4xBEdkWhVGDQYyOgcFBCEBqWGJlCjoHjH4CTq7MI6cieSKQShPWpOR+4uKDxUyDqFuDcq1eRnZnZ7NnMhWMRRJaNJZjIzji5ORsswjKFHAMfjYWbl7t44ciuGPqHWGMupaXhifHj8ey0aSgqLGy+UERkN1iCiexQ3SIMgQWYLJ9vmzbw9PJCxuXLmDVjBqqrqsSORERWjiWYyE7VFmHvtj4swGTxXFxdsXjpUvj4+uK3Y8fw9pw5dnV0GhGZn0kleN26dYiIiIBEIkFKSsrdn0BEFsXJzRn9xw5nASar0KZtWyz6+ms4Ojnhl40b8dXnn4sdiYismEklODIyEhs2bMDAgQPNlYeIiKhRXSIj8fGCBZBIJPhy0SJsiY8XOxIRWSmTSnB4eDjCwsLMlYWIiOiuBg0dildffx2CIKCoqEjsOERkpXhOMBERWZ3Hn3oKPXv3RqfOncWOQkRW6q4lePjw4cjLy2tw//vvv49HHnmkyS+0dOlSLF26FABQVXHzHiISERE1VLcAZ125AmcXF3h6eYmYiIisyV1L8O7du83yQnFxcYiLiwMAePg0vDIQERGRMc6ePo1np05Fu+BgLFu5EkqlUuxIRGQFeEQaERFZNR9fXygdHHDqxAm8/vLL0Gq1YkciIitgUgmOj49HQEAAjh49ilGjRmHEiBHmykVEZHaF+TeRnZZR776Cq9eRm54tTiAyi9Y+Pli8bBmcnZ2xY9s2fP7f/4odiYisgEkleNy4ccjOzkZVVRWuXbuGHTt2mCsXEZHZpaacRcquo8hKTQdQU4ATt+zH+V9/5+6hlesUFob5ixdDKpVi+VdfYcNPP4kdiYgsHMchiMhu9BzeD95tW+O3PUk4uT8ZiVv2w9HFCTEPD4FEwm+H1i5mwADMeecdAMDcN99E0pEjIiciIkvG7/pEZDdkchn6jhoMQRCQcfYPaNRq9B87DA7OjmJHs0sefq3QLsIfrp7OZlvzsUmT8HRcHLRaLa7m5JhtXSKyPYJOhIuve3t7Izg4uKVf1izy8/PRunVrsWOQGfC9tB18L20H30vbwPfRdtjCe5mRkYEbN240uF+UEmzNevXqhZSUFLFjkBnwvbQdfC9tB99L28D30XbY8nvJcQgiIiIisjsswURERERkd1iC71HtVe/I+vG9tB18L20H30vbwPfRdtjye8mZYCIiIiKyO9wJJiIiIiK7wxJ8j9atW4eIiAhIJBKb/bSkrUtISEBYWBhCQ0Mxb948seOQkaZOnQofHx9ERkaKHYVMlJWVhSFDhqBLly6IiIjAwoULxY5ERqqsrETv3r1x3333ISIiAm+99ZbYkchEGo0G3bt3x+jRo8WOYnYswfcoMjISGzZswMCBA8WOQkbQaDSYOXMmtm/fjnPnzmH16tU4d+6c2LHICFOmTEFCQoLYMcgMZDIZ5s+fj3PnziEpKQlLlizh30srpVQqsXfvXpw6dQonT55EQkICkpKSxI5FJli4cCHCw8PFjtEsWILvUXh4OMLCwsSOQUZKTk5GaGgoQkJCoFAoMGnSJGzatEnsWGSEgQMHwtPTU+wYZAZ+fn7o0aMHAMDV1RXh4eHI4dXerJIgCHBxcQEAqFQqqFQqCIIgcioyVnZ2NrZu3Ypp06aJHaVZsASTXcnJyUFgYKD+dkBAAH/YElmQjIwMnDhxAn369BE7ChlJo9EgKioKPj4+iI2N5XtpxWbNmoWPP/4YEolt1kXb/K8y0fDhwxEZGdngf9wxJCJqPqWlpRg/fjwWLFgANzc3seOQkaRSKU6ePIns7GwkJyfjzJkzYkciI/zyyy/w8fFBz549xY7SbGRiB7BEu3fvFjsCNRN/f39kZWXpb2dnZ8Pf31/EREQE1PzqfPz48XjiiSfw6KOPih2HzMDd3R1DhgxBQkICP8BqhY4cOYLNmzdj27ZtqKysRHFxMZ588kmsXLlS7Ghmw51gsivR0dFIS0tDeno6qqursWbNGowZM0bsWER2TafT4ZlnnkF4eDhefPFFseOQCfLz81FYWAgAqKiowK5du9C5c2dxQ5FRPvzwQ2RnZyMjIwNr1qzB0KFDbaoAAyzB9yw+Ph4BAQE4evQoRo0ahREjRogdie6BTCbD4sWLMWLECISHh2PixImIiIgQOxYZ4fHHH0e/fv2QmpqKgIAALF++XOxIZKQjR47ghx9+wN69exEVFYWoqChs27ZN7FhkhNzcXAwZMgTdunVDdHQ0YmNjbfJoLbINvGIcEREREdkd7gQTERERkd1hCSYiIiIiu8MSTERERER2hyWYiIiIiOwOSzARERER2R2WYCIiIiKyOyzBRERERGR3WIKJiIiIyO78H1oP2/saFum6AAAAAElFTkSuQmCC", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.clf()\n", - "fig, ax = plt.subplots(1,figsize=(12,8))\n", - "fig.patch.set_facecolor('white')\n", - "ax.contourf(x_test_grid, y_test_grid, q_y_score_grid.reshape(x_test_grid.shape), cmap=\"coolwarm\")\n", - "CS1 = ax.contour(\n", - " x_test_grid,\n", - " y_test_grid,\n", - " q_y_score_grid.reshape(x_test_grid.shape),\n", - " levels=[0.5],\n", - " linewidths=2,\n", - ")\n", - "ax.scatter(x_train[:, 0], x_train[:, 1], c=y_train, cmap=\"jet\", marker=\"D\")\n", - "ax.scatter(\n", - " q_wrong_predictions[:, 0], q_wrong_predictions[:, 1], c=\"red\", marker=\"o\", edgecolors=\"k\", s=32\n", - ")\n", - "ax.scatter(x_test[:, 0], x_test[:, 1], c=q_y_pred_test, cmap=\"jet\", marker=\"x\")\n", - "CS2 = ax.contour(\n", - " x_test_grid,\n", - " y_test_grid,\n", - " y_score_grid.reshape(x_test_grid.shape),\n", - " levels=[0.5],\n", - " linewidths=2,\n", - " linestyles=\"dashed\",\n", - " cmap=\"hot\",\n", - ")\n", - "ax.clabel(CS1, CS1.levels, inline=True, fontsize=10)\n", - "ax.clabel(CS2, CS2.levels, inline=True, fontsize=10)\n", - "CS1.collections[0].set_label(f\"Quantized FHE decision boundary, acc={homomorphic_accuracy:.1f}\")\n", - "CS2.collections[0].set_label(f\"Sklearn decision boundary, acc={sklearn_acc:.1f}\")\n", - "ax.legend(loc=\"upper right\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "52a83d37", - "metadata": {}, - "source": [ - "### Enjoy!" - ] - } - ], - "metadata": { - "execution": { - "timeout": 10800 - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/user/advanced_examples/PoissonRegression.ipynb b/docs/user/advanced_examples/PoissonRegression.ipynb deleted file mode 100644 index 46f1b0c7a..000000000 --- a/docs/user/advanced_examples/PoissonRegression.ipynb +++ /dev/null @@ -1,888 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b760a0f6", - "metadata": {}, - "source": [ - "# Poisson Regression\n", - "\n", - "This tutorial shows how to train several Generalized Linear Models (GLM) with scikit-learn, quantize them and run them in FHE using Concrete Numpy. We make use of strong quantization to ensure the accumulator of the linear part does not overflow when computing in FHE (7-bit accumulator). We show that conversion to FHE does not degrade performance with respect to the quantized model working on values in the clear." - ] - }, - { - "cell_type": "markdown", - "id": "253288cf", - "metadata": {}, - "source": [ - "### Import libraries\n", - "\n", - "We import scikit-learn libraries and Concrete Numpy quantization tools:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "6200ab62", - "metadata": {}, - "outputs": [], - "source": [ - "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" - ] - }, - { - "cell_type": "markdown", - "id": "f43e2387", - "metadata": {}, - "source": [ - "And finally we import some helpers for visualization:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "d104c8df", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "\n", - "import matplotlib.pyplot as plt\n", - "from IPython.display import display" - ] - }, - { - "cell_type": "markdown", - "id": "53e676b8", - "metadata": {}, - "source": [ - "### Insurance claims dataset\n", - "\n", - "In this tutorial, we show how to build a regression model that predicts the frequency of incidents in an insurance setting.\n", - "\n", - "We download a data set from OpenML that contains 670,000 examples giving the frequency of car accidents for drivers of various ages, past accident history, car type, car color, geographical region, etc. We take only the first 50 000 examples to speed up training.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "d451e829", - "metadata": {}, - "outputs": [], - "source": [ - "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": "39a70df7", - "metadata": {}, - "source": [ - "The target variable is the number of claims per year, which is computed by the following formula :" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "5e163891", - "metadata": {}, - "outputs": [], - "source": [ - "df[\"Frequency\"] = df[\"ClaimNb\"] / df[\"Exposure\"]" - ] - }, - { - "cell_type": "markdown", - "id": "75f4fdb7", - "metadata": {}, - "source": [ - "Let's visualize our data set, showing that the target variable, \"Frequency\" has a poisson distribution !" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "2a124a62", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3sAAAG5CAYAAAA3ci11AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAABxL0lEQVR4nO3dd3xUVf7/8fckEemhhpJEEEJNKEIAWV16UTTBghRRQNAo9lX2p9913QXXgitY1rpRlqIsCK4LRAULGkUFaVZYICDBFEQEEyEQ0s7vj5ghM5nJzCQzmcnk9Xw88kjunXvPfd87N5n7yS3HYowxAgAAAAAElRB/BwAAAAAAeB/FHgAAAAAEIYo9AAAAAAhCFHsAAAAAEIQo9gAAAAAgCFHsAQAAAEAQotgDqmjv3r3q27evmjRpon/84x9uz5eamqqoqCi3pl2+fLnGjBlT1Yi10i233KK//e1v/o4BAF4TGxur1NRUf8fwq//+97+Kjo5W48aN9eWXX/o7Tq302WefqUuXLmrcuLHWrFnj9nxLlizRxRdf7Na0jz76qG688cYqJkQgstDPHhzp2LGjjhw5otDQUOu4ffv2qX379n5MFVhmzZqlpk2b6qmnnvJovtTUVF133XXKzMz0UbLAULYPhYWFKTQ0VD179tS0adOUlJSkkJDA+j/TsGHD9PXXX+vHH3/Uueee6+84AGqRjh076pVXXtGoUaOs45YsWaJXXnlFn376qdvtpKen6/zzz1dhYaHCwsJ8EdWvOnfurCeffFLjx493+LrFYlHDhg1lsVgkSWFhYcrJyanBhIFv5MiRSkxM1F133eXRfFXZHxE8AuuICwElJSVFJ0+etH7ZF3pFRUV+ShYYDh06pNjYWH/HCGgpKSk6ceKEDh06pPvvv1+PP/64Zs2a5XT64uJin+QwxqikpMTha+np6dq0aZMsFovWrVvnk+UDgL/5+zPbnc/Mr7/+2nrM4ajQ8/c6+BvHHagKij14xGKx6Pnnn1eXLl3UpUsXSdJbb72lvn37qlmzZvrd736nb775xjr9l19+qX79+qlJkyaaNGmSJk+erD//+c+SHF9WYLFYtH//fknSmTNnNGfOHJ133nlq06aNbrnlFp0+fVrS2UshFy5cqIiICLVr106LFy+2tnP69Gnde++96tChg8LDw3XxxRfr9OnTuuyyy/Tss8/aLLN3797673//63B9161bp9jYWDVr1kzDhg3T//73P0nSiBEj9NFHH+n2229X48aNtW/fvgrzHj9+XDfccIPat2+v5s2b64orrnC4jPnz56tz585q0qSJevbsaZPFfhtZLBa98MIL6tKli5o0aaIHH3xQBw4c0O9+9zs1bdpUEydOVEFBgSTp559/1uWXX65mzZqpRYsW+v3vf++w4Jk9e7bmzJljM278+PF68sknJUmPP/64IiMj1aRJE3Xr1k0bN250uB6VCQ8PV2Jiol5//XUtXbpU3333nSRpxowZmj17tsaNG6dGjRrpo48+0owZM6z7SI8ePfTWW29Z2ykqKlLr1q21c+dOSdKWLVv0u9/9Ts2aNVOfPn1sLpMaNmyYHnjgAV100UVq2LChvv/+e4fZli1bpgsvvFAzZszQ0qVLbV47duyYEhIS1LRpUw0YMEB//vOfbd6PPXv2aPTo0WrRooW6deumVatWebxtAAS/jh076oMPPpAkbd26VfHx8WratKnatGmje+65R5I0ZMgQSVKzZs3UuHFjbd68WSUlJXr44YfVoUMHRUREaNq0acrNzbW2u2zZMnXo0EEtW7bU3/72N5vlzJ07VxMmTNB1112npk2basmSJdq6dasGDx6sZs2aqV27drr99tutnxmSZ58x9pxlPXPmjBo3bqzi4mL16dNHnTt3dnu7paeny2KxaNGiRTrvvPM0YsQISdK//vUv9ejRQ82bN9fYsWN16NAh6zzvv/++unfvrvDwcN1+++0aOnSoXnnlFes2ue666yq0X1ZE5ubmatasWWrXrp0iIyP15z//2fpPyLLP4zlz5qh58+Y6//zztX79emtbzj7z4+LilJKSYp2usLBQrVq1cnop68svv6yYmBi1aNFCiYmJys7OllR6ZvT7779XQkKCGjdurDNnzlSYNyMjQ1dddZVat26tli1b6vbbb3e4jLvuukvR0dFq2rSp+vfvr02bNllfK7+NyrbP4sWLFR0drebNm+ull17Stm3b1Lt3bzVr1sxmGfv379fQoUMVHh6uVq1aadKkSQ6XjxpmAAc6dOhg3n///QrjJZlRo0aZY8eOmVOnTpmdO3ea1q1bmy1btpiioiKzZMkS06FDB5Ofn2/OnDljzjvvPPPkk0+agoICs3r1ahMWFmYeeOABY4wxixcvNhdddFGF9tPS0owxxtx9990mISHBHDt2zPz666/m8ssvN/fff78xxpiPPvrIhIaGmgcffNAUFBSYt99+2zRo0MAcP37cGGPMrbfeaoYOHWoyMzNNUVGR+eyzz0x+fr55/fXXzcCBA63L++qrr0yLFi3MmTNnKqzr3r17TcOGDc17771nCgoKzOOPP246d+5snXbo0KHm5ZdfdroNx40bZyZOnGiOHz9uCgoKTGpqqjV7ZGSkdbpVq1aZrKwsU1xcbFauXGkaNmxosrOzHW4jSSYxMdHk5uaa7777ztSrV8+MGDHCHDhwwOTk5JgePXqYJUuWGGOMuf/++83NN99sCgoKTEFBgfnkk09MSUlJhZwff/yxiYqKsr52/PhxU79+fZOVlWX27NljoqKiTFZWljHGmIMHD5r9+/c7XefynO1D0dHR5oUXXjDGGDN9+nTTtGlT8+mnn5ri4mJz+vRpM336dOs+Mm/ePHPttdda533rrbdM9+7djTHGZGZmmhYtWpi3337bFBcXm/fee8+0aNHC/PTTT8aY0vcnOjrafPfdd6awsNAUFBQ4zNm5c2fz/PPPm+3bt5uwsDDz448/Wl+bNGmSmTRpksnLyzO7du0yUVFR1vfj5MmTJioqyvzrX/8yhYWFZufOnaZly5Zm165dbm0fAMHB0d86+7/d5ae58MILzbJly4wxxpw4ccJs3rzZGFP691WSKSwstM63aNEi07lzZ3PgwAFz4sQJc+WVV5rrrrvOGGPMrl27TKNGjcymTZvMmTNnzL333mvCwsKsy/nrX/9qwsLCzH//+19TXFxsTp06ZbZv3242b95sCgsLzcGDB0337t3NU089ZV2eJ58x9irLWtZ22ee7I45eL9sm119/vTl58qQ5deqUWbNmjencubPZvXu3KSwsNH/729/M4MGDjTHGHD161DRu3NisXr3aFBQUmCeffNKEhoZaP6v/+te/mqlTp1Zov2ybX3HFFSYpKcmcPHnSHDlyxAwYMMC89NJL1vc0LCzMJCcnm6KiIvPCCy+Ydu3aWT87nX3mP/7442bixInWZa5Zs8bExcU53AYbN240LVu2NDt27DD5+fnm9ttvN7///e+trzv7XDXGmKKiItO7d29z9913m5MnT5rTp0+bTZs2WbOX3x9fffVV8/PPP5vCwkKzYMEC06ZNG3P69OkK26hs+9x8883m9OnT5t133zXn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WLFCTZo00dNPP6033njDK+tVWdbqcnTc4ahgSktLs/k7Xfa5Xaay447o6Gide+65+vnnnz1+Gqqrz/zp06frlVdeUVFRkQYPHuz0OMJ+G+bl5enYsWNuH3f88MMPKioqqjT/pk2b9Pe//10bN25UbGysQkJC1Lx5c68cd7Rt29b6mfzpp59q1KhRGjJkiPVzG/7BmT1Uy0033aSXXnpJX3zxhYwxysvL09tvv60TJ05o8ODBCgsL0z/+8Q8VFhbqzTff1NatW63z9unTR7t27dJXX32l/Px8zZ071/paSEiIbrrpJv3hD3+w/lctKytL7777rstMISEhmjlzpu655x5lZ2eruLhYmzdvtt7MPHjwYIWEhOjee+91elZPkiZOnKi3335bGzduVGFhoRYuXKhzzz1Xv/vd71xmaNeunS699FLdeuut+uWXX1RYWKhPPvmkwnR5eXmyWCxq3bq1JGnx4sXWh5dU11tvvaX9+/fLGKPw8HCFhoY67fLgggsuUKtWrXTjjTdq7Nix1g+rvXv36sMPP9SZM2dUv359NWjQoErdJvz666966623NHnyZF133XXq1auX2/NOnjxZ7733nl588UXrWT1Juu6665SSkqJ3331XxcXFys/PV2pqqttdWqxZs0ahoaHavXu3vvrqK3311Vf63//+p9///vdatmyZQkNDddVVV2nu3Lk6deqU9uzZo2XLllnnv/zyy7Vv3z69+uqrKiwsVGFhobZt22Z9iA8AOPLaa6/p6NGjCgkJsf6tDQkJUevWrRUSEmLzMKkpU6boqaee0sGDB3Xy5En96U9/0qRJkxQWFqYJEyYoJSVFn3/+uQoKCjR37lyXB+wnTpxQ06ZN1bhxY+3Zs0cvvvii19arsqzedMstt+ixxx7Trl27JJU+VGX16tWSpMsuu0y7du3Sm2++qaKiIv3jH/+wKej69u2rTz75RD/88INyc3P12GOPWV9r166dxowZo3vvvVe//vqrSkpKdODAAX388ccuM7n6zL/iiiu0c+dOPfPMM5o2bZrTdqZMmaLFixfrq6++0pkzZ/SnP/1JgwYNUseOHV1mGDhwoNq1a6f7779feXl5ys/P12effVZhuhMnTigsLEytW7dWUVGRHnroIf36668u23fH6tWrrZ/BzZs3l8ViCbiuluoi3gFUS3x8vF5++WXdfvvtat68uWJiYrRkyRJJUr169fTmm29qyZIlatGihV5//XVdddVV1nm7du2qv/zlLxo1apS6dOlS4cmcjz/+uGJiYnThhReqadOmGjVqlM1/MSuzYMEC9erVSwMGDFCLFi1033332TyJctq0afr2229tnsplr1u3bnrttdd0xx13qFWrVkpJSVFKSorq1avnVoZXX31V55xzjrp3766IiAg9/fTTFabp2bOn7r33Xg0ePFht2rTRt99+q4suusit9l1JS0vTqFGj1LhxYw0ePFi33nqrhg8f7nT6a6+9Vh988IFNQXXmzBndf//9atWqldq2bauffvrJ+uG4fPlyl4+ATkhIUJMmTRQdHa1HHnlE99xzj81TU93Rrl07DR48WJ9//rnNk72io6O1du1aPfroo2rdurWio6P1xBNPOO1iwd7SpUt1ww036LzzzlPbtm2tX7fffruWL1+uoqIiPffcc8rNzVXbtm11/fXXa8qUKdZ++Jo0aaL33ntPK1euVPv27dW2bVvdd999Dp+QBgBlNmzYoNjYWDVu3Fh33XWXVq5cqQYNGqhhw4bWJwg3a9ZMW7Zs0cyZM3X99ddryJAhOv/881W/fn3rE6VjY2P17LPPavLkyWrXrp0aN26siIiISvsKXbBggf7973+rSZMmuummm7z6tMTKsnrTlVdeqfvuu0+TJ09W06ZNFRcXZ30qZqtWrbR69Wrdf//9atmypdLS0mw+U0ePHq1Jkyapd+/e6t+/vy6//HKbtpctW6aCggL17NlTzZs314QJE2xuDahMZZ/5DRo00NVXX62DBw/aHAfZGzVqlP72t7/p6quvVrt27XTgwAGtXLnSreWHhoYqJSVF+/fv13nnnaeoqCi9/vrrFaYbO3asLrnkEnXt2lUdOnRQ/fr13b7U1ZVt27Zp0KBBaty4sRITE/XMM8+oU6dOXmkbVUen6qhRM2bMUFRUlB5++GG/5li2bJmSk5PpYBQeue+++/Tjjz9W6KIBAPzt5MmTatasmdLS0nT++ef7O07AGDZsmK677jrdeOONfs3x0EMPad++fXrttdf8mgN1D2f2UOecOnVKL7zwgpKSkvwdBQFuz549+uabb2SM0datW7Vo0SJdeeWV/o4FAJJkfXhVXl6e5syZo169erl1yR9q1vHjx7Vo0SKOO+AXFHuoU9599121bt1abdq0sblcEXDk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TyVJgwcP1tSpPFAIABAYXBZ7AwYM0J49e7R3715JUrdu3XTOOef4PBgAAIHq7rvv1mOPPSZJuuqqq6zdEH377be6++67lZKS4s94AABIcqPYk6Rt27YpPT1dRUVF2rlzpyRp2rRpPg0GAECgOnLkiHr16lVhfK9evZSenl7zgQAAcMBlsXf99dfrwIED6tu3r0JDQyVJFouFYg8AUGfl5OQ4fe306dM1FwQAgEq4LPa2b9+u3bt3y2Kx1EQeAAACXnx8vF5++WXddNNNNuNfeeUV9e/f30+pAACw5bLYi4uL048//qh27drVRB4AAALe008/rSuvvFLLly+3Fnfbt29XQUGB/vvf//o5Xe3g6T+RjTE+SgIAwctlsffzzz+rZ8+eGjhwoM4991zr+HXr1vk0GAAAgapNmzb6/PPP9dFHH+m7776TJF122WUaMWKEn5NBKi0kFyxYoOHDh7s1PYUkgGDlstibO3duDcQAAKD2GT58uNsFBQAANc1lsTd06FAdOnRIaWlpGjVqlE6dOqXi4uKayAYAAAAAqKIQVxO8/PLLmjBhgm6++WZJUlZWlq644gpf5wIAAAAAVIPLYu/555/XZ599pqZNm0qSunTpop9++snnwQAAAAAAVeey2Dv33HNVr14963BRURHdMAAAAABAgHNZ7A0dOlSPPvqoTp8+rffff1/XXHONEhISaiIbAAAAAKCKXBZ78+fPV+vWrdWrVy/985//1Lhx4/Twww/XRDYAACBpzZo1uummmzRp0iS99957/o4DAKglXBZ7ISEhuummm7R69Wq98cYbuummm7iMEwCAapo5c6YiIiIUFxdnM37Dhg3q1q2bYmJiNH/+fEnSFVdcoZdfflkvvfSSXn/9dX/EBQDUQi6LvfPPP1+dOnWq8AUAAKpuxowZ2rBhg8244uJi3XbbbVq/fr12796tFStWaPfu3dbXH374Yd122201HRUAUEu57Gdv+/bt1p/z8/O1evVqHT9+3KehAAAIdkOGDFF6errNuK1btyomJsb6T9XJkydr7dq16tGjh+6//35deuml6tevn9M2k5OTlZycLEnKzMxUampqtTJGRUVpwYIF1WrDWzxZlwULFniUvbrbydtOnjwZcJk8UZvzk90/anN2KbDzuyz2WrZsaTN89913q3///nrooYd8FgoAgLooKytL0dHR1uGo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atu2rY4cOeLndKWKi4vVt29fRUREaPTo0ercubOaNWumsLAwSVJUVJSysrL8nFK6++679fe//10hIaW727FjxwIyZxmLxaIxY8aof//+Sk5OlqSA2wcOHjyo1q1b64YbbtAFF1ygG2+8UXl5eQGX097KlSs1ZcoUSYG3TSMjIzVnzhydd955ateuncLDw9W/f/+A3Ffj4uK0adMmHTt2TKdOndI777yjjIyMgNum5TnLlpWVpejoaOt0gbKN67La/J7Uxv3MneONQMvvyfFH+exhYWEKDw/XsWPH/Jbdk2OSQMsueXaMEkj7jafHLYGUvTx3jmMCLTvFngMnT57U1VdfraefflpNmza1ec1ischisfgpma3Q0FB99dVXyszM1NatW7Vnzx5/R6rgrbfeUkREhPr37+/vKG779NNPtXPnTq1fv17PP/+8PvnkE5vXA2EfKCoq0s6dOzV79mx9+eWXatSoUYX7agIhZ3kFBQVat26drrnmmgqvBULWX375RWvXrtXBgweVnZ2tvLw8bdiwwa+ZnOnRo4fuu+8+jRkzRpdccon69u2r0NBQm2kCYZs6E8jZEDxqw35WW4437NWG4w9HauMxib3acIziSG08brEX6McxzlDs2SksLNTVV1+tqVOn6qqrrpIktWnTRocPH5YkHT58WBEREf6MWEGzZs00fPhwbd68WTk5OSoqKpIkZWZmKjIy0q/ZPvvsM61bt04dO3bU5MmT9eGHH+quu+4KuJzllWWJiIjQlVdeqa1btwbcPhAVFaWoqCgNGjRIkjRhwgTt3Lkz4HKWt379evXr109t2rSRFHi/Vx988IHOP/98tW7dWuecc46uuuoqffbZZwG7r86aNUs7duzQJ598oubNm6tr164Bt03Lc5YtMjJSGRkZ1ukCaRvXVbX5PalN+5knxxuBmF9y7/ijfPaioiLl5uaqZcuWfsnr6TFJIGUv48kxSiDtN54etwRS9jLuHscEWnaKvXKMMZo1a5Z69Oihe+65xzo+MTFRS5culSQtXbpU48eP91dEq6NHjyonJ0eSdPr0ab3//vvq0aOHhg8frjfeeENSYGR97LHHlJmZqfT0dK1cuVIjRozQ8uXLAy5nmby8PJ04ccL683vvvae4uLiA2wfatm2r6Oho7d27V5K0ceNG9ezZM+BylrdixQrrpQ9S4P1enXfeedqy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6tWrp4EDByoqKkqSrPcOhoeHq127dhowYIAkqWnTppKka665Rn/729/0xBNP6F//+pdmzJhRYVmffvqp7rjjDklS9+7d1aFDB+3bt8/ahiMffvihli1bJqn0Xsbw8HCb140x+tOf/qRPPvlEISEhysrK0pEjR9S2bVub6QYMGGD9PO3cubPGjBkjSerVq5f1DONFF12kGTNmaOLEibrqqqtcbjvAExR7gA99//33Cg0NVUREhCSpUaNGDqeLjIxUy5Yt9c033+j111/XSy+9JKn0w+Q///mPunXrZjP9F1984bSt48eP68MPP9S3334ri8Wi4uJiWSwWPfHEE05zGmMUGxurzZs3V2U1AQBBqvxnjTFG//d//6ebb77ZZpqnn37a6fxhYWHWB5Xl5+fbtPXss89q7NixNtOnpqbq3HPPtQ6HhoaqqKjIafsNGzbU6NGjtXbtWq1atUo7duxwa72qa/ny5Tp69Kh27Nihc845Rx07drRZvzLl1yUkJMQ6HBISYl2vl156SV988YXefvtt9e/fXzt27FDLli1rZD0Q/LhnD/CRo0eP6pZbbtHtt98ui8XicvpJkybp73//u3Jzc9W7d29J0tixY/Xss89a71f48ssvXbbzxhtv6Prrr9ehQ4eUnp6ujIwMnX/++dq0aZMuuugi/ec//1FJSYmOHDmi1NRUSVK3bt109OhRa7FXWFhocw8hAABjx47Vv/71L+sDSrKysvTTTz9pyJAhWrNmjU6fPq0TJ04oJSXFOk/Hjh2tBVjZWbyytl588UUVFhZKkvbt26e8vDyny+7WrZsOHz6sbdu2SZJOnDhhLZZuvPFG3XnnnRowYICaN29eYd7f//73Wr58uXU5P/zwQ4V/otobOXKkXnzxRUmlV7vk5ubavJ6bm6uIiAidc845+uijj3To0KFK26vMgQMHNGjQID300ENq3bq1MjIyqtwWYI9iD/Ci06dPW7teGDVqlMaMGWNzA3tlJkyYoJUrV2rixInWcQ8++KAKCwvVu3dvxcbG6sEHH3TZzooVK3TllVfajLv66qu1YsUKXX311YqKilLPnj113XXXqV+/fgoPD1e9evX0xhtv6L777lOfPn3Ut2/fCjejAwDqtjFjxujaa6/V4MGD1atXL02YMEEnTpxQv379NGnSJPXp00eXXnqp9VJLSZozZ45efPFFXXDBBfr555+t42+88Ub17NlT/fr1U1xcnG6++eZKz+DVq1dPr7/+uu644w716dNHo0ePtp5J69+/v5o2baobbrjB4by33nqrSkpK1KtXL02aNElLliyxOePmyDPPPKOPPvpIvXr1Uv/+/bV7926b16dOnart27erV69eWrZsmbp37+5y+znzxz/+Ub169VJcXJz1ATSAt9D1AlDHnDx5Uo0bN9axY8c0cOBAffbZZxXuMQAAoKrmzp2rxo0ba86cOTWyvOzsbA0bNkx79uwJqq4hAG/gnj2gjrn88suVk5OjgoICPfjggxR6AIBaa9myZXrggQf05JNPUugBDnBmDwAAAACCEP8CAQAAAIAgRLEHAAAAAEGIYg8AAAAAghDFHgAAAAAEIYo9AAAAAAhC/x9EGOISXG8oWgAAAABJRU5ErkJggg==", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.ioff()\n", - "fig, ax = plt.subplots(1,2,figsize=(15,7))\n", - "fig.patch.set_facecolor('white')\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=\"#ffb700\")\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], color=\"black\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "5c8310ab", - "metadata": {}, - "source": [ - "We split the data into a training and a test set, but we also keep a part of the data to be used for calibration. This calibration set is not used for training, nor for testing the model. Thus we ensure better generalization of the quantized model." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "d81db277", - "metadata": {}, - "outputs": [], - "source": [ - "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" - ] - }, - { - "cell_type": "markdown", - "id": "4690cc15", - "metadata": {}, - "source": [ - "## Simple single variable insurance incident frequency predictor\n", - "\n", - "Our initial example only uses a single predictor feature, so we can easily visualize results. " - ] - }, - { - "cell_type": "markdown", - "id": "faa5247c", - "metadata": {}, - "source": [ - "We first train the scikit-learn PoissonRegressor model:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "682fb2d8", - "metadata": {}, - "outputs": [], - "source": [ - "reg = PoissonRegressor(max_iter=300)\n", - "reg.fit(df_train[\"DrivAge\"].values.reshape(-1,1), df_train[\"Frequency\"]);" - ] - }, - { - "cell_type": "markdown", - "id": "084fb296", - "metadata": {}, - "source": [ - "We can now test this predictor on the test data:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "4953b03e", - "metadata": {}, - "outputs": [], - "source": [ - "test_data = np.sort(df_test[\"DrivAge\"].values).reshape(-1,1)\n", - "predictions = reg.predict(test_data)" - ] - }, - { - "cell_type": "markdown", - "id": "f28155cf", - "metadata": {}, - "source": [ - "Let's visualize our predictions to see how our model performs !" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "111574ed", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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BQWSpF27dmny5MkKCgqSv7+//vCHP5ifda3TP8Nu3brJ09PTrvoB4EwIzQBajVGjRumGG27QPffcI6kmQM2ePVvHjh0zp+LiYt13330KDg7WkSNHVFJSYr5/37599bbp5uZmPg8PD9df//pXm+2VlpZq5MiRkqQ77rhDW7Zs0Y4dO7Rr1y49++yzkqShQ4dq6dKlOnjwoKZOnaqrrrqq3n5CQkK0b98+VVdXm8tycnIUGhp61m2xZMkSlZeXKzQ0VKNGjdJbb72lo0ePatCgQQ2+p+6xno0zHWeXLl308ccf61e/+pXZ63268PBwRUdH27TvyZMn9emnn0qSbrnlFsXHx2v37t06ceKEnnjiCRmG0azHAACNITQDaFXuvPNOffHFF9q2bZuuu+46LV++XP/5z39UVVWlsrIyrVmzRrm5uYqMjFRSUpIefvhhlZeXa8OGDVq+fPkZt33zzTfrySef1H//+19J0vHjx/X+++9LqhlasGnTJlVUVMjHx0ft27eXu7u7ysvLlZqaquPHj8vLy0v+/v42vcm1hg8fro4dO+qZZ55RRUWF1qxZo+XLl+vqq68+q3YYNWqUXn31VV1yySWSpNGjR+vVV1/VRRddJA8Pjwbf06NHD+3du/es9mfPcY4ePVqpqam64oortHnz5nrbGDZsmPz8/PT000+rtLRUVVVV2r59u7799ltJ0smTJ+Xv7y9fX1/t3LlTf/nLX86qVgA4G4RmAK1KQECA5syZo0cffVTh4eFaunSpnnjiCQUEBCg8PFzPPvus2ZubmpqqDRs2qFu3bvrjH/+omTNnql27do1ue9q0afr973+vq6++Wv7+/urXr58+++wzSdKJEyc0b948denSRZGRkerWrZt+97vfSZLefvttRUVFyd/fX6+//rpSU1Prbdvb21vLly/XZ599pu7du+vWW2/V//t//0/x8fFn1Q6jRo3SyZMnzdB80UUXqaSkxJxvyP3336/HHntMnTt3bvTKF2diz3EmJyfrH//4hy6//HJt3brV5jUPDw99/PHHSk9PV3R0tLp37665c+fq+PHjkqTnnntO//rXv+Tn56d58+Zp5syZTa4RAM6Wm3H637YAoI2aOXOm4uPjzR8SAgBQi55mAG3Wt99+qz179qi6ulorVqzQ0qVLNXXqVGeXBQBwQS0Wmm+88UYFBgaqX79+5rIjR44oOTlZsbGxSk5O1tGjR1tq9wBg6cCBAxo9erR8fX11xx136C9/+YsSExOdXRYAwAW12PCMr7/+Wr6+vpozZ455V6x7771XXbt21X333aennnpKR48e1dNPP90SuwcAAACaTYuOac7KytLkyZPN0BwXF6c1a9YoODhY+fn5Gj16NBeZBwAAgMtz6JjmgoIC8w5ZQUFB5gXrAQAAAFfmab1Ky3BzczvjRehTUlKUkpIiSdq5c+dZX3YJAAAAsEdWVla9O43Wcmho7tGjh/Lz883hGYGBgY2uO3/+fM2fP1+SlJSUpLS0NEeVCQAAgDYoKSmp0dccOjxjypQpeuuttyRJb731ln75y186cvcAAADAWWmx0HzNNdfoggsuUEZGhsLCwvTGG2/ovvvu0xdffKHY2FitXLlS9913X0vtHgAAAGg2LTY8Y9GiRQ0u//LLL1tqlwAAAECLcNoPAQEAAJpbRUWFcnNzVVZW5uxS4MLat2+vsLAweXl52f0eQjMAAGg1cnNz5efnp6ioqDNepQttl2EYOnz4sHJzcxUdHW33+xz6Q0AAAICWVFZWpm7duhGY0Sg3Nzd169atyX+NIDQDAIBWhcAMK2dzjhCaAQAAmpGHh4cGDRpkTllZWVqzZo0mT558VttLT0/Xp59+2qT3jB49ukXucbFmzRp98803zb7dWjfccIOWLFlyxuVz587Vjh07WqyGxjCmGQAAoBl16NBB6enpNsuysrLOenvp6elKS0vTpEmTzq2ws1BZWSlPz5/j4po1a+Tr66uRI0darttS/v73v7f4PhpCTzMAAIADHTlyRFOnTtWAAQM0YsQIff/995KkzZs364ILLlBiYqJGjhypjIwMlZeX68EHH9S7776rQYMG6d1337XZVlVVle655x7169dPAwYM0CuvvFJvf59//rkuuOACDR48WFdeeaWKiookSY8++qiGDh2qfv36af78+TIMQ1JNL/Wdd96ppKQkvfTSS+Z2srKy9Prrr+uFF17QoEGDtHbtWt1www26+eabNXz4cN17773as2ePJk6cqCFDhujiiy/Wzp07JdX0FN9xxx0aOXKkYmJizF5jwzB0++23Ky4uTuPGjdPBgwct269uL7qvr68WLlyogQMHasSIESooKJAkFRYWavr06Ro6dKiGDh2q9evXN+kzagg9zQAAoFW688476/X4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7RoYGKhp06Zp8+bNfOc4QFhYmMLCwjR8+HBJ0owZM7R161ba3oE+++wzDR48WD169JDEv7WOsHLlSkVHRysgIEBeXl664oortH79er7vm4DQ/D+GYeimm25SQkKC7r77bnP5lClT9NZbb0mS3nrrLf3yl790VomtVmFhoY4dOyZJKi0t1RdffKGEhASNGTNGS5YskUTbt5Qnn3xSubm5ysrK0uLFizV27FilpqbS9g5QXFyskydPms8///xz9evXj+8cBwgKClJ4eLgyMjIkSV9++aX69OlD2zvQokWLzKEZEv/WOkJERIQ2btyokpISGYZhnvd839uPm5v8z7p163TxxRerf//+5tjOJ554QsOHD9dVV12lnJwcRUZG6r333lPXrl2dXG3r8v333+v6669XVVWVqqurddVVV+nBBx/U3r17dfXVV+vIkSNKTEzUO++8o3bt2jm73FZrzZo1eu655/Txxx/T9g6wd+9eTZs2TVLNcIFrr71WCxcu1OHDh/nOcYD09HTNnTtX5eXliomJ0Ztvvml+/9D2Lau4uFgRERHau3evOnXqJEmc9w7y0EMP6d1335Wnp6cSExP197//Xfv37+f73k6EZgAAAMACwzMAAAAAC4RmAAAAwAKhGQAAALBAaAYAAAAsEJoBAAAAC4RmAHAwDw8PDRo0SH379tXAgQP1/PPPq7q6usF18/LyNGPGjBar5aOPPpKbmxt34QQAC1xyDgAczNfXV0VFRZKkgwcP6tprr9WFF16oRx55xGa9yspKeXp6Nss+G9vWzJkzlZeXp7Fjx9bbPwDgZ/Q0A4ATBQYGKiUlRa+++qoMw9A///lPTZkyRWPHjtWll16qrKws9evXT5I0YsQI/fe//zXfO3r0aKWlpam4uFg33nijhg0bpsTERC1dulSS6m3rdEVFRVq3bp3eeOMNLV682FxeXV2tW2+9VfHx8UpOTtakSZPMO4Zt2bJFo0aN0pAhQzRhwgTz1scA0NoRmgHAyWJiYlRVVaWDBw9KkrZu3aolS5boq6++sllv5syZeu+99yRJ+fn5ys/PV1JSkh5//HGNHTtWmzdv1urVq/W73/1OxcXFZ9yWJC1dulQTJ05U79691a1bN23ZskWS9O9//1tZWVnasWOH3n77bW3YsEGSVFFRod/85jdasmSJtmzZohtvvFELFy5ssXYBAFfSPH/3AwA0m+Tk5AZvIXzVVVdp/PjxeuSRR/Tee++ZY50///xzLVu2TM8995wkqaysTDk5OWfcliQtWrRICxYskCRdffXVWrRokYYMGaJ169bpyiuvlLu7u4KCgjRmzBhJUkZGhrZv367k5GRJUlVVlYKDg5v34AHARRGaAcDJ9u7dKw8PDwUGBkqSfHx8GlwvNDRU3bp10/fff693331Xr7/+uiTJMAx98MEHiouLs1l/06ZNjW7ryJEjWrVqlX744Qe5ubmpqqpKbm5uevbZZxut0zAM9e3b1+x5BoC2hOEZAOBEhYWFuvnmm3X77bfLzc3Ncv2ZM2fqmWee0fHjxzVgwABJ0oQJE/TKK6+o9nfd3333neV2lixZotmzZys7O1tZWVnat2+foqOjtXbtWl144YX64IMPVF1drYKCAq1Zs0aSFBcXp8LCQpvhGnXHWANAa0ZoBgAHKy0tNS85N27cOI0fP14PPfSQXe+dMWOGFi9erKuuuspc9sADD6iiokIDBgxQ37599cADD1huZ9GiRZo2bZrNsunTp2vRokWaPn26wsLC1KdPH1133XUaPHiwOnXqJG9vby1ZskS///3vNXDgQA0aNEjffPNN0w4eAM5TXHIOAFBPUVGRfH19dfjwYQ0bNkzr169XUFCQs8sCAKdhTDMAoJ7Jkyfr2LFjKi8v1wMPPEBgBtDm0dMMAAAAWGBMMwAAAGCB0AwAAABYIDQDAAAAFgjNAAAAgAVCMwAAAGCB0AwAAABY+P9p0pjROOzIegAAAABJRU5ErkJggg==", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.clf()\n", - "fig, ax = plt.subplots(1,figsize=(12,8))\n", - "fig.patch.set_facecolor('white')\n", - "ax.plot(test_data, predictions, color=\"black\", label=f\"Float clear trend line\")\n", - "ax.scatter(df_test[\"DrivAge\"], df_test[\"Frequency\"], marker=\"o\", color=\"#ffb700\")\n", - "ax.set_xlabel(\"Driver Age\")\n", - "ax.set_ylim(0,10)\n", - "ax.set_title(\"Regression with sklearn\")\n", - "ax.set_ylabel(\"Frequency of claims\")\n", - "ax.legend(loc=\"upper right\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "429d8cc8", - "metadata": {}, - "source": [ - "### Analysis\n", - "\n", - "The trend line obtained from the model suggests an increase of incidents with driver age, but the data shows that incidents peak around the ages of 30 to 40 years of age with a decrease afterwards. This simple model does not seem to be a good one. We convert it to FHE to show visually some details of the conversion. In the second part of this example we train a more powerful model." - ] - }, - { - "cell_type": "markdown", - "id": "2d959640", - "metadata": {}, - "source": [ - "### FHE models need to be quantized, so let's define a **Quantized Poisson Regressor**\n", - "\n", - "We use the quantization primitives available in the Concrete library: QuantizedArray, QuantizedFunction, and QuantizedLinear to define a Poisson Regressor which is a Generalized Linear Model with exponential link." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "9f5acbfe", - "metadata": {}, - "outputs": [], - "source": [ - "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", - "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", - " self.q_calibration_data = QuantizedArray(n_bits, calibration_data)\n", - "\n", - " q_weights = QuantizedArray(2, np.expand_dims(sklearn_model.coef_,1), is_signed=False)\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", - " # 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", - " super().__init__(quant_layers_dict)\n", - "\n", - "\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", - "\n", - " 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" - ] - }, - { - "cell_type": "markdown", - "id": "ab82ae87", - "metadata": {}, - "source": [ - "### We can now convert the scikit-learn model to our quantized version\n", - "\n", - "First, we get the calibration data, and we then run it through the non-quantized (float) 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": 12, - "id": "09d12194", - "metadata": {}, - "outputs": [], - "source": [ - "calib_data = np.expand_dims(df_calib[\"DrivAge\"].values, 1)\n", - "n_bits = 5\n", - "q_glm = QuantizedGLM(n_bits, reg, calib_data)" - ] - }, - { - "cell_type": "markdown", - "id": "e2528092", - "metadata": {}, - "source": [ - "Once the model's parameters and input ranges are quantized, we can quantize our test data and perform quantized inference. " - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "f0f0699a", - "metadata": {}, - "outputs": [], - "source": [ - "q_test_data = q_glm.quantize_input(test_data)\n", - "y_pred = q_glm.forward_and_dequant(q_test_data)\n" - ] - }, - { - "cell_type": "markdown", - "id": "a5a50eb8", - "metadata": {}, - "source": [ - "Let's visualize the results of the quantized model. We can measure the goodness of fit on the test data using the Poisson deviance. We then plot the two trend lines (float value model and quantized model) to check for differences. " - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "04777aeb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "mean Poisson deviance (float): 3.7115219475021872\n", - "mean Poisson deviance (quant): 3.716861851757367\n" - ] - } - ], - "source": [ - "from sklearn.metrics import mean_poisson_deviance\n", - "\n", - "y_gt = df_test[\"Frequency\"]\n", - "gt_weight = df_test[\"Exposure\"]\n", - "\n", - "dev_real = mean_poisson_deviance(y_gt, predictions, sample_weight=gt_weight)\n", - "dev_q = mean_poisson_deviance(y_gt, y_pred, sample_weight=gt_weight)\n", - "\n", - "print(f\"mean Poisson deviance (float): {dev_real}\")\n", - "print(f\"mean Poisson deviance (quant): {dev_q}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "5fb15eb4", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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dly7do2IiAh69eqlXhb+bsuWLVOH6lu3bhEfH6+JZjU7+fn50vY2pq22G9p22zVJQvMDsra2Vq+0t2jRIkxNTfnTn/7U4OfHx8djamraoNDcEBKahWj+ysrKiIqKYsqUKTz66KM1Hre3tycwMBATExNMTEwYMGAAR48epWfPntjZ2QFVQzbGjRtHYmJiraE5Ojqa6OhooGqe5rY6d2tbnre2rba9rbYb2nbbNanNDM9ITk5myZIlvP766yxZsoTk5ORGP8bBgwcZOHAg/v7+DB06VL1C3UcffYS7uzteXl5MnDiR9PR0li5dygcffFBjlT+o6sUeMmQIHh4ezJw5s9rqdWPHjsXf3x8PDw91b9LChQspKirCx8dHvYpfbdsJIbRHURRmzJiBm5sbCxYsqHWbMWPGkJycTHl5OYWFhezfvx83NzcKCgrUK28WFBSwZcsWevfurcnyhRCizWsTPc3JycnExMRQVlYGQE5ODjExMQB4eno2yjEUReG5555j/fr12NrasmbNGl5++WX++9//8tZbb5GWloahoSG3b9/G0tKS2bNn19k7/frrr9OvXz9effVVYmNjqy25/N///hcrKyuKiooIDAwkKiqKt956i48//ljd813XdtbW1o3SViHE/duzZw8rV65UTxsHVZ8QXbhwAYDZs2fj5uZGnz598PLyQkdHh5kzZ9K7d2/Onz/PuHHjgKohHJMnT2bYsGHaaooQQrRJbSI0x8XFqQPzHWVlZcTFxTVaaC4pKeH48eNEREQAUFFRQefOnQHw8vJiypQpjB07lrFjx9a7r507d/LTTz8BMHLkSNq3b69+7KOPPmLdunVA1byvqamptYbhhm4nhNCMfv36VfvUqC4TJ05k6dKl1e5zdnbm6NGjTVWaEEKIBmgToTknJ+e+7n8QiqLg4eHBvn37ajwWGxvLzp07iYmJ4c0333zgoSHx8fFs27aNffv20a5dO8LCwiguLn7g7YQQQgghRMO0iTHNFhYW93X/gzA0NOT69evq0FxWVsaJEyeorKxUrwT29ttvk5OTQ35+PmZmZuoxir83YMAAvv32WwA2bdrErVu3gKqQ3759e9q1a8fp06dJSEhQP0dfX7/a8JO6thNCCCGEEPevTYTm8PBw9PX1q92nr6+vnjKuMejo6LB27VpeeuklvL298fHxYe/evVRUVDB16lQ8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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.clf()\n", - "fig, ax = plt.subplots(1,figsize=(12,8))\n", - "fig.patch.set_facecolor('white')\n", - "#ax.set_yscale(\"log\")\n", - "ax.plot(test_data, predictions, color=\"black\", label=f\"Float clear trend line, d={dev_real:.3f}\")\n", - "ax.scatter(df_test[\"DrivAge\"], df_test[\"Frequency\"], marker=\"o\", color=\"gray\", label=\"Test data\")\n", - "ax.set_xlabel(\"Driver Age\")\n", - "ax.set_ylim(0,10)\n", - "ax.set_title(\"Poisson Regression, float in clear vs. quantized \")\n", - "ax.set_ylabel(\"Frequency of claims\")\n", - "ax.plot(test_data, y_pred, color=\"red\",label=f\"Quantized trend line, d={dev_q:.3f}\")\n", - "ax.legend(loc=\"upper left\")\n", - "ax.grid()\n", - "\n", - "# inset axes....\n", - "axins = ax.inset_axes([0.5, 0.5, 0.47, 0.47])\n", - "axins.plot(test_data, predictions, color=\"black\", label=f\"Float clear trend line, d={dev_real:.3f}\")\n", - "axins.plot(test_data, y_pred, color=\"red\",label=f\"Quantized trend line, d={dev_q:.3f}\")\n", - "# sub region of the original image\n", - "x1, x2, y1, y2 = 60, 65, 2.3, 2.7\n", - "axins.set_xlim(x1, x2)\n", - "axins.set_ylim(y1, y2)\n", - "#axins.set_xticklabels([])\n", - "#axins.set_yticklabels([])\n", - "axins.grid()\n", - "ax.indicate_inset_zoom(axins, edgecolor=\"black\")\n", - "\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "aa8854b2", - "metadata": {}, - "source": [ - "### Analysis\n", - "\n", - "We see, in the graph above, that the trend line of the quantized model is more jaggy and has slightly higher deviance. The tradeoff between better fit and compatibility with FHE compilation needs to be made by the practitioner." - ] - }, - { - "cell_type": "markdown", - "id": "af6bc89e", - "metadata": {}, - "source": [ - "### Now it's time to make the inference homomorphic. Compiling a model to FHE is done with a single line of code\n" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "fe9935bd", - "metadata": {}, - "outputs": [], - "source": [ - "engine = q_glm.compile(q_test_data)" - ] - }, - { - "cell_type": "markdown", - "id": "46753da7", - "metadata": {}, - "source": [ - "And now we can test the model on the test set in FHE:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "ca928b78", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 100/100 [00:21<00:00, 4.72it/s]\n" - ] - } - ], - "source": [ - "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)" - ] - }, - { - "cell_type": "markdown", - "id": "68f67b3f", - "metadata": {}, - "source": [ - "Finally we check if there are any differences to the quantized model on non-encrypted clear data by plotting the trend lines. Sometimes, FHE noise can create minor artifacts." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "92c7f2f5", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.clf()\n", - "fig, ax = plt.subplots(1,figsize=(12,8))\n", - "fig.patch.set_facecolor('white')\n", - "ax.plot(test_data, predictions, color=\"black\", label=f\"Float clear trend line, d={dev_real:.3f}\")\n", - "ax.plot(test_data, y_pred_fhe, color=\"blue\", label=f\"FHE quantized trend line\")\n", - "ax.scatter(df_test[\"DrivAge\"], df_test[\"Frequency\"], marker=\"o\", color=\"gray\", label=\"Test data\")\n", - "ax.set_xlabel(\"Driver Age\")\n", - "ax.set_ylim(0,10)\n", - "ax.set_title(\"Poisson Regression, float in clear vs. quantized FHE encrypted\")\n", - "ax.set_ylabel(\"Frequency of claims\")\n", - "ax.plot(test_data, y_pred, color=\"red\",label=f\"Quantized trend line, d={dev_q:.3f}\")\n", - "ax.legend(loc=\"upper left\")\n", - "ax.grid()\n", - "\n", - "axins = ax.inset_axes([0.5, 0.5, 0.47, 0.47])\n", - "axins.plot(test_data, predictions, color=\"black\", label=f\"Float clear trend line, d={dev_real:.3f}\")\n", - "axins.plot(test_data, y_pred, color=\"red\",label=f\"Quantized FHE trend line, d={dev_q:.3f}\")\n", - "axins.plot(test_data, y_pred_fhe, color=\"blue\", label=f\"FHE quantized trend line\")\n", - "x1, x2, y1, y2 = 60, 65, 2.3, 2.7\n", - "axins.set_xlim(x1, x2)\n", - "axins.set_ylim(y1, y2)\n", - "axins.grid()\n", - "ax.indicate_inset_zoom(axins, edgecolor=\"black\")\n", - "\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "14394b94", - "metadata": {}, - "source": [ - "## A multi-variate model\n", - "\n", - "The simple single variable model does not achieve good results (age is not a good predictor for the number of claims). Let's train a model with all of our predictor variables. We proceed by transforming the raw features into ones that can be input to a regression model. Thus, the categorical features are transformed into one-hot encoding, but we also reduce the resolution of vehicle and person by binning. Transforming the data this way, we end up with a total of 57 continuous features (instead of the initial 11).\n", - "\n", - "Here is where we encounter one of the limitations of our framework. We perform a dot product in the prediction, in the QuantizedLinear class, but in our framework the maximum integer size is, for now, limited to 7 bits. As every multiplication doubles the number of bits of precision of the inputs performing 57 multiplication-additions of integers to compute w.x would quickly overflow 7 bits. \n", - "\n", - "As a workaround to the limited accumulator resolution, we perform PCA to reduce dimensionality from 57 to 14 dimensions and train our multi-variate model in this reduced dimensionality space. However, we also train a reference model on all of the original features. " - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "759507c5", - "metadata": {}, - "outputs": [], - "source": [ - "from sklearn.metrics import mean_poisson_deviance\n", - "\n", - "from sklearn.compose import ColumnTransformer\n", - "from sklearn.pipeline import Pipeline, make_pipeline\n", - "from sklearn.preprocessing import (\n", - " FunctionTransformer,\n", - " KBinsDiscretizer,\n", - " OneHotEncoder,\n", - " StandardScaler,\n", - ")\n", - "import warnings\n", - "warnings.filterwarnings('ignore')\n", - "\n", - "log_scale_transformer = make_pipeline(\n", - " FunctionTransformer(np.log, validate=False), StandardScaler()\n", - ");\n", - "\n", - "linear_model_preprocessor = ColumnTransformer(\n", - " [\n", - " (\"passthrough_numeric\", \"passthrough\", [\"BonusMalus\"]),\n", - " (\"binned_numeric\", KBinsDiscretizer(n_bins=10), [\"VehAge\", \"DrivAge\"]),\n", - " (\"log_scaled_numeric\", log_scale_transformer, [\"Density\"]),\n", - " (\n", - " \"onehot_categorical\",\n", - " OneHotEncoder(sparse=False),\n", - " [\"VehBrand\", \"VehPower\", \"VehGas\", \"Region\", \"Area\"],\n", - " ),\n", - " ],\n", - " remainder=\"drop\",\n", - ");\n", - "\n", - "poisson_glm = Pipeline(\n", - " [\n", - " (\"preprocessor\", linear_model_preprocessor),\n", - " (\"regressor\", PoissonRegressor(alpha=1e-12, max_iter=300)),\n", - " ]\n", - ");\n", - "\n", - "poisson_glm_pca = Pipeline(\n", - " [\n", - " (\"preprocessor\", linear_model_preprocessor),\n", - " (\"pca\", PCA(n_components=14, whiten=True)),\n", - " (\"regressor\", PoissonRegressor(alpha=1e-12, max_iter=300)),\n", - " ]\n", - ");\n", - "\n", - "poisson_glm.fit(df_train, df_train[\"Frequency\"], regressor__sample_weight=df_train[\"Exposure\"])\n", - "\n", - "poisson_glm_pca.fit(\n", - " df_train, df_train[\"Frequency\"], regressor__sample_weight=df_train[\"Exposure\"]\n", - ");" - ] - }, - { - "cell_type": "markdown", - "id": "bfbd0ff1", - "metadata": {}, - "source": [ - "### Now we evaluate the new models" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "0ffae598", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "PoissonRegressor evaluation: 1.3773\n", - "PoissonRegressor+PCA evaluation: 1.4399\n" - ] - } - ], - "source": [ - "def score_estimator(y_pred, y_gt, gt_weight):\n", - " \"\"\"Score an estimator on the test set.\"\"\"\n", - " y_pred = np.squeeze(y_pred)\n", - " dev = mean_poisson_deviance(y_gt, y_pred, sample_weight=gt_weight)\n", - " return dev\n", - "\n", - "\n", - "def score_sklearn_estimator(estimator, df_test):\n", - " \"\"\"A wrapper to score a sklearn pipeline on a dataframe\"\"\"\n", - " return score_estimator(estimator.predict(df_test), df_test[\"Frequency\"], df_test[\"Exposure\"])\n", - "\n", - "\n", - "def score_concrete_glm_estimator(poisson_glm_pca, q_glm, df_test):\n", - " \"\"\"A wrapper to score QuantizedGLM on a dataframe, transforming the dataframe using\n", - " a sklearn pipeline\n", - " \"\"\"\n", - " test_data = poisson_glm_pca[\"pca\"].transform(poisson_glm_pca[\"preprocessor\"].transform(df_test))\n", - " q_test_data = q_glm.quantize_input(test_data)\n", - " y_pred = q_glm.forward_and_dequant(q_test_data)\n", - " return score_estimator(y_pred, df_test[\"Frequency\"], df_test[\"Exposure\"])\n", - "\n", - "\n", - "print(f\"PoissonRegressor evaluation: {score_sklearn_estimator(poisson_glm, df_test):.4f}\")\n", - "print(f\"PoissonRegressor+PCA evaluation: {score_sklearn_estimator(poisson_glm_pca, df_test):.4f}\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "de58b9eb", - "metadata": {}, - "source": [ - "### Test the multi-variate GLM with multiple quantization bit-widths" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "bce8b011", - "metadata": {}, - "outputs": [], - "source": [ - "# Now, get calibration data from the held out set\n", - "calib_data = poisson_glm_pca[\"pca\"].transform(\n", - " poisson_glm_pca[\"preprocessor\"].transform(df_calib)\n", - ")\n", - "\n", - "# Let's see how performance decreases with bit-depth.\n", - "# This is just a test of our quantized model, not in FHE\n", - "n_bits_test = np.asarray([28, 16, 6, 5, 4, 3, 2])\n", - "dev_bits_test = np.zeros_like(n_bits_test,dtype=np.float32)\n", - "for i, n_bits in enumerate(n_bits_test):\n", - " q_glm = QuantizedGLM(n_bits, poisson_glm_pca[\"regressor\"], calib_data)\n", - " dev_bits_test[i] = score_concrete_glm_estimator(poisson_glm_pca, q_glm, df_test)\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "6dcb5f7e", - "metadata": {}, - "source": [ - "We plot the Poisson deviance with respect to the quantized bit-width, to show how performance degrades with quantization:" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "0e3c4858", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtoAAAHgCAYAAACb58plAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/MnkTPAAAACXBIWXMAAAsTAAALEwEAmpwYAABSsklEQVR4nO3deVzUdeLH8fdwI5eK4AFepCKHioJ45VWJ5pZXaVl55FWZbW27VntU1m6bbbtdduq2lh1q2bltGViRR6Wiosl44IGpyQBeHILAzPz+MPlpiqAyfGeG1/Px2H0wM9/5ft/fLzPxnq+f+XxNdrvdLgAAAAB1ysPoAAAAAIA7omgDAAAADkDRBgAAAByAog0AAAA4AEUbAAAAcACKNgAAAOAAXkYHqEuNGzdWhw4djI4BF1NSUqKAgACjYwBwAFd+f7ty9tpw9/2rCxwj55WTk6OCgoIal3Orot28eXNlZGQYHQMuJj09XYMGDTI6BgAHcOX3tytnrw1337+6wDFyXklJSbVajqEjAAAAgANQtAEAAAAHoGgDAAAADuBWY7TPp6KiQgcOHFBZWZnRUeCkQkJCtG3bNqNj4Ax+fn6KjIyUt7e30VEAALhkbl+0Dxw4oKCgILVr104mk8noOHBCRUVFCgoKMjoGfmG323X48GEdOHBA7du3NzoOAACXzO2HjpSVlSk0NJSSDbgIk8mk0NBQ/hUKAODy3L5oS6JkAy6G9ywAwB00iKJtNE9PTyUkJCg+Pl5jx47ViRMnql32008/1dy5c+sx3bkCAwMv6Xk///yzbrzxxjpOUzsvvPCCYmJidOuttxqy/dr6+9//ftbtvn37XvY633jjDc2aNeu894eFhSkhIUEJCQmaOHGiJGny5MlatmzZWcue/p3n5OTI39+/6jkJCQlatGjROeuurKzUn/70J3Xs2LFquSeeeOKc9Z1pzpw5MplM2rVrV9V9zz33nEwmE/PfAwDcEkW7Hvj7+yszM1Nbt26Vj4+PXn311WqXHTFihB566KF6TFd3WrVqdU6Bqy8vv/yy0tLS9M4779Rq+crKSgcnOr9fF+3vvvvOodu76aablJmZqczMzPMW5vO54oorqp6TmZlZVdDP9Je//EU///yzfvzxR2VmZmrVqlWqqKiocd1dunTRkiVLqm6///77iouLq/0OAQDgQija9ax///7atWuXjhw5olGjRqlr167q3bu3tmzZIunss5Pvv/++4uPj1a1bNw0YMECSlJWVpeTkZCUkJKhr167Kzs6WJD3zzDOKj49XfHy8nnvuOUmnzk7GxMRo+vTpiouLU0pKikpLS8/JtHfvXvXp00ddunTRX/7yl7Mee/rpp9WzZ0917dpVjz76qCTpoYce0ksvvVS1zJw5c/TPf/5TOTk5io+Pr9p2//791aNHD/Xo0aOqUJ6+ytWNN96ozp0769Zbb5XdbpckrV+/Xn379lW3bt2UnJysoqIiWa1WzZ49uyrDa6+9dk7+O++8U3v27NG1116rZ599ttpjO2fOHE2YMEH9+vXThAkTzlqH3W7XrFmzFB0drWuuuUbDhw+v+tDQrl27qsusZmRkVF2la926derTp4+6d++uvn37aseOHVW/wzFjxmjYsGHq2LGjHnjggarjVlpaqoSEhKoz76fP/D7yyCNVZ4YjIiJ0++23S5Lefvvtqt/3HXfcIavVKklauHChOnXqpOTkZK1Zs+acY+JIJ06c0IIFCzRv3jz5+flJkoKCgjRnzpwanztq1Ch98sknkqTdu3crJCREzZo1c2RcAAAM4/azjpzpsf9myfxzYZ2uM7ZVsB69vnZn5CorK/XFF19o2LBhevTRR9W9e3d9/PHH+vrrrzVx4kRlZmaetfzjjz+uL7/8UhERETp27Jgk6dVXX9W9996rW2+9VeXl5bJardqwYYMWLlyotWvXym63q1evXho4cKCaNGmi7OxsLV68WAsWLNC4ceP0wQcf6LbbbjtrO/fee6/uuusuTZw48awCnZqaquzsbK1bt052u10jRozQypUrddNNN+m+++7T3XffLUl677339OWXX1aVQEkKDw9XWlqa/Pz8lJ2drfHjx1cND9i0aZOysrLUqlUr9evXT2vWrFFycrJuuukmLV26VD179lRhYaH8/f31+uuvKyQkROvXr9fJkyfVr18/paSknDUbxauvvqrly5frm2++UbNmzXTPPfdUe2zNZrNWr14tf3//s47BRx99pB07dshsNstisSg2NlZTpky54O+zc+fOWrVqlby8vLRixQr96U9/0gcffCBJyszM1KZNm+Tr66vo6Gjdc889mjt3rl588cVzfs+nf9ePP/64jh07pv79+2vWrFnatm2bli5dqjVr1sjb21szZ87UO++8oyFDhujRRx/Vhg0bFBISosGDB6t79+7nzbh06VKtXr266vd8usDPnj1bf/vb3877nN27dyshIaHq9rx589S/f/+q27t27VKbNm0uaaaW4OBgtW7dWlu3btUnn3yim266SQsXLrzo9QAA4AoaVNE2yumzmNKpM9pTp05Vr169qkrZVVddpcOHD6uw8OwPAf369dPkyZM1btw4jRkzRpLUp08fPfHEEzpw4IDGjBmjjh07avXq1Ro9erQCAgIkSWPGjNGqVas0YsQItW/fvmrbiYmJysnJOSffmjVrqrJMmDBBDz74oKRTRTs1NbWqxBUXFys7O1tTp05VXl6efv75Z+Xn56tJkyZq3br1WeuuqKjQrFmzlJmZKU9PT+3cubPqseTkZEVGRkqSEhISlJOTo5CQELVs2VI9e/aUdKqQnc6wZcuWqrPLx48fV3Z29gWnfVu9enW1x3bEiBHnlGxJWrlypcaPHy9PT0+1atVKV111VbXrP+348eOaNGmSsrOzZTKZzho6cfXVVyskJESSFBsbq3379ql169YXXJ/dbtdtt92m+++/X4mJiXrxxRe1YcOGqmNSWlqq8PBwrV27VoMGDVJYWJikU8NDzjy+Z7rpppv04osvnnP/008/fdZ4+jPHVJ8eOlJbCxcu1PPPP6/Dhw/ru+++q3E/b775Zi1ZskRffvmlvvrqK4o2AMBtNaiiXdszz3Xt9Bjti/Xqq69q7dq1+t///qfExERt2LBBt9xyi3r16qX//e9/Gj58+HmHUpzJ19e36mdPT8/zDh2Rzj/Lg91u1x//+Efdcccd5zw2duxYLVu2TLm5ubrpppvOefzZZ59V8+bNtXnzZtlstqohBufLdKHx0na7XfPmzdPQoUOrXeZinP4wcjG8vLxks9kk6awp5x5++GENHjxYH330kXJycqqGlEgXt4+nzZkzR5GRkVVnne12uyZNmqQnn3zyrOU+/vjji96HutShQwf99NNPVfOP33777br99tsVHx9/1r9qVOe6667T7NmzlZSUVPWBCgAAd8QYbYP079+/6ot76enpatas2TmlY/fu3erVq5cef/xxhYWFaf/+/dqzZ4+ioqL029/+ViNHjtSWLVvUv39/ffzxxzpx4oRKSkr00UcfnfVP/TXp169f1RfUzvwy4dChQ/Wf//xHxcXFkqSDBw8qLy9P0qkzpUuWLNGyZcs0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cXrggQcMSgegrh07dkw33nijOnfurJiYGH3//fdGR6qVdu3aqUuXLlV/y9yV1WpV9+7ddd111xkdxemUlZUpOTlZ3bp1U1xcnB599FGjI+EX1XXQ6rjk0JFDhw7p0KFD6tGjh4qKipSYmKiPP/5Y9913n373u9/p2muv1eeff65//OMfSk9PNzounMTKlSsVGBioiRMnauvWrZKkb775Rk888YT+97//ydfXV3l5eQoPDzc4KYC6MGnSJPXv31/Tpk1TeXm5Tpw4ocaNGxsdq0bt2rVTRkaGmjVrZnQUh3rmmWeUkZGhwsJCffbZZ0bHcSp2u10lJSUKDAxURUWFrrzySj3//PPq3bu30dEavOo6aGxs7HmXd8kz2i1btlSPHj0kSUFBQYqJidHBgwdlMplUWFgo6dQFFFq1amVkTDiZAQMGqGnTpmfd98orr+ihhx6Sr6+vJFGyATdx/PhxrVy5UlOnTpUk+fj4uETJbigOHDig//3vf5o2bZrRUZySyWRSYGCgJKmiokIVFRUymUwGp4JUfQetjksW7TPl5ORo06ZN6tWrl5577jnNnj1brVu31h/+8Ac9+eSTRseDk9u5c6dWrVqlXr16aeDAgVq/fr3RkQDUgb179yosLEy33367unfvrmnTpqmkpMToWLViMpmUkpKixMREp70Q0OW677779I9//EMeHi5fQxzGarUqISFB4eHhGjJkiHr16mV0JPzKmR20Oi79Ci8uLtYNN9yg5557TsHBwXrllVf07LPPav/+/Xr22WerzmQA1amsrNSRI0f0ww8/6Omnn9a4cePkgqOpAPxKZWWlNm7cqLvuukubNm1SQECA5s6da3SsWlm9erU2btyoL774Qi+99JJWrlxpdKQ69dlnnyk8PFyJiYlGR3Fqnp6eyszM1IEDB7Ru3bqqIY9wDr/uoNVx2aJdUVGhG264QbfeeqvGjBkjSXrzzTerfh47dixfhkSNIiMjNWbMGJlMJiUnJ8vDw0MFBQVGxwJwmSIjIxUZGVl1punGG2/Uxo0bDU5VOxEREZJODWUbPXq02/0tW7NmjT799FO1a9dON998s77++mvddtttRsdyWo0bN9bgwYO1fPlyo6PgF+froNVxyaJtt9s1depUxcTE6P7776+6v1WrVvr2228lSV9//bU6duxoVES4iFGjRumbb76RdGoYSXl5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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from matplotlib import pyplot as plt\n", - "plt.clf()\n", - "fig, ax = plt.subplots(1, figsize=(12,8)) \n", - "fig.patch.set_facecolor(\"white\")\n", - "ax.plot(n_bits_test, dev_bits_test, label=\"Poisson deviance for quantized FHE GLM\")\n", - "ax.set_xlim(2,28)\n", - "ax.invert_xaxis()\n", - "ax.set_xlabel(\"Number of bits\")\n", - "ax.set_ylabel(\"Poisson deviance\")\n", - "ax.set_xscale(\"log\")\n", - "ax.set_xticks(n_bits_test)\n", - "ax.set_xticklabels([str(k) for k in n_bits_test])\n", - "ax.grid()\n", - "ax.legend(loc=\"upper left\")\n", - "display(fig)" - ] - }, - { - "cell_type": "markdown", - "id": "43e6fd06", - "metadata": {}, - "source": [ - "### Analysis\n", - "\n", - "While the prediction quality is mostly stable until 6 bits, we see a decrease in prediction performance in lower bit-widths. For 4 bits the performance seems to improve, but this is probably just a lucky sampling of the data, as this graph shows a single experiment. We expect to have a smooth increase of the deviance with lower bit-width when running the experiment multiple times.\n", - "\n", - "With 14 features, we can have weights and data in at most 2 bits. " - ] - }, - { - "cell_type": "markdown", - "id": "1ac216b1", - "metadata": {}, - "source": [ - "We now choose an operating point that is compatible with FHE: 2 bit quantization." - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "3c521ec8", - "metadata": {}, - "outputs": [], - "source": [ - "q_glm = QuantizedGLM(2, poisson_glm_pca[\"regressor\"], calib_data)\n", - "test_data = poisson_glm_pca[\"pca\"].transform(poisson_glm_pca[\"preprocessor\"].transform(df_test))\n", - "q_test_data = q_glm.quantize_input(test_data)" - ] - }, - { - "cell_type": "markdown", - "id": "a7f45c8c", - "metadata": {}, - "source": [ - "### Compile the multi-variate GLM to FHE. Again, with a single line of code we compile to FHE:" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "f89eaa07", - "metadata": {}, - "outputs": [], - "source": [ - "engine = q_glm.compile(q_test_data)" - ] - }, - { - "cell_type": "markdown", - "id": "baa0667b", - "metadata": {}, - "source": [ - "Finally, we evaluate the model on encrypted data:" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "f6fe2737", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 100/100 [00:40<00:00, 2.50it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "PoissonRegressor evaluation: 1.3773\n", - "PoissonRegressor+PCA evaluation: 1.4399\n", - "FHE Quantized deviance: 1.6530\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "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)\n", - "\n", - "dev_pca_quantized_fhe = score_estimator(y_pred_fhe, df_test[\"Frequency\"], df_test[\"Exposure\"])\n", - "\n", - "print(f\"PoissonRegressor evaluation: {score_sklearn_estimator(poisson_glm, df_test):.4f}\")\n", - "print(f\"PoissonRegressor+PCA evaluation: {score_sklearn_estimator(poisson_glm_pca, df_test):.4f}\")\n", - "print(f\"FHE Quantized deviance: {dev_pca_quantized_fhe:.4f}\")" - ] - }, - { - "cell_type": "markdown", - "id": "c18dbdd1", - "metadata": {}, - "source": [ - "### Conclusion\n", - "\n", - "In this tutorial, we have discussed how we can use Concrete Numpy to convert a scikit-learn based Poisson regression model to FHE. \n", - "\n", - "First of all, we have shown that with the proper choice of pipeline and parameters, we can do the conversion with little loss of precision. This decrease in the quality of prediction is due to quantization of model weights and input data, and some minor noise can appear due to FHE. This noise is visible on the single variable FHE trend line as minor deviations of the blue curve with respect to the red one. \n", - "\n", - "Finally, we have shown how conversion of a model to FHE can be done with a single line of code and how quantization is aided by the tools in Concrete Numpy. \n" - ] - } - ], - "metadata": { - "execution": { - "timeout": 10800 - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/user/advanced_examples/figures/QuantizationVisualized.svg b/docs/user/advanced_examples/figures/QuantizationVisualized.svg deleted file mode 100644 index 78da7838b..000000000 --- a/docs/user/advanced_examples/figures/QuantizationVisualized.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -min(x)min(x)max(x)max(x)Mapto 0Map...Mapto 1Map...DistanceBetweenConsecutiveValuesDistan...Mapto 2Map...Mapto 3Map...(when n = 2)(when n = 2)00= 1 / scale= 1 / q= 1 / scale...x = (x  + zp ) / q x = (x  + zp ) / q qqxxxxzero pointzp = 2zero point...Viewer does not support full SVG 1.1 \ No newline at end of file diff --git a/docs/user/advanced_examples/index.rst b/docs/user/advanced_examples/index.rst deleted file mode 100644 index 08db99740..000000000 --- a/docs/user/advanced_examples/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -Advanced examples -================= - -.. toctree:: - :maxdepth: 1 - - FullyConnectedNeuralNetwork.ipynb - LinearRegression.ipynb - LogisticRegression.ipynb - PoissonRegression.ipynb - DecisionTreeClassifier.ipynb diff --git a/docs/user/basics/compiling_and_executing.md b/docs/user/basics/compiling_and_executing.md index dd298fb56..560acae7a 100644 --- a/docs/user/basics/compiling_and_executing.md +++ b/docs/user/basics/compiling_and_executing.md @@ -107,4 +107,3 @@ Today, we cannot simulate a client / server API in python, but it is for very so - [Working With Floating Points Tutorial](../tutorial/working_with_floating_points.md) - [Table Lookup Tutorial](../tutorial/table_lookup.md) -- [Compiling a torch model](../howto/compiling_torch_model.md) diff --git a/docs/user/basics/index.rst b/docs/user/basics/index.rst index 36d2387ef..2050fd6b1 100644 --- a/docs/user/basics/index.rst +++ b/docs/user/basics/index.rst @@ -7,5 +7,4 @@ Getting Started intro.md installing.md compiling_and_executing.md - ../howto/compiling_torch_model.md benchmarks.md diff --git a/docs/user/basics/intro.md b/docs/user/basics/intro.md index 3d6b770a0..aed7e6625 100644 --- a/docs/user/basics/intro.md +++ b/docs/user/basics/intro.md @@ -14,7 +14,6 @@ With **Concrete Numpy**, data scientists can implement machine learning models u **Concrete Numpy** is made of several parts: - an entry API, which is the main function of the so-called **Concrete frontend**, which takes programs made from a subset of numpy, and converts them to an FHE program - the **Concrete compiler**, which is called by the frontend, which allows you to turn an MLIR program into an FHE program, on the top of **Concrete Library**, which contains the core cryptographic APIs for computing with FHE; -- some ML tools, in an early version, allowing for example to turn some torch programs into numpy, and then to use the main API stack to finally get an FHE program. In a further release, **Concrete Numpy** will be divided into a **Concrete Framework** package, containing the compiler, the core lib and the frontend(s), and in a **Concrete ML**, which will contain ML tools, made on top of the **Concrete Framework**. Names of these packages are succeptible to change. @@ -36,5 +35,3 @@ The main _current_ limits are: - **Concrete** only supports unsigned integers - **Concrete** needs integers to fit in a maximum of 7 bits - **Concrete** computations are exact (except a very small probability) for computations on 6 bits or less, and exact at a probability close to 90% for 7 bits computations - -To overcome the above limitations, Concrete has a [popular quantization](../explanation/quantization.md) method built in the framework that allows map floating point values to integers. We can [use this approach](../howto/use_quantization.md) to run models in FHE. Lastly, we give hints to the user on how to [reduce the precision](../howto/reduce_needed_precision.md) of a model to make it work in Concrete. diff --git a/docs/user/explanation/fhe_and_framework_limits.md b/docs/user/explanation/fhe_and_framework_limits.md index 27f3a75d1..b7f02880d 100644 --- a/docs/user/explanation/fhe_and_framework_limits.md +++ b/docs/user/explanation/fhe_and_framework_limits.md @@ -10,7 +10,7 @@ However, one still has to consider that FHE is slow, as compared to the vanilla ### Multiplying by constants -In the scheme used in **Concrete Numpy**, namely [TFHE](https://tfhe.github.io/tfhe/), multiplications by constants is only defined for integer constants. Notably, one can't multiply by floats. As float multiplication is very usual in the data science (think of weights of dense layers, for example), this could be a problem, but quantization is at our rescue. See [this](quantization.md) section for more details. +In the scheme used in **Concrete Numpy**, namely [TFHE](https://tfhe.github.io/tfhe/), multiplications by constants is only defined for integer constants. Notably, one can't multiply by floats. As float multiplication is very usual in the data science (think of weights of dense layers, for example), this could be a problem, but quantization is at our rescue. See [Quantization](https://docs.preprod.zama.ai/concrete-ml/main/user/explanation/quantization.html) section of Concrete ML documentation for more details. ### Achieving computations of not-linear functions @@ -30,5 +30,4 @@ As we explained, we wanted to focus first on cryptographic challenges. Performan ### Currently restricted to 7 bits computations -For the moment, we can only perform computations with 7 bits or less. Furthermore, the exactness of computations is only ensured for 6 bits or less; for 7 bits, the computations are exact with a probability close to 90%. Of course, we are working on increasing this limit, and making the probability of a wrong computation as close to 0% as possible. Don't hesitate to look at our [quantization](quantization.md) section to know how to use smaller integers. - +For the moment, we can only perform computations with 7 bits or less. Furthermore, the exactness of computations is only ensured for 6 bits or less; for 7 bits, the computations are exact with a probability close to 90%. Of course, we are working on increasing this limit, and making the probability of a wrong computation as close to 0% as possible. Don't hesitate to look at [Quantization](https://docs.preprod.zama.ai/concrete-ml/main/user/explanation/quantization.html) section of Concrete ML documentation to know how to use smaller integers. diff --git a/docs/user/explanation/future_features.md b/docs/user/explanation/future_features.md index ee7ee2810..a1017017f 100644 --- a/docs/user/explanation/future_features.md +++ b/docs/user/explanation/future_features.md @@ -11,17 +11,3 @@ for example) and faster production execution (with distribution over a set of ma - **more complete benchmarks**: we will have an extended benchmark, containing lots of functions that you may want to compile; then, we will measure the framework progress by tracking the number of successfully compiled functions over time. Also, this public benchmark will be a way for other competing frameworks or technologies to compare fairly with us, in terms of functionality or performance - **client/server APIs**: today, the `run` function is performing the key generation, the encryption, the inference and the decryption to allow machine learning practitioners to test both performance and accuracy of FHE friendly models. Soon, we are going to have separated APIs to perform the steps one by one, and thus, a full client / server API - **serialization**: we are going to add several utils, to serialize ciphertexts or keys - -## Regarding machine learning - -We will continue to consider our `NPFHECompiler` class (compilation of numpy programs) as the main entry point for **Concrete Numpy**. In the future, we may move all ML tools currently present in **Concrete Numpy** to a new to-be-named ML specific package. - -Our plans to extend machine learning support in the future are: - -- **extend support for torch**: having more layers and more complex `forward `patterns, and also having ready-to-use neural networks and neural network blocks that are compatible with FHE -- **support for other ML frameworks**: we will provide FHE compatible model architectures for classical ML models which will be trainable with popular frameworks such as sklearn. Tools for quantization aware training and FHE compatible algorithms are also in our plans. - -If you are looking for a specific new feature, you can drop a message to . - - - diff --git a/docs/user/explanation/index.rst b/docs/user/explanation/index.rst index 56c90295a..9398e13cb 100644 --- a/docs/user/explanation/index.rst +++ b/docs/user/explanation/index.rst @@ -6,5 +6,4 @@ Explanations what_is_fhe.md fhe_and_framework_limits.md - quantization.md future_features.md diff --git a/docs/user/explanation/quantization.md b/docs/user/explanation/quantization.md deleted file mode 100644 index 60e7fe7b5..000000000 --- a/docs/user/explanation/quantization.md +++ /dev/null @@ -1,47 +0,0 @@ -# Quantization - -```{note} -from [Wikipedia](https://en.wikipedia.org/wiki/Quantization): - -> Quantization is the process of constraining an input from a continuous or otherwise large set of values (such as the real numbers) to a discrete set (such as the integers). -``` - -## Why is it needed? - -Modern computing has long been using data types that use 32 or 64 bits (be that integers or floating point numbers), or even bigger data types. However due to the costly nature of FHE computations (see [the limits of FHE](fhe_and_framework_limits.md)), using such types with FHE is impractical (or plain impossible) to have computations executing in a reasonable amount of time. - -## The gist of quantization - -The basic idea of quantization is to take a range of values represented by a _large_ data type and represent it by a _smaller_ data type. This means some accuracy in the number's representation is lost, but in a lot of cases it is possible to adapt computations to still give meaningful results while using significantly less bits to sent the data used during those computations. - -## Quantization in practice - -Let's first define some notations. Let $ [\alpha, \beta ] $ be the range of our value to quantize where $ \alpha $ is the minimum and $ \beta $ is the maximum. - -To quantize a range with floating point values (in $ \mathbb{R} $) to unsigned integer values (in $ \mathbb{N} $), we first need to choose the data type that is going to be used. **Concrete Library**, the library used in the **Concrete Numpy**, is currently limited to 7 bits unsigned integers, so we'll use that for the example. Knowing that, for a value in the range $ [\alpha, \beta ] $, we can compute the `scale` $ S $ of the quantization: - -$$ S = \frac{\beta - \alpha}{2^n - 1} $$ - - - where $ n $ is the number of bits (here 7). In practice the quantization scale is then $ S = \frac{\beta - \alpha}{127} $. This means the gap between consecutive representible values cannot be smaller than that $ S $ value which means there can be a substantial loss of precision. Every interval of length $ S $ will be represented by a value within the range $ [0..127] $. - -The other important parameter from this quantization schema is the `zero point` $ Z $ value. This essentially brings the 0 floating point value to a specific integer. Doing this allows us to have an asymetric quantization where the resulting integer is in the unsigned integer realm, $ \mathbb{N} $. - -$$ Z = \mathtt{round} \left(- \frac{\alpha}{S} \right) $$ - -There is more mathematics involved in how computations change when replacing floating point values by integers for a fully connected or a convolution layer. The IntelLabs distiller quantization documentation goes into a [detailed explanation](https://intellabs.github.io/distiller/algo_quantization.html) about the maths to quantize values and how to keep computations consistent. - -Regarding quantization and FHE compilation, it is important to understand the difference between two modes: - -1. the quantization is done before the compilation; notably, the quantization is completely controlled by the user, and can be done by any means, including by using third party frameworks -2. the quantization is done during the compilation (inside our framework), with much less control by the user. - -For the moment, only the second method is available in **Concrete Numpy**, but we plan to have the first method available in a further release, since it should give more freedom and better results to the user. - -We detail the use of quantization within **Concrete Numpy** in [here](../howto/use_quantization.md). - -## Resources - -- IntelLabs distiller explanation of quantization: [Distiller documentation](https://intellabs.github.io/distiller/algo_quantization.html) -- Lei Mao's blog on quantization: [Quantization for Neural Networks](https://leimao.github.io/article/Neural-Networks-Quantization/) -- Google paper on Neural Network quantization and integer only inference: [Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference](https://arxiv.org/abs/1712.05877) diff --git a/docs/user/howto/compiling_torch_model.md b/docs/user/howto/compiling_torch_model.md deleted file mode 100644 index c0d4f9ab3..000000000 --- a/docs/user/howto/compiling_torch_model.md +++ /dev/null @@ -1,67 +0,0 @@ -# Compiling a Torch Model - -**Concrete Numpy** allows you to compile a torch model to its FHE counterpart. - - -A simple command can compile a torch model to its FHE counterpart. This process executes most of the concepts described in the documentation on [how to use quantization](use_quantization.md) and triggers the compilation to be able to run the model over homomorphically encrypted data. - - -```python -from torch import nn -import torch -class LogisticRegression(nn.Module): - """LogisticRegression with Torch""" - - def __init__(self): - super().__init__() - self.fc1 = nn.Linear(in_features=14, out_features=1) - self.sigmoid1 = nn.Sigmoid() - - - def forward(self, x): - """Forward pass.""" - out = self.fc1(x) - out = self.sigmoid1(out) - return out - -torch_model = LogisticRegression() -``` - -```{warning} -Note that the architecture of the neural network passed to be compiled must respect some hard constraints given by FHE. Please read the our [detailed documentation](../howto/reduce_needed_precision.md) on these limitations. -``` - -Once your model is trained you can simply call the `compile_torch_model` function to execute the compilation. - - -```python -from concrete.torch.compile import compile_torch_model -import numpy -torch_input = torch.randn(100, 14) -quantized_numpy_module = compile_torch_model( - torch_model, # our model - torch_input, # a representative inputset to be used for both quantization and compilation - n_bits = 2, -) -``` - -You can then call `quantized_numpy_module.forward_fhe.run()` to have the FHE inference. - -Now your model is ready to infer in FHE settings. - - -```python -enc_x = numpy.array([numpy.random.randn(14)]).astype(numpy.uint8) # An example that is going to be encrypted, and used for homomorphic inference. -fhe_prediction = quantized_numpy_module.forward_fhe.run(enc_x) -``` - -`fhe_prediction` contains the clear quantized output. The user can now dequantize the output to get the actual floating point prediction as follows: - - -```python -clear_output = quantized_numpy_module.dequantize_output( - numpy.array(fhe_prediction, dtype=numpy.float32) -) -``` - -If you want to see more compilation examples, you can check out the [Fully Connected Neural Network](../advanced_examples/FullyConnectedNeuralNetwork.ipynb) diff --git a/docs/user/howto/index.rst b/docs/user/howto/index.rst index b3855438b..35b0a075b 100644 --- a/docs/user/howto/index.rst +++ b/docs/user/howto/index.rst @@ -6,8 +6,6 @@ How To numpy_support.md printing_and_drawing.md - compiling_torch_model.md - use_quantization.md reduce_needed_precision.md debug_support_submit_issues.md faq.md diff --git a/docs/user/howto/reduce_needed_precision.md b/docs/user/howto/reduce_needed_precision.md index 1a9bce372..d494212fa 100644 --- a/docs/user/howto/reduce_needed_precision.md +++ b/docs/user/howto/reduce_needed_precision.md @@ -61,16 +61,10 @@ The input contains 28x28x8 = 6272 bits of information. In practice you could sti This shows how adapting your data can allow you to use models that may require smaller data types (i.e. use less precision) to perform their computations. -```{note} -Binarizing here is an extreme case of quantization which is introduced [here](../explanation/quantization.md). You can also find further resources on the linked page. -``` - ### Model accuracy when quantizing for FHE Quantization and binarization increase inference speed, reduce model byte-size and are required to run computation in FHE. However, quantization and, especially, binarization, induce a loss in the accuracy of the model since it's representation power is diminished. Choosing quantization parameters carefully can alleviate the accuracy loss all the while allowing compilation to FHE. -This is illustrated in both advanced examples [Linear Regression](../advanced_examples/LinearRegression.ipynb) and [Logistic Regression](../advanced_examples/LogisticRegression.ipynb). - The end result has a granularity/imprecision linked to the data types used and for the Quantized Logistic Regression to the lattice used to evaluate the logistic model. ## Limitations for FHE friendly neural network diff --git a/docs/user/howto/use_quantization.md b/docs/user/howto/use_quantization.md deleted file mode 100644 index 6275cb23f..000000000 --- a/docs/user/howto/use_quantization.md +++ /dev/null @@ -1,159 +0,0 @@ -# Using Quantization in **Concrete Numpy** - -In this section we detail some usage of [quantization](../explanation/quantization.md) as implemented in **Concrete**. - -## Quantization Basics - -**Concrete Numpy** implements some basic concepts of quantization. The very basic purpose of it is to convert floating point values to integers. We can apply such conversion using `QuantizedArray` available in `concrete.quantization`. - -`QuantizedArray` takes 2 arguments: -- `n_bits` that defines the precision of the quantization. Currently, `n_bits` is limited to 7, due to some **Concrete Library** limits. -- `values` that will be converted to integers - -```python -from concrete.quantization import QuantizedArray -import numpy -numpy.random.seed(0) -A = numpy.random.uniform(-2, 2, 10) -# array([ 0.19525402, 0.86075747, 0.4110535, 0.17953273, -0.3053808, -# 0.58357645, -0.24965115, 1.567092 , 1.85465104, -0.46623392]) -q_A = QuantizedArray(7, A) -q_A.qvalues -# array([ 37, 73, 48, 36, 9, -# 58, 12, 112, 127, 0]) -# the quantized integers values from A. -q_A.scale -# 0.018274684777173276, the scale S. -q_A.zero_point -# 26, the zero point Z. -q_A.dequant() -# array([ 0.20102153, 0.85891018, 0.40204307, 0.18274685, -0.31066964, -# 0.58478991, -0.25584559, 1.57162289, 1.84574316, -0.4751418 ]) -# Dequantized values. -``` - -## Neural networks in the Quantized Realm - -Neural networks are implemented with a diverse set of operations, such as convolution, linear transformations, activation functions and element-wise operations. When working with quantized values, these operations can not be carried out the same way as for floating point values. With quantization it is necessary to re-scale the input and output values of each operation to fit in the quantization domain. - -Re-scaling raw input values to the quantized domain implies that we need to make use of floating point operations. In the FHE setting where we only work with integers, this could be a problem, but luckily, the FHE implementation behind **Concrete Numpy** provides a workaround. We essentially make use of a [table lookup](../tutorial/table_lookup.md) which is later translated into a [PBS](https://whitepaper.zama.ai). - -Of course, having a PBS for every quantized addition isn't recommended for computational cost reasons. Also, **Concrete Numpy** allows PBS only for univariate operations (i.e. matrix multiplication can't be done in a PBS). Therefore, our quantized modules split the computation of floating point values and unsigned integers as it is currently done in `concrete.quantization.QuantizedLinear`. - - -The above operations are all implemented in **Concrete Numpy** and transparent to the user via our Quantized Modules. - -**Concrete Numpy** allows you to convert numpy operations to their FHE counterparts. This essentially opens the door to any python computing framework such as [PyTorch](https://pytorch.org/). **Concrete Numpy** implements a Torch to Numpy converter that makes it easy for the user to use a torch model. - -First we define a model: - - -```python -from torch import nn -import torch -class LogisticRegression(nn.Module): - """LogisticRegression with Torch""" - - def __init__(self): - super().__init__() - self.fc1 = nn.Linear(in_features=14, out_features=1) - self.sigmoid1 = nn.Sigmoid() - - - def forward(self, x): - """Forward pass.""" - out = self.fc1(x) - out = self.sigmoid1(out) - return out - -torch_model = LogisticRegression() -``` - -We then convert this model to numpy only operations: - -```python -from concrete.torch import NumpyModule -numpy_model = NumpyModule(torch_model) -``` - -The `NumpyModule` allows us to runs inference as for a `nn.Module`. Here, the prediction of the numpy module should be exactly the same. - -We can then quantize the numpy module with `PostTrainingAffineQuantization` as follows: - - -```python -from concrete.quantization import PostTrainingAffineQuantization -numpy_input = numpy.random.uniform(-1, 1, size=(10,14)) # some input with 14 features to calibrate the quantization -n_bits = 2 # number of bits of precision for the weights, activation, inputs and outputs. -post_training_quant = PostTrainingAffineQuantization(n_bits, numpy_model) -quantized_numpy_module = post_training_quant.quantize_module(numpy_input) -``` - -Here, the quantized model takes a quantized array and runs inference in the quantized paradigm. - -We can then easily verify that all models give similar predictions. Obviously, the `n_bits` chosen may adversely affect the prediction of the `quantized_numpy_module`. You can try increasing this parameter to see the effect on your model but keep in mind that the compilation will require all the values of your network to be less than 7 bits of precision. - - -```python -torch_model(torch.from_numpy(numpy_input).float()) -# tensor([[-0.0690], -# [-0.1108], -# [-0.0743], -# [-0.0464], -# [ 0.0261], -# [-0.1380], -# [-0.0941], -# [-0.1589], -# [ 0.0374], -# [-0.1088]]) -numpy_model(numpy_input) -# array([[-0.06901879], -# [-0.11081327], -# [-0.07429631], -# [-0.04636377], -# [ 0.02613242], -# [-0.13795333], -# [-0.09408965], -# [-0.15885062], -# [ 0.03735061], -# [-0.10878125]]) -quantized_numpy_module.forward_and_dequant(QuantizedArray(2, numpy_input)) -# array([[-0.03792994], -# [-0.15551274], -# [-0.03792994], -# [ 0.08154936], -# [ 0.08154936], -# [-0.15551274], -# [-0.03792994], -# [-0.15551274], -# [ 0.08154936], -# [-0.15551274]]) -``` - -```{warning} -The current implementation of the framework parses the layers in the order of their definition in the nn.Module. Thus, the order of instantiation of the layers in the constructor (init function) is crucial for the conversion to numpy to work properly. -``` - -```{warning} -Do not reuse a layer or an activation multiple times in the forward (i.e. self.sigmoid for each layer activation) and always place them at the correct position (the order of appearance in the forward function) in the init function. -``` - -It is now possible to compile the `quantized_numpy_module`. Details on how to compile the model are available in the [torch compilation documentation](compiling_torch_model.md). -## Building your own QuantizedModule - -**Concrete Numpy** also offers the possibility to build your own models and use them in the FHE settings. The `QuantizedModule` is a very simple abstraction that allows to create any model using the available operators: - -- QuantizedSigmoid, the quantized version of `nn.Sigmoid` -- QuantizedLinear, the quantized version of `nn.Linear` -- QuantizedReLU6, the quantized version of `nn.ReLU6` - - -A well detailed example is available for a [Linear Regression](../advanced_examples/LinearRegression.ipynb). - - -## Future releases - -Currently, the quantization is only available via `PostTrainingAffineQuantization` which is a [popular](https://arxiv.org/pdf/1712.05877.pdf) approach for quantization but has some constraints. - -In future releases we plan to offer the possibility to the user to apply quantization beforehand and convert the model directly to our `QuantizedModule`. This will allow users to take advantage of Quantization Aware Training (QAT) that allow neural networks to reach better accuracies. - diff --git a/docs/user/index.rst b/docs/user/index.rst index 64a6fe87a..1f2e2230f 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -7,5 +7,4 @@ User guide Getting started Tutorial How To - Advanced examples Explanations diff --git a/poetry.lock b/poetry.lock index f3262a377..363066618 100644 --- a/poetry.lock +++ b/poetry.lock @@ -58,6 +58,20 @@ lazy-object-proxy = ">=1.4.0" typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<1.14" +[[package]] +name = "asttokens" +version = "2.0.5" +description = "Annotate AST trees with source code positions" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[package.extras] +test = ["astroid", "pytest"] + [[package]] name = "atomicwrites" version = "1.4.0" @@ -159,6 +173,7 @@ optional = false python-versions = ">=3.6" [package.dependencies] +lockfile = {version = ">=0.9", optional = true, markers = "extra == \"filecache\""} msgpack = ">=0.5.2" requests = "*" @@ -355,6 +370,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] testing = ["pre-commit"] +[[package]] +name = "executing" +version = "0.8.2" +description = "Get the currently executing AST node of a frame, and other information" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "flake8" version = "4.0.1" @@ -462,7 +485,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "4.10.0" +version = "4.10.1" description = "Read metadata from Python packages" category = "dev" optional = false @@ -509,7 +532,7 @@ python-versions = "*" [[package]] name = "ipykernel" -version = "6.6.1" +version = "6.7.0" description = "IPython Kernel for Jupyter" category = "dev" optional = false @@ -530,15 +553,16 @@ test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "ipyparallel"] [[package]] name = "ipython" -version = "7.31.0" +version = "8.0.0" description = "IPython: Productive Interactive Computing" category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" [package.dependencies] appnope = {version = "*", markers = "sys_platform == \"darwin\""} backcall = "*" +black = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" jedi = ">=0.16" @@ -547,10 +571,11 @@ pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} pickleshare = "*" prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" -traitlets = ">=4.2" +stack-data = "*" +traitlets = ">=5" [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +all = ["Sphinx (>=1.3)", "curio", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.19)", "pandas", "pygments", "pytest", "pytest-asyncio", "qtconsole", "testpath", "trio"] doc = ["Sphinx (>=1.3)"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] @@ -558,7 +583,8 @@ nbformat = ["nbformat"] notebook = ["notebook", "ipywidgets"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] +test = ["pytest", "pytest-asyncio", "testpath", "pygments"] +test_extra = ["pytest", "testpath", "curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.19)", "pandas", "pygments", "trio"] [[package]] name = "ipython-genutils" @@ -643,17 +669,9 @@ 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.3.3" +version = "4.4.0" description = "An implementation of JSON Schema validation for Python" category = "dev" optional = false @@ -686,7 +704,7 @@ qtconsole = "*" [[package]] name = "jupyter-client" -version = "7.1.0" +version = "7.1.1" description = "Jupyter protocol implementation and client libraries" category = "dev" optional = false @@ -957,11 +975,11 @@ testing = ["beautifulsoup4", "coverage", "docutils (>=0.17.0,<0.18.0)", "pytest [[package]] name = "nbclient" -version = "0.5.9" +version = "0.5.10" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7.0" [package.dependencies] jupyter-client = ">=6.1.5" @@ -970,9 +988,8 @@ nest-asyncio = "*" traitlets = ">=4.2" [package.extras] -dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] -test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] +test = ["ipython", "ipykernel", "ipywidgets (<8.0.0)", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "xmltodict", "black", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)"] [[package]] name = "nbconvert" @@ -1079,7 +1096,7 @@ test = ["pytest (>=6.2)", "pytest-cov (>=2.12)", "codecov (>=2.1)"] [[package]] name = "notebook" -version = "6.4.6" +version = "6.4.7" description = "A web-based notebook environment for interactive computing" category = "dev" optional = false @@ -1109,7 +1126,7 @@ test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "re [[package]] name = "numpy" -version = "1.22.0" +version = "1.22.1" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false @@ -1137,26 +1154,6 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" -[[package]] -name = "pandas" -version = "1.3.5" -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" @@ -1222,17 +1219,16 @@ python-versions = ">=3.6" [[package]] name = "pip-audit" -version = "1.1.1" +version = "1.1.2" description = "A tool for scanning Python environments for known vulnerabilities" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -CacheControl = ">=0.12.10" -cyclonedx-python-lib = ">=0.11.1" +CacheControl = {version = ">=0.12.10", extras = ["filecache"]} +cyclonedx-python-lib = ">=0.11.1,<1.0.0" html5lib = ">=1.1" -lockfile = ">=0.12.2" packaging = ">=21.0.0" pip-api = ">=0.0.26" progress = ">=1.6" @@ -1347,6 +1343,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "pure-eval" +version = "0.2.1" +description = "Safely evaluate AST nodes without side effects" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +tests = ["pytest"] + [[package]] name = "py" version = "1.11.0" @@ -1365,7 +1372,7 @@ python-versions = "*" [[package]] name = "py-progress-tracker" -version = "0.3.3" +version = "0.4.0" description = "A simple benchmarking library" category = "dev" optional = false @@ -1488,11 +1495,11 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyrsistent" -version = "0.18.0" +version = "0.18.1" description = "Persistent/Functional/Immutable data structures" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "pytest" @@ -1805,37 +1812,6 @@ python-versions = ">=3.7" [package.extras] idna2008 = ["idna"] -[[package]] -name = "scikit-learn" -version = "1.0.2" -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" @@ -1871,7 +1847,7 @@ win32 = ["pywin32"] [[package]] name = "setuptools-scm" -version = "6.3.2" +version = "6.4.1" description = "the blessed package to manage your versions by scm tags" category = "main" optional = false @@ -1882,7 +1858,8 @@ packaging = ">=20.0" tomli = ">=1.0.0" [package.extras] -toml = ["setuptools (>=42)", "tomli (>=1.0.0)"] +test = ["pytest (>=6.2)", "virtualenv (>20)"] +toml = ["setuptools (>=42)"] [[package]] name = "six" @@ -2048,6 +2025,22 @@ python-versions = ">=3.5" lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +[[package]] +name = "stack-data" +version = "0.1.4" +description = "Extract data from python stack frames and tracebacks for informative displays" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +asttokens = "*" +executing = "*" +pure-eval = "*" + +[package.extras] +tests = ["pytest", "typeguard", "pygments", "littleutils"] + [[package]] name = "tabulate" version = "0.8.9" @@ -2094,14 +2087,6 @@ 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" @@ -2126,17 +2111,6 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -[[package]] -name = "torch" -version = "1.10.1" -description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -typing-extensions = "*" - [[package]] name = "tornado" version = "6.1" @@ -2211,7 +2185,7 @@ python-versions = "*" name = "typing-extensions" version = "4.0.1" description = "Backported and Experimental Type Hints for Python 3.6+" -category = "main" +category = "dev" optional = false python-versions = ">=3.6" @@ -2293,7 +2267,7 @@ full = ["pygraphviz"] [metadata] lock-version = "1.1" python-versions = ">=3.8,<3.10" -content-hash = "172f6d91b9fb861532cdadd18d926808b8aff097788f169ab76b3a74d7753544" +content-hash = "70a6a696538026ddba976c0d8dcb191ce48d42a2c822b0e6a9cb1778b9b9c03f" [metadata.files] alabaster = [ @@ -2335,6 +2309,10 @@ astroid = [ {file = "astroid-2.8.6-py3-none-any.whl", hash = "sha256:cd8326b424c971e7d87678609cf6275d22028afd37d6ac59c16d47f1245882f6"}, {file = "astroid-2.8.6.tar.gz", hash = "sha256:5f6f75e45f15290e73b56f9dfde95b4bf96382284cde406ef4203e928335a495"}, ] +asttokens = [ + {file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"}, + {file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -2572,6 +2550,10 @@ execnet = [ {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, ] +executing = [ + {file = "executing-0.8.2-py2.py3-none-any.whl", hash = "sha256:32fc6077b103bd19e6494a72682d66d5763cf20a106d5aa7c5ccbea4e47b0df7"}, + {file = "executing-0.8.2.tar.gz", hash = "sha256:c23bf42e9a7b9b212f185b1b2c3c91feb895963378887bb10e64a2e612ec0023"}, +] flake8 = [ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, @@ -2605,8 +2587,8 @@ imagesize = [ {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.10.0-py3-none-any.whl", hash = "sha256:b7cf7d3fef75f1e4c80a96ca660efbd51473d7e8f39b5ab9210febc7809012a4"}, - {file = "importlib_metadata-4.10.0.tar.gz", hash = "sha256:92a8b58ce734b2a4494878e0ecf7d79ccd7a128b5fc6014c401e0b61f006f0f6"}, + {file = "importlib_metadata-4.10.1-py3-none-any.whl", hash = "sha256:899e2a40a8c4a1aec681feef45733de8a6c58f3f6a0dbed2eb6574b4387a77b6"}, + {file = "importlib_metadata-4.10.1.tar.gz", hash = "sha256:951f0d8a5b7260e9db5e41d429285b5f451e928479f19d80818878527d36e95e"}, ] importlib-resources = [ {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, @@ -2622,12 +2604,12 @@ invoke = [ {file = "invoke-1.6.0.tar.gz", hash = "sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3"}, ] ipykernel = [ - {file = "ipykernel-6.6.1-py3-none-any.whl", hash = "sha256:de99f6c1caa72578305cc96122ee3a19669e9c1958694a2b564ed1be28240ab9"}, - {file = "ipykernel-6.6.1.tar.gz", hash = "sha256:91ff0058b45660aad4a68088041059c0d378cd53fc8aff60e5abc91bcc049353"}, + {file = "ipykernel-6.7.0-py3-none-any.whl", hash = "sha256:6203ccd5510ff148e9433fd4a2707c5ce8d688f026427f46e13d7ebf9b3e9787"}, + {file = "ipykernel-6.7.0.tar.gz", hash = "sha256:d82b904fdc2fd8c7b1fbe0fa481c68a11b4cd4c8ef07e6517da1f10cc3114d24"}, ] ipython = [ - {file = "ipython-7.31.0-py3-none-any.whl", hash = "sha256:4c4234cdcc6b8f87c5b5c7af9899aa696ac5cfcf0e9f6d0688018bbee5c73bce"}, - {file = "ipython-7.31.0.tar.gz", hash = "sha256:346c74db7312c41fa566d3be45d2e759a528dcc2994fe48aac1a03a70cd668a3"}, + {file = "ipython-8.0.0-py3-none-any.whl", hash = "sha256:5b58cf977635abad74d76be49dbb2e97fddd825fb8503083d55496aa1160b854"}, + {file = "ipython-8.0.0.tar.gz", hash = "sha256:004a0d05aeecd32adec4841b6e2586d5ca35785b1477db4d8333a39333e0ce98"}, ] ipython-genutils = [ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, @@ -2653,13 +2635,9 @@ 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.3.3-py3-none-any.whl", hash = "sha256:eb7a69801beb7325653aa8fd373abbf9ff8f85b536ab2812e5e8287b522fb6a2"}, - {file = "jsonschema-4.3.3.tar.gz", hash = "sha256:f210d4ce095ed1e8af635d15c8ee79b586f656ab54399ba87b8ab87e5bff0ade"}, + {file = "jsonschema-4.4.0-py3-none-any.whl", hash = "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"}, + {file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"}, ] jupyter = [ {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"}, @@ -2667,8 +2645,8 @@ jupyter = [ {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, ] jupyter-client = [ - {file = "jupyter_client-7.1.0-py3-none-any.whl", hash = "sha256:64d93752d8cbfba0c1030c3335c3f0d9797cd1efac012652a14aac1653db11a3"}, - {file = "jupyter_client-7.1.0.tar.gz", hash = "sha256:a5f995a73cffb314ed262713ae6dfce53c6b8216cea9f332071b8ff44a6e1654"}, + {file = "jupyter_client-7.1.1-py3-none-any.whl", hash = "sha256:f0c576cce235c727e30b0a0da88c2755d0947d0070fa1bc45f195079ffd64e66"}, + {file = "jupyter_client-7.1.1.tar.gz", hash = "sha256:540ca35e57e83c5ece81abd9b781a57cba39a37c60a2a30c8c1b2f6663544343"}, ] jupyter-console = [ {file = "jupyter_console-6.4.0-py3-none-any.whl", hash = "sha256:7799c4ea951e0e96ba8260575423cb323ea5a03fcf5503560fa3e15748869e27"}, @@ -2978,8 +2956,8 @@ myst-parser = [ {file = "myst_parser-0.15.2-py3-none-any.whl", hash = "sha256:40124b6f27a4c42ac7f06b385e23a9dcd03d84801e9c7130b59b3729a554b1f9"}, ] nbclient = [ - {file = "nbclient-0.5.9-py3-none-any.whl", hash = "sha256:8a307be4129cce5f70eb83a57c3edbe45656623c31de54e38bb6fdfbadc428b3"}, - {file = "nbclient-0.5.9.tar.gz", hash = "sha256:99e46ddafacd0b861293bf246fed8540a184adfa3aa7d641f89031ec070701e0"}, + {file = "nbclient-0.5.10-py3-none-any.whl", hash = "sha256:5b582e21c8b464e6676a9d60acc6871d7fbc3b080f74bef265a9f90411b31f6f"}, + {file = "nbclient-0.5.10.tar.gz", hash = "sha256:b5fdea88d6fa52ca38de6c2361401cfe7aaa7cd24c74effc5e489cec04d79088"}, ] nbconvert = [ {file = "nbconvert-6.4.0-py3-none-any.whl", hash = "sha256:f5ec6a1fad9e3aa2bee7c6a1c4ad3e0fafaa7ff64f29ba56d9da7e1669f8521c"}, @@ -3006,32 +2984,32 @@ networkx = [ {file = "networkx-2.6.3.tar.gz", hash = "sha256:c0946ed31d71f1b732b5aaa6da5a0388a345019af232ce2f49c766e2d6795c51"}, ] notebook = [ - {file = "notebook-6.4.6-py3-none-any.whl", hash = "sha256:5cad068fa82cd4fb98d341c052100ed50cd69fbfb4118cb9b8ab5a346ef27551"}, - {file = "notebook-6.4.6.tar.gz", hash = "sha256:7bcdf79bd1cda534735bd9830d2cbedab4ee34d8fe1df6e7b946b3aab0902ba3"}, + {file = "notebook-6.4.7-py3-none-any.whl", hash = "sha256:968e9c09639fe4b9dbf4b9f028daf861b563c124d735a99d6d48c09317553f31"}, + {file = "notebook-6.4.7.tar.gz", hash = "sha256:b01da66f11a203b3839d6afa4013674bcfff41c36552f9ad0fbcb2d93c92764a"}, ] numpy = [ - {file = "numpy-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d22662b4b10112c545c91a0741f2436f8ca979ab3d69d03d19322aa970f9695"}, - {file = "numpy-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:11a1f3816ea82eed4178102c56281782690ab5993251fdfd75039aad4d20385f"}, - {file = "numpy-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5dc65644f75a4c2970f21394ad8bea1a844104f0fe01f278631be1c7eae27226"}, - {file = "numpy-1.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c16cec1c8cf2728f1d539bd55aaa9d6bb48a7de2f41eb944697293ef65a559"}, - {file = "numpy-1.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97e82c39d9856fe7d4f9b86d8a1e66eff99cf3a8b7ba48202f659703d27c46f"}, - {file = "numpy-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:e41e8951749c4b5c9a2dc5fdbc1a4eec6ab2a140fdae9b460b0f557eed870f4d"}, - {file = "numpy-1.22.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bece0a4a49e60e472a6d1f70ac6cdea00f9ab80ff01132f96bd970cdd8a9e5a9"}, - {file = "numpy-1.22.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:818b9be7900e8dc23e013a92779135623476f44a0de58b40c32a15368c01d471"}, - {file = "numpy-1.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47ee7a839f5885bc0c63a74aabb91f6f40d7d7b639253768c4199b37aede7982"}, - {file = "numpy-1.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a024181d7aef0004d76fb3bce2a4c9f2e67a609a9e2a6ff2571d30e9976aa383"}, - {file = "numpy-1.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f71d57cc8645f14816ae249407d309be250ad8de93ef61d9709b45a0ddf4050c"}, - {file = "numpy-1.22.0-cp38-cp38-win32.whl", hash = "sha256:283d9de87c0133ef98f93dfc09fad3fb382f2a15580de75c02b5bb36a5a159a5"}, - {file = "numpy-1.22.0-cp38-cp38-win_amd64.whl", hash = "sha256:2762331de395739c91f1abb88041f94a080cb1143aeec791b3b223976228af3f"}, - {file = "numpy-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:76ba7c40e80f9dc815c5e896330700fd6e20814e69da9c1267d65a4d051080f1"}, - {file = "numpy-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0cfe07133fd00b27edee5e6385e333e9eeb010607e8a46e1cd673f05f8596595"}, - {file = "numpy-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6ed0d073a9c54ac40c41a9c2d53fcc3d4d4ed607670b9e7b0de1ba13b4cbfe6f"}, - {file = "numpy-1.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41388e32e40b41dd56eb37fcaa7488b2b47b0adf77c66154d6b89622c110dfe9"}, - {file = "numpy-1.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b55b953a1bdb465f4dc181758570d321db4ac23005f90ffd2b434cc6609a63dd"}, - {file = "numpy-1.22.0-cp39-cp39-win32.whl", hash = "sha256:5a311ee4d983c487a0ab546708edbdd759393a3dc9cd30305170149fedd23c88"}, - {file = "numpy-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:a97a954a8c2f046d3817c2bce16e3c7e9a9c2afffaf0400f5c16df5172a67c9c"}, - {file = "numpy-1.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb02929b0d6bfab4c48a79bd805bd7419114606947ec8284476167415171f55b"}, - {file = "numpy-1.22.0.zip", hash = "sha256:a955e4128ac36797aaffd49ab44ec74a71c11d6938df83b1285492d277db5397"}, + {file = "numpy-1.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d62d6b0870b53799204515145935608cdeb4cebb95a26800b6750e48884cc5b"}, + {file = "numpy-1.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:831f2df87bd3afdfc77829bc94bd997a7c212663889d56518359c827d7113b1f"}, + {file = "numpy-1.22.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8d1563060e77096367952fb44fca595f2b2f477156de389ce7c0ade3aef29e21"}, + {file = "numpy-1.22.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69958735d5e01f7b38226a6c6e7187d72b7e4d42b6b496aca5860b611ca0c193"}, + {file = "numpy-1.22.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45a7dfbf9ed8d68fd39763940591db7637cf8817c5bce1a44f7b56c97cbe211e"}, + {file = "numpy-1.22.1-cp310-cp310-win_amd64.whl", hash = "sha256:7e957ca8112c689b728037cea9c9567c27cf912741fabda9efc2c7d33d29dfa1"}, + {file = "numpy-1.22.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:800dfeaffb2219d49377da1371d710d7952c9533b57f3d51b15e61c4269a1b5b"}, + {file = "numpy-1.22.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:65f5e257987601fdfc63f1d02fca4d1c44a2b85b802f03bd6abc2b0b14648dd2"}, + {file = "numpy-1.22.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:632e062569b0fe05654b15ef0e91a53c0a95d08ffe698b66f6ba0f927ad267c2"}, + {file = "numpy-1.22.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d245a2bf79188d3f361137608c3cd12ed79076badd743dc660750a9f3074f7c"}, + {file = "numpy-1.22.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b4018a19d2ad9606ce9089f3d52206a41b23de5dfe8dc947d2ec49ce45d015"}, + {file = "numpy-1.22.1-cp38-cp38-win32.whl", hash = "sha256:f8ad59e6e341f38266f1549c7c2ec70ea0e3d1effb62a44e5c3dba41c55f0187"}, + {file = "numpy-1.22.1-cp38-cp38-win_amd64.whl", hash = "sha256:60f19c61b589d44fbbab8ff126640ae712e163299c2dd422bfe4edc7ec51aa9b"}, + {file = "numpy-1.22.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2db01d9838a497ba2aa9a87515aeaf458f42351d72d4e7f3b8ddbd1eba9479f2"}, + {file = "numpy-1.22.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bcd19dab43b852b03868796f533b5f5561e6c0e3048415e675bec8d2e9d286c1"}, + {file = "numpy-1.22.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78bfbdf809fc236490e7e65715bbd98377b122f329457fffde206299e163e7f3"}, + {file = "numpy-1.22.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c51124df17f012c3b757380782ae46eee85213a3215e51477e559739f57d9bf6"}, + {file = "numpy-1.22.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88d54b7b516f0ca38a69590557814de2dd638d7d4ed04864826acaac5ebb8f01"}, + {file = "numpy-1.22.1-cp39-cp39-win32.whl", hash = "sha256:b5ec9a5eaf391761c61fd873363ef3560a3614e9b4ead17347e4deda4358bca4"}, + {file = "numpy-1.22.1-cp39-cp39-win_amd64.whl", hash = "sha256:4ac4d7c9f8ea2a79d721ebfcce81705fc3cd61a10b731354f1049eb8c99521e8"}, + {file = "numpy-1.22.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e60ef82c358ded965fdd3132b5738eade055f48067ac8a5a8ac75acc00cad31f"}, + {file = "numpy-1.22.1.zip", hash = "sha256:e348ccf5bc5235fc405ab19d53bec215bb373300e5523c7b476cc0da8a5e9973"}, ] packageurl-python = [ {file = "packageurl-python-0.9.6.tar.gz", hash = "sha256:c01fbaf62ad2eb791e97158d1f30349e830bee2dd3e9503a87f6c3ffae8d1cf0"}, @@ -3041,33 +3019,6 @@ 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.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:62d5b5ce965bae78f12c1c0df0d387899dd4211ec0bdc52822373f13a3a022b9"}, - {file = "pandas-1.3.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:adfeb11be2d54f275142c8ba9bf67acee771b7186a5745249c7d5a06c670136b"}, - {file = "pandas-1.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:60a8c055d58873ad81cae290d974d13dd479b82cbb975c3e1fa2cf1920715296"}, - {file = "pandas-1.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd541ab09e1f80a2a1760032d665f6e032d8e44055d602d65eeea6e6e85498cb"}, - {file = "pandas-1.3.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2651d75b9a167cc8cc572cf787ab512d16e316ae00ba81874b560586fa1325e0"}, - {file = "pandas-1.3.5-cp310-cp310-win_amd64.whl", hash = "sha256:aaf183a615ad790801fa3cf2fa450e5b6d23a54684fe386f7e3208f8b9bfbef6"}, - {file = "pandas-1.3.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:344295811e67f8200de2390093aeb3c8309f5648951b684d8db7eee7d1c81fb7"}, - {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:552020bf83b7f9033b57cbae65589c01e7ef1544416122da0c79140c93288f56"}, - {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cce0c6bbeb266b0e39e35176ee615ce3585233092f685b6a82362523e59e5b4"}, - {file = "pandas-1.3.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d28a3c65463fd0d0ba8bbb7696b23073efee0510783340a44b08f5e96ffce0c"}, - {file = "pandas-1.3.5-cp37-cp37m-win32.whl", hash = "sha256:a62949c626dd0ef7de11de34b44c6475db76995c2064e2d99c6498c3dba7fe58"}, - {file = "pandas-1.3.5-cp37-cp37m-win_amd64.whl", hash = "sha256:8025750767e138320b15ca16d70d5cdc1886e8f9cc56652d89735c016cd8aea6"}, - {file = "pandas-1.3.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fe95bae4e2d579812865db2212bb733144e34d0c6785c0685329e5b60fcb85dd"}, - {file = "pandas-1.3.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f261553a1e9c65b7a310302b9dbac31cf0049a51695c14ebe04e4bfd4a96f02"}, - {file = "pandas-1.3.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b6dbec5f3e6d5dc80dcfee250e0a2a652b3f28663492f7dab9a24416a48ac39"}, - {file = "pandas-1.3.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3bc49af96cd6285030a64779de5b3688633a07eb75c124b0747134a63f4c05f"}, - {file = "pandas-1.3.5-cp38-cp38-win32.whl", hash = "sha256:b6b87b2fb39e6383ca28e2829cddef1d9fc9e27e55ad91ca9c435572cdba51bf"}, - {file = "pandas-1.3.5-cp38-cp38-win_amd64.whl", hash = "sha256:a395692046fd8ce1edb4c6295c35184ae0c2bbe787ecbe384251da609e27edcb"}, - {file = "pandas-1.3.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bd971a3f08b745a75a86c00b97f3007c2ea175951286cdda6abe543e687e5f2f"}, - {file = "pandas-1.3.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37f06b59e5bc05711a518aa10beaec10942188dccb48918bb5ae602ccbc9f1a0"}, - {file = "pandas-1.3.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c21778a688d3712d35710501f8001cdbf96eb70a7c587a3d5613573299fdca6"}, - {file = "pandas-1.3.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3345343206546545bc26a05b4602b6a24385b5ec7c75cb6059599e3d56831da2"}, - {file = "pandas-1.3.5-cp39-cp39-win32.whl", hash = "sha256:c69406a2808ba6cf580c2255bcf260b3f214d2664a3a4197d0e640f573b46fd3"}, - {file = "pandas-1.3.5-cp39-cp39-win_amd64.whl", hash = "sha256:32e1a26d5ade11b547721a72f9bfc4bd113396947606e00d5b4a5b79b3dcb006"}, - {file = "pandas-1.3.5.tar.gz", hash = "sha256:1e4285f5de1012de20ca46b188ccf33521bff61ba5c5ebd78b4fb28e5416a9f1"}, -] pandocfilters = [ {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"}, @@ -3127,8 +3078,8 @@ pip-api = [ {file = "pip_api-0.0.26-py3-none-any.whl", hash = "sha256:b24e94e5d5d3f161a2db49653798e6a4c1f0ed6b379e511b45a8fa57c185d711"}, ] pip-audit = [ - {file = "pip-audit-1.1.1.tar.gz", hash = "sha256:61d772968b6ef644f43ecceace89665d28d7ea521a9390e59188c4189d580856"}, - {file = "pip_audit-1.1.1-py3-none-any.whl", hash = "sha256:86aff3427a544757d1d30e8a0ee83eb040c85a94e7b8b6541ed4058493090b44"}, + {file = "pip-audit-1.1.2.tar.gz", hash = "sha256:374e8528a1376145cbe0f0ec4a7b6a5ebfd6152f665d274498ea49d8bffef24c"}, + {file = "pip_audit-1.1.2-py3-none-any.whl", hash = "sha256:48325027b803376bee22ca273f8a1b477324c10663c6218a5acebfdc4a107328"}, ] pip-licenses = [ {file = "pip-licenses-3.5.3.tar.gz", hash = "sha256:f44860e00957b791c6c6005a3328f2d5eaeee96ddb8e7d87d4b0aa25b02252e4"}, @@ -3198,6 +3149,10 @@ ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] +pure-eval = [ + {file = "pure_eval-0.2.1-py3-none-any.whl", hash = "sha256:94eeb505a88721bec7bb21a4ac49758b8b1a01530da1a70d4ffc1d9937689d71"}, + {file = "pure_eval-0.2.1.tar.gz", hash = "sha256:0f04483b16c9429532d2c0ddc96e2b3bb6b2dc37a2bfb0e986248dbfd0b78873"}, +] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -3206,8 +3161,8 @@ py-cpuinfo = [ {file = "py-cpuinfo-8.0.0.tar.gz", hash = "sha256:5f269be0e08e33fd959de96b34cd4aeeeacac014dd8305f70eb28d06de2345c5"}, ] py-progress-tracker = [ - {file = "py-progress-tracker-0.3.3.tar.gz", hash = "sha256:344a312bc183f4ab4fca5deb5d7d8b94195d3e4c81a2aa929cefee63952ac4d2"}, - {file = "py_progress_tracker-0.3.3-py3-none-any.whl", hash = "sha256:f298f203c86c32539ba50ee955e8f7121e1095e0704436057f405e2527c7695c"}, + {file = "py-progress-tracker-0.4.0.tar.gz", hash = "sha256:579344440781f5895b5630ab6be1a640320e22baab78af8d726b40cad619f162"}, + {file = "py_progress_tracker-0.4.0-py3-none-any.whl", hash = "sha256:5c39a94527e005a220b85ad02f6dc95691b5174458d97055f5ab8c28b827eec4"}, ] pycodestyle = [ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, @@ -3282,27 +3237,27 @@ pyparsing = [ {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, ] pyrsistent = [ - {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"}, - {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"}, - {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"}, - {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"}, - {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"}, - {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"}, - {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"}, - {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"}, - {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"}, - {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"}, - {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"}, - {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"}, - {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"}, - {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"}, + {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, + {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, + {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, + {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, + {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, + {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, + {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, + {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, + {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, + {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, + {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, + {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, + {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, + {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, + {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, + {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, ] pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, @@ -3486,71 +3441,6 @@ rfc3986 = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, ] -scikit-learn = [ - {file = "scikit-learn-1.0.2.tar.gz", hash = "sha256:b5870959a5484b614f26d31ca4c17524b1b0317522199dc985c3b4256e030767"}, - {file = "scikit_learn-1.0.2-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:da3c84694ff693b5b3194d8752ccf935a665b8b5edc33a283122f4273ca3e687"}, - {file = "scikit_learn-1.0.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:75307d9ea39236cad7eea87143155eea24d48f93f3a2f9389c817f7019f00705"}, - {file = "scikit_learn-1.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f14517e174bd7332f1cca2c959e704696a5e0ba246eb8763e6c24876d8710049"}, - {file = "scikit_learn-1.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9aac97e57c196206179f674f09bc6bffcd0284e2ba95b7fe0b402ac3f986023"}, - {file = "scikit_learn-1.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:d93d4c28370aea8a7cbf6015e8a669cd5d69f856cc2aa44e7a590fb805bb5583"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:85260fb430b795d806251dd3bb05e6f48cdc777ac31f2bcf2bc8bbed3270a8f5"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a053a6a527c87c5c4fa7bf1ab2556fa16d8345cf99b6c5a19030a4a7cd8fd2c0"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:245c9b5a67445f6f044411e16a93a554edc1efdcce94d3fc0bc6a4b9ac30b752"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:158faf30684c92a78e12da19c73feff9641a928a8024b4fa5ec11d583f3d8a87"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08ef968f6b72033c16c479c966bf37ccd49b06ea91b765e1cc27afefe723920b"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16455ace947d8d9e5391435c2977178d0ff03a261571e67f627c8fee0f9d431a"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-win32.whl", hash = "sha256:2f3b453e0b149898577e301d27e098dfe1a36943f7bb0ad704d1e548efc3b448"}, - {file = "scikit_learn-1.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:46f431ec59dead665e1370314dbebc99ead05e1c0a9df42f22d6a0e00044820f"}, - {file = "scikit_learn-1.0.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:ff3fa8ea0e09e38677762afc6e14cad77b5e125b0ea70c9bba1992f02c93b028"}, - {file = "scikit_learn-1.0.2-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:9369b030e155f8188743eb4893ac17a27f81d28a884af460870c7c072f114243"}, - {file = "scikit_learn-1.0.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7d6b2475f1c23a698b48515217eb26b45a6598c7b1840ba23b3c5acece658dbb"}, - {file = "scikit_learn-1.0.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:285db0352e635b9e3392b0b426bc48c3b485512d3b4ac3c7a44ec2a2ba061e66"}, - {file = "scikit_learn-1.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb33fe1dc6f73dc19e67b264dbb5dde2a0539b986435fdd78ed978c14654830"}, - {file = "scikit_learn-1.0.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1391d1a6e2268485a63c3073111fe3ba6ec5145fc957481cfd0652be571226d"}, - {file = "scikit_learn-1.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc3744dabc56b50bec73624aeca02e0def06b03cb287de26836e730659c5d29c"}, - {file = "scikit_learn-1.0.2-cp38-cp38-win32.whl", hash = "sha256:a999c9f02ff9570c783069f1074f06fe7386ec65b84c983db5aeb8144356a355"}, - {file = "scikit_learn-1.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:7626a34eabbf370a638f32d1a3ad50526844ba58d63e3ab81ba91e2a7c6d037e"}, - {file = "scikit_learn-1.0.2-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:a90b60048f9ffdd962d2ad2fb16367a87ac34d76e02550968719eb7b5716fd10"}, - {file = "scikit_learn-1.0.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:7a93c1292799620df90348800d5ac06f3794c1316ca247525fa31169f6d25855"}, - {file = "scikit_learn-1.0.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:eabceab574f471de0b0eb3f2ecf2eee9f10b3106570481d007ed1c84ebf6d6a1"}, - {file = "scikit_learn-1.0.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:55f2f3a8414e14fbee03782f9fe16cca0f141d639d2b1c1a36779fa069e1db57"}, - {file = "scikit_learn-1.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80095a1e4b93bd33261ef03b9bc86d6db649f988ea4dbcf7110d0cded8d7213d"}, - {file = "scikit_learn-1.0.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa38a1b9b38ae1fad2863eff5e0d69608567453fdfc850c992e6e47eb764e846"}, - {file = "scikit_learn-1.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff746a69ff2ef25f62b36338c615dd15954ddc3ab8e73530237dd73235e76d62"}, - {file = "scikit_learn-1.0.2-cp39-cp39-win32.whl", hash = "sha256:e174242caecb11e4abf169342641778f68e1bfaba80cd18acd6bc84286b9a534"}, - {file = "scikit_learn-1.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:b54a62c6e318ddbfa7d22c383466d38d2ee770ebdb5ddb668d56a099f6eaf75f"}, -] -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"}, @@ -3564,8 +3454,8 @@ send2trash = [ {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"}, ] setuptools-scm = [ - {file = "setuptools_scm-6.3.2-py3-none-any.whl", hash = "sha256:4c64444b1d49c4063ae60bfe1680f611c8b13833d556fd1d6050c0023162a119"}, - {file = "setuptools_scm-6.3.2.tar.gz", hash = "sha256:a49aa8081eeb3514eb9728fa5040f2eaa962d6c6f4ec9c32f6c1fba88f88a0f2"}, + {file = "setuptools_scm-6.4.1-py3-none-any.whl", hash = "sha256:93bbcc1d3e92f20eaef42df31e4b5e5a348f8cb0e48eaff3e060184a57e94b07"}, + {file = "setuptools_scm-6.4.1.tar.gz", hash = "sha256:9bd9ff7fd5fa1cf469fe28a632336b9cfd351476c6d09379ff676d3945f669b9"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -3618,6 +3508,10 @@ sphinxcontrib-serializinghtml = [ {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, ] +stack-data = [ + {file = "stack_data-0.1.4-py3-none-any.whl", hash = "sha256:02cc0683cbc445ae4ca8c4e3a0e58cb1df59f252efb0aa016b34804a707cf9bc"}, + {file = "stack_data-0.1.4.tar.gz", hash = "sha256:7769ed2482ce0030e00175dd1bf4ef1e873603b6ab61cd3da443b410e64e9477"}, +] tabulate = [ {file = "tabulate-0.8.9-py3-none-any.whl", hash = "sha256:d7c013fe7abbc5e491394e10fa845f8f32fe54f8dc60c6622c6cf482d25d47e4"}, {file = "tabulate-0.8.9.tar.gz", hash = "sha256:eb1d13f25760052e8931f2ef80aaf6045a6cceb47514db8beab24cded16f13a7"}, @@ -3633,10 +3527,6 @@ 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"}, @@ -3649,26 +3539,6 @@ tomlkit = [ {file = "tomlkit-0.7.0-py2.py3-none-any.whl", hash = "sha256:6babbd33b17d5c9691896b0e68159215a9387ebfa938aa3ac42f4a4beeb2b831"}, {file = "tomlkit-0.7.0.tar.gz", hash = "sha256:ac57f29693fab3e309ea789252fcce3061e19110085aa31af5446ca749325618"}, ] -torch = [ - {file = "torch-1.10.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:adbb5f292e260e39715d67478823e03e3001db1af5b02c18caa34549dccb421e"}, - {file = "torch-1.10.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:ac8cae04458cc47555fa07a760496c2fdf687223bcc13df5fed56ea3aead37f5"}, - {file = "torch-1.10.1-cp36-cp36m-win_amd64.whl", hash = "sha256:40508d67288c46ff1fad301fa6e996e0e936a733f2401475fc92c21dc3ef702d"}, - {file = "torch-1.10.1-cp36-none-macosx_10_9_x86_64.whl", hash = "sha256:8b47bd113c6cbd9a49669aaaa233ad5f25852d6ca3e640f9c71c808e65a1fdf4"}, - {file = "torch-1.10.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:50360868ad3f039cf99f0250300dbec51bf686a7b84dc6bbdb8dff4b1171c0f0"}, - {file = "torch-1.10.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e3d2154722189ed74747a494dce9588978dd55e43ca24c5bd307fb52620b232b"}, - {file = "torch-1.10.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d9c495bcd5f00becff5b051b5e4be86b7eaa0433cd0fe57f77c02bc1b93ab5b1"}, - {file = "torch-1.10.1-cp37-none-macosx_10_9_x86_64.whl", hash = "sha256:6b327d7b4eb2461b16d46763d46df71e597235ccc428650538a2735a0898270d"}, - {file = "torch-1.10.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:1c6c56178e5dacf7602ad00dc79c263d6c41c0f76261e9641e6bd2679678ceb3"}, - {file = "torch-1.10.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2ffa2db4ccb6466c59e3f95b7a582d47ae721e476468f4ffbcaa2832e0b92b9b"}, - {file = "torch-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:af577602e884c5e40fbd29ec978f052202355da93cd31e0a23251bd7aaff5a99"}, - {file = "torch-1.10.1-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:725d86e9809073eef868a3ddf4189878ee7af46fac71403834dd0925b3db9b82"}, - {file = "torch-1.10.1-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:fa197cfe047d0515bef238f42472721406609ebaceff2fd4e17f2ad4692ee51c"}, - {file = "torch-1.10.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cca660b27a90dbbc0af06c859260f6b875aef37c0897bd353e5deed085d2c877"}, - {file = "torch-1.10.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:01f4ffdafbfbd7d106fb4e487feee2cf29cced9903df8cb0444b0e308f9c5e92"}, - {file = "torch-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:607eccb7d539a11877cd02d95f4b164b7941fcf538ac7ff087bfed19e3644283"}, - {file = "torch-1.10.1-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:26b6dfbe21e247e67c615bfab0017ec391ed1517f88bbeea6228a49edd24cd88"}, - {file = "torch-1.10.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:5644280d88c5b6de27eacc0d911f968aad41a4bab297af4df5e571bc0927d3e4"}, -] tornado = [ {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, diff --git a/pylintrc b/pylintrc index d4eff5abc..a24e88ae5 100644 --- a/pylintrc +++ b/pylintrc @@ -438,7 +438,7 @@ contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. -generated-members=torch +generated-members= # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). diff --git a/pyproject.toml b/pyproject.toml index b1f27e5cb..d1a5314fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,6 @@ numpy = "^1.22.0" pygraphviz = { version = "^1.7", optional = true } Pillow = "^9.0.0" loguru = "^0.5.3" -torch = "^1.10.1" setuptools = "*" concrete-compiler = "^0.1.1" @@ -79,11 +78,9 @@ pygments-style-tomorrow = "^1.0.0" beautifulsoup4 = "^4.10.0" pip-licenses = "^3.5.3" sphinx-zama-theme = "2.0.8" -scikit-learn = "^1.0.2" -pandas = "^1.3.5" pip-audit = "^1.1.1" pytest-codeblocks = "^0.12.2" -py-progress-tracker = "^0.3.3" +py-progress-tracker = "^0.4.0" twine = "^3.7.1" [build-system] diff --git a/script/progress_tracker_utils/benchmark_and_publish_findings_in_docker.sh b/script/progress_tracker_utils/benchmark_and_publish_findings_in_docker.sh index a076d76a3..59d98dfb8 100755 --- a/script/progress_tracker_utils/benchmark_and_publish_findings_in_docker.sh +++ b/script/progress_tracker_utils/benchmark_and_publish_findings_in_docker.sh @@ -27,7 +27,7 @@ mkdir -p /tmp/keycache mkdir -p logs initial_concrete_log=logs/$(date -u --iso-8601=seconds).concrete.log -make -s concrete_benchmark 2>&1 | tee -a "$initial_concrete_log" +make -s benchmark 2>&1 | tee -a "$initial_concrete_log" final_concrete_log=logs/$(date -u --iso-8601=seconds).concrete.log cat -s "$initial_concrete_log" | sed '1d; $d' > "$final_concrete_log" @@ -44,22 +44,3 @@ curl \ -H 'Content-Type: application/json' \ -d @progress.json \ -X POST "$CONCRETE_PROGRESS_TRACKER_URL"/measurement - -initial_ml_log=logs/$(date -u --iso-8601=seconds).ml.log -make -s ml_benchmark 2>&1 | tee -a "$initial_ml_log" - -final_ml_log=logs/$(date -u --iso-8601=seconds).ml.log -cat -s "$initial_ml_log" | sed '1d; $d' > "$final_ml_log" - -# sed above removes the first and the last lines of the log -# which are empty to provide a nice console output -# but empty lines are useless for logs so we get rid of them - -rm "$initial_ml_log" -cp "$final_ml_log" logs/latest.ml.log - -curl \ - -H 'Authorization: Bearer '"$ML_PROGRESS_TRACKER_TOKEN"'' \ - -H 'Content-Type: application/json' \ - -d @progress.json \ - -X POST "$ML_PROGRESS_TRACKER_URL"/measurement diff --git a/tests/conftest.py b/tests/conftest.py index 8be9a9b9c..cd4a95c07 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,6 @@ """PyTest configuration file""" import json import operator -import random import re import shutil from pathlib import Path @@ -11,7 +10,6 @@ import networkx as nx import networkx.algorithms.isomorphism as iso import numpy import pytest -import torch from concrete.common.compilation import CompilationConfiguration from concrete.common.fhe_circuit import FHECircuit @@ -367,21 +365,6 @@ def remove_color_codes(): return lambda x: REMOVE_COLOR_CODES_RE.sub("", x) -def function_to_seed_torch(): - """Function to seed torch""" - - # Seed torch with something which is seed by pytest-randomly - torch.manual_seed(random.randint(0, 2 ** 64 - 1)) - torch.use_deterministic_algorithms(True) - - -@pytest.fixture -def seed_torch(): - """Fixture to seed torch""" - - return function_to_seed_torch - - def check_is_good_execution_impl( fhe_circuit: FHECircuit, function: Callable, diff --git a/tests/numpy/test_compile_user_friendly_api.py b/tests/numpy/test_compile_user_friendly_api.py index bdcc7d9ed..39dc92100 100644 --- a/tests/numpy/test_compile_user_friendly_api.py +++ b/tests/numpy/test_compile_user_friendly_api.py @@ -261,3 +261,20 @@ def test_np_fhe_compiler_full_compilation(default_compilation_configuration, che for i in range(64): assert fhe_circuit.run(i) == function_to_compile(i) + + +def test_np_fhe_compiler_compile_on_inputset(default_compilation_configuration): + """Test the case where we generate an FHE circuit with a single call.""" + + def function_to_compile(x): + return x + 42 + + compiler = NPFHECompiler( + function_to_compile, + {"x": "encrypted"}, + default_compilation_configuration, + ) + circuit = compiler.compile_on_inputset(numpy.arange(64)) + + for i in range(64): + assert circuit.run(i) == function_to_compile(i) diff --git a/tests/quantization/test_compilation.py b/tests/quantization/test_compilation.py deleted file mode 100644 index bb43f993a..000000000 --- a/tests/quantization/test_compilation.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Test Neural Networks compilations""" -import numpy -import pytest -from torch import nn - -from concrete.quantization import PostTrainingAffineQuantization, QuantizedArray -from concrete.torch import NumpyModule - -# INPUT_OUTPUT_FEATURE is the number of input and output of each of the network layers. -# (as well as the input of the network itself) -# Currently, with 7 bits maximum, we can use 15 weights max in the theoretical case. -INPUT_OUTPUT_FEATURE = [1, 2, 3] - - -class FC(nn.Module): - """Torch model for the tests""" - - def __init__(self, input_output): - super().__init__() - self.fc1 = nn.Linear(in_features=input_output, out_features=input_output) - self.sigmoid1 = nn.Sigmoid() - self.fc2 = nn.Linear(in_features=input_output, out_features=input_output) - - def forward(self, x): - """Forward pass.""" - out = self.fc1(x) - out = self.sigmoid1(out) - out = self.fc2(out) - - return out - - -@pytest.mark.parametrize( - "model", - [pytest.param(FC)], -) -@pytest.mark.parametrize( - "input_output_feature", - [pytest.param(input_output_feature) for input_output_feature in INPUT_OUTPUT_FEATURE], -) -def test_quantized_module_compilation( - input_output_feature, - model, - seed_torch, - default_compilation_configuration, - check_is_good_execution, -): - """Test a neural network compilation for FHE inference.""" - # Seed torch - seed_torch() - - n_bits = 2 - - # Define an input shape (n_examples, n_features) - input_shape = (50, input_output_feature) - - # Build a random Quantized Fully Connected Neural Network - - # Define the torch model - torch_fc_model = model(input_output_feature) - # Create random input - numpy_input = numpy.random.uniform(-100, 100, size=input_shape) - - # Create corresponding numpy model - numpy_fc_model = NumpyModule(torch_fc_model) - # Quantize with post-training static method - post_training_quant = PostTrainingAffineQuantization(n_bits, numpy_fc_model) - quantized_model = post_training_quant.quantize_module(numpy_input) - # Quantize input - q_input = QuantizedArray(n_bits, numpy_input) - quantized_model(q_input) - - # Compile - quantized_model.compile(q_input, default_compilation_configuration) - - for x_q in q_input.qvalues: - x_q = numpy.expand_dims(x_q, 0) - check_is_good_execution( - fhe_circuit=quantized_model.forward_fhe, - function=quantized_model.forward, - args=[x_q.astype(numpy.uint8)], - postprocess_output_func=lambda x: quantized_model.dequantize_output( - x.astype(numpy.float32) - ), - check_function=numpy.isclose, - verbose=False, - ) diff --git a/tests/quantization/test_quantized_activations.py b/tests/quantization/test_quantized_activations.py deleted file mode 100644 index 585b031e9..000000000 --- a/tests/quantization/test_quantized_activations.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Tests for the quantized activation functions.""" -import numpy -import pytest - -from concrete.quantization import QuantizedArray, QuantizedReLU6, QuantizedSigmoid - -N_BITS_ATOL_TUPLE_LIST = [ - (32, 10 ** -2), - (28, 10 ** -2), - (20, 10 ** -2), - (16, 10 ** -1), - (8, 10 ** -0), - (5, 10 ** -0), -] - - -@pytest.mark.parametrize( - "n_bits, atol", - [pytest.param(n_bits, atol) for n_bits, atol in N_BITS_ATOL_TUPLE_LIST], -) -@pytest.mark.parametrize( - "input_range", - [pytest.param((-1, 1)), pytest.param((-2, 2)), pytest.param((-10, 10)), pytest.param((0, 20))], -) -@pytest.mark.parametrize( - "input_shape", - [pytest.param((10, 40, 20)), pytest.param((100, 400))], -) -@pytest.mark.parametrize( - "quant_activation", - [ - pytest.param(QuantizedSigmoid), - pytest.param(QuantizedReLU6), - ], -) -@pytest.mark.parametrize("is_signed", [pytest.param(True), pytest.param(False)]) -def test_activations(quant_activation, input_shape, input_range, n_bits, atol, is_signed): - """Test activation functions.""" - values = numpy.random.uniform(input_range[0], input_range[1], size=input_shape) - q_inputs = QuantizedArray(n_bits, values, is_signed) - quant_sigmoid = quant_activation(n_bits) - quant_sigmoid.calibrate(values) - expected_output = quant_sigmoid.q_out.values - q_output = quant_sigmoid(q_inputs) - qvalues = q_output.qvalues - - # Quantized values must be contained between 0 and 2**n_bits - 1. - assert numpy.max(qvalues) <= 2 ** n_bits - 1 - assert numpy.min(qvalues) >= 0 - - # Dequantized values must be close to original values - dequant_values = q_output.dequant() - - # Check that all values are close - assert numpy.isclose(dequant_values.ravel(), expected_output.ravel(), atol=atol).all() diff --git a/tests/quantization/test_quantized_array.py b/tests/quantization/test_quantized_array.py deleted file mode 100644 index df1be2741..000000000 --- a/tests/quantization/test_quantized_array.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Tests for the quantized array/tensors.""" -import numpy -import pytest - -from concrete.quantization import QuantizedArray - - -@pytest.mark.parametrize( - "n_bits", - [32, 28, 20, 16, 8, 4], -) -@pytest.mark.parametrize("is_signed", [pytest.param(True), pytest.param(False)]) -@pytest.mark.parametrize("values", [pytest.param(numpy.random.randn(2000))]) -def test_quant_dequant_update(values, n_bits, is_signed, check_array_equality): - """Test the quant and dequant function.""" - - quant_array = QuantizedArray(n_bits, values, is_signed) - qvalues = quant_array.quant() - - # Quantized values must be contained between 0 and 2**n_bits - assert numpy.max(qvalues) <= 2 ** (n_bits) - 1 - quant_array.offset - assert numpy.min(qvalues) >= -quant_array.offset - - # Dequantized values must be close to original values - dequant_values = quant_array.dequant() - - # Check that all values are close - tolerance = quant_array.scale / 2 - assert numpy.isclose(dequant_values, values, atol=tolerance).all() - - # Explain the choice of tolerance - # This test checks the values are quantized and dequantized correctly - # Each quantization have a maximum error per quantized value an it's `scale / 2` - - # To give an intuition, let's say you have the scale of 0.5 - # the range `[a + 0.00, a + 0.25]` will be quantized into 0, dequantized into `a + 0.00` - # the range `[a + 0.25, a + 0.75]` will be quantized into 1, dequantized into `a + 0.50` - # the range `[a + 0.75, a + 1.25]` will be quantized into 2, dequantized into `a + 1.00` - # ... - - # So for each quantization-then-dequantization operation, - # the maximum error is `0.25`, which is `scale / 2` - - # Test update functions - new_values = numpy.array([0.3, 0.5, -1.2, -3.4]) - new_qvalues_ = quant_array.update_values(new_values) - - # Make sure the shape changed for the qvalues - assert new_qvalues_.shape != qvalues.shape - - new_qvalues = numpy.array([1, 4, 7, 29]) - new_values_updated = quant_array.update_qvalues(new_qvalues) - - # Make sure that we can see at least one change. - assert not numpy.array_equal(new_qvalues, new_qvalues_) - assert not numpy.array_equal(new_values, new_values_updated) - - # Check that the __call__ returns also the qvalues. - check_array_equality(quant_array(), new_qvalues) diff --git a/tests/quantization/test_quantized_layers.py b/tests/quantization/test_quantized_layers.py deleted file mode 100644 index 5f78d2072..000000000 --- a/tests/quantization/test_quantized_layers.py +++ /dev/null @@ -1,62 +0,0 @@ -"""Tests for the quantized layers.""" -import numpy -import pytest - -from concrete.quantization import QuantizedArray, QuantizedLinear - -# QuantizedLinear unstable with n_bits>23 -# and hard to test with numpy.isclose with n_bits < 8 -N_BITS_LIST = [20, 16, 8] - - -@pytest.mark.parametrize( - "n_bits", - [pytest.param(n_bits) for n_bits in N_BITS_LIST], -) -@pytest.mark.parametrize( - "n_examples, n_features, n_neurons", - [ - pytest.param(50, 3, 4), - pytest.param(20, 500, 30), - pytest.param(200, 300, 50), - pytest.param(10000, 100, 1), - pytest.param(10, 20, 1), - ], -) -@pytest.mark.parametrize("is_signed", [pytest.param(True), pytest.param(False)]) -def test_quantized_linear(n_examples, n_features, n_neurons, n_bits, is_signed): - """Test the quantization linear layer of numpy.array. - - With n_bits>>0 we expect the results of the quantized linear - to be the same as the standard linear layer. - """ - inputs = numpy.random.uniform(size=(n_examples, n_features)) - q_inputs = QuantizedArray(n_bits, inputs) - - # shape of weights: (n_features, n_neurons) - weights = numpy.random.uniform(size=(n_features, n_neurons)) - q_weights = QuantizedArray(n_bits, weights, is_signed) - - bias = numpy.random.uniform(size=(1, n_neurons)) - q_bias = QuantizedArray(n_bits, bias, is_signed) - - # Define our QuantizedLinear layer - q_linear = QuantizedLinear(n_bits, q_weights, q_bias) - - # Calibrate the Quantized layer - q_linear.calibrate(inputs) - - expected_outputs = q_linear.q_out.values - actual_output = q_linear(q_inputs).dequant() - - assert numpy.isclose(expected_outputs, actual_output, atol=10 ** -0).all() - - # Same test without bias - q_linear = QuantizedLinear(n_bits, q_weights) - - # Calibrate the Quantized layer - q_linear.calibrate(inputs) - expected_outputs = q_linear.q_out.values - actual_output = q_linear(q_inputs).dequant() - - assert numpy.isclose(expected_outputs, actual_output, atol=10 ** -0).all() diff --git a/tests/quantization/test_quantized_module.py b/tests/quantization/test_quantized_module.py deleted file mode 100644 index 0725f156b..000000000 --- a/tests/quantization/test_quantized_module.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Tests for the quantized module.""" -import numpy -import pytest -import torch -from torch import nn - -from concrete.quantization import PostTrainingAffineQuantization, QuantizedArray -from concrete.torch import NumpyModule - - -class CNN(nn.Module): - """Torch CNN model for the tests.""" - - def __init__(self): - super().__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.AvgPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - """Forward pass.""" - x = self.pool(torch.relu(self.conv1(x))) - x = self.pool(torch.relu(self.conv2(x))) - x = torch.flatten(x, 1) - x = torch.relu(self.fc1(x)) - x = torch.relu(self.fc2(x)) - x = self.fc3(x) - return x - - -class FC(nn.Module): - """Torch model for the tests""" - - def __init__(self): - super().__init__() - self.fc1 = nn.Linear(in_features=32 * 32 * 3, out_features=128) - self.sigmoid1 = nn.Sigmoid() - self.fc2 = nn.Linear(in_features=128, out_features=64) - self.sigmoid2 = nn.Sigmoid() - self.fc3 = nn.Linear(in_features=64, out_features=64) - self.sigmoid3 = nn.Sigmoid() - self.fc4 = nn.Linear(in_features=64, out_features=64) - self.sigmoid4 = nn.Sigmoid() - self.fc5 = nn.Linear(in_features=64, out_features=10) - - def forward(self, x): - """Forward pass.""" - out = self.fc1(x) - out = self.sigmoid1(out) - out = self.fc2(out) - out = self.sigmoid2(out) - out = self.fc3(out) - out = self.sigmoid3(out) - out = self.fc4(out) - out = self.sigmoid4(out) - out = self.fc5(out) - - return out - - -N_BITS_ATOL_TUPLE_LIST = [ - (28, 10 ** -2), - (20, 10 ** -2), - (16, 10 ** -1), - (8, 10 ** -0), - (4, 10 ** -0), -] - - -@pytest.mark.parametrize( - "n_bits, atol", - [pytest.param(n_bits, atol) for n_bits, atol in N_BITS_ATOL_TUPLE_LIST], -) -@pytest.mark.parametrize( - "model, input_shape", - [ - pytest.param(FC, (100, 32 * 32 * 3)), - ], -) -@pytest.mark.parametrize( - "is_signed", - [pytest.param([False, True])], -) -def test_quantized_linear(model, input_shape, n_bits, atol, is_signed, seed_torch): - """Test the quantized module with a post-training static quantization. - - With n_bits>>0 we expect the results of the quantized module - to be the same as the standard module. - """ - # Seed torch - seed_torch() - # Define the torch model - torch_fc_model = model() - # Create random input - numpy_input = numpy.random.uniform(size=input_shape) - # Create corresponding numpy model - numpy_fc_model = NumpyModule(torch_fc_model) - # Predict with real model - numpy_prediction = numpy_fc_model(numpy_input) - # Quantize with post-training static method - post_training_quant = PostTrainingAffineQuantization( - n_bits, numpy_fc_model, is_signed=is_signed - ) - quantized_model = post_training_quant.quantize_module(numpy_input) - # Quantize input - q_input = QuantizedArray(n_bits, numpy_input) - # Forward and Dequantize to get back to real values - dequant_prediction = quantized_model.forward_and_dequant(q_input) - - assert numpy.isclose(numpy_prediction, dequant_prediction, atol=atol).all() diff --git a/tests/torch/test_compile_torch.py b/tests/torch/test_compile_torch.py deleted file mode 100644 index b3090cc9b..000000000 --- a/tests/torch/test_compile_torch.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Tests for the torch to numpy module.""" -import numpy -import pytest -from torch import nn - -from concrete.quantization import QuantizedArray -from concrete.torch.compile import compile_torch_model - -# INPUT_OUTPUT_FEATURE is the number of input and output of each of the network layers. -# (as well as the input of the network itself) -INPUT_OUTPUT_FEATURE = [1, 2] - - -class FC(nn.Module): - """Torch model for the tests""" - - def __init__(self, input_output, activation_function): - super().__init__() - self.fc1 = nn.Linear(in_features=input_output, out_features=input_output) - self.act_f = activation_function() - self.fc2 = nn.Linear(in_features=input_output, out_features=input_output) - - def forward(self, x): - """Forward pass.""" - out = self.fc1(x) - out = self.act_f(out) - out = self.fc2(out) - - return out - - -@pytest.mark.parametrize( - "activation_function", - [ - pytest.param(nn.Sigmoid, id="sigmoid"), - pytest.param(nn.ReLU6, id="relu"), - ], -) -@pytest.mark.parametrize( - "model", - [pytest.param(FC)], -) -@pytest.mark.parametrize( - "input_output_feature", - [pytest.param(input_output_feature) for input_output_feature in INPUT_OUTPUT_FEATURE], -) -def test_compile_torch( - input_output_feature, - model, - activation_function, - seed_torch, - default_compilation_configuration, - check_is_good_execution, -): - """Test the different model architecture from torch numpy.""" - - # Seed torch - seed_torch() - - n_bits = 2 - - # Define an input shape (n_examples, n_features) - n_examples = 50 - - # Define the torch model - torch_fc_model = model(input_output_feature, activation_function) - # Create random input - inputset = [ - numpy.random.uniform(-100, 100, size=input_output_feature) for _ in range(n_examples) - ] - - # Compile - quantized_numpy_module = compile_torch_model( - torch_fc_model, - inputset, - default_compilation_configuration, - n_bits=n_bits, - ) - - # Quantize inputs all at once to have meaningful scale and zero point - q_input = QuantizedArray(n_bits, numpy.array(inputset)) - - # Compare predictions between FHE and QuantizedModule - for x_q in q_input.qvalues: - x_q = numpy.expand_dims(x_q, 0) - check_is_good_execution( - fhe_circuit=quantized_numpy_module.forward_fhe, - function=quantized_numpy_module.forward, - args=[x_q.astype(numpy.uint8)], - postprocess_output_func=lambda x: quantized_numpy_module.dequantize_output( - x.astype(numpy.float32) - ), - check_function=numpy.isclose, - verbose=False, - ) diff --git a/tests/torch/test_torch_to_numpy.py b/tests/torch/test_torch_to_numpy.py deleted file mode 100644 index 3dbbb430b..000000000 --- a/tests/torch/test_torch_to_numpy.py +++ /dev/null @@ -1,127 +0,0 @@ -"""Tests for the torch to numpy module.""" -import numpy -import pytest -import torch -from torch import nn - -from concrete.torch import NumpyModule - - -class CNN(nn.Module): - """Torch CNN model for the tests.""" - - def __init__(self): - super().__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.AvgPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - """Forward pass.""" - x = self.pool(torch.relu(self.conv1(x))) - x = self.pool(torch.relu(self.conv2(x))) - x = torch.flatten(x, 1) - x = torch.relu(self.fc1(x)) - x = torch.relu(self.fc2(x)) - x = self.fc3(x) - return x - - -class FC(nn.Module): - """Torch model for the tests""" - - def __init__(self, activation_function): - super().__init__() - self.fc1 = nn.Linear(in_features=32 * 32 * 3, out_features=128) - self.act_1 = activation_function() - self.fc2 = nn.Linear(in_features=128, out_features=64) - self.act_2 = activation_function() - self.fc3 = nn.Linear(in_features=64, out_features=64) - self.act_3 = activation_function() - self.fc4 = nn.Linear(in_features=64, out_features=64) - self.act_4 = activation_function() - self.fc5 = nn.Linear(in_features=64, out_features=10) - - def forward(self, x): - """Forward pass.""" - out = self.fc1(x) - out = self.act_1(out) - out = self.fc2(out) - out = self.act_2(out) - out = self.fc3(out) - out = self.act_3(out) - out = self.fc4(out) - out = self.act_4(out) - out = self.fc5(out) - - return out - - -@pytest.mark.parametrize( - "model, input_shape", - [ - pytest.param(FC, (100, 32 * 32 * 3)), - ], -) -@pytest.mark.parametrize( - "activation_function", - [ - pytest.param(nn.Sigmoid, id="sigmoid"), - pytest.param(nn.ReLU6, id="relu"), - ], -) -def test_torch_to_numpy(model, input_shape, activation_function, seed_torch): - """Test the different model architecture from torch numpy.""" - - # Seed torch - seed_torch() - # Define the torch model - torch_fc_model = model(activation_function) - # Create random input - torch_input_1 = torch.randn(input_shape) - # Predict with torch model - torch_predictions = torch_fc_model(torch_input_1).detach().numpy() - # Create corresponding numpy model - numpy_fc_model = NumpyModule(torch_fc_model) - # Torch input to numpy - numpy_input_1 = torch_input_1.detach().numpy() - # Predict with numpy model - numpy_predictions = numpy_fc_model(numpy_input_1) - - # Test: the output of the numpy model is the same as the torch model. - assert numpy_predictions.shape == torch_predictions.shape - # Test: prediction from the numpy model are the same as the torh model. - assert numpy.isclose(torch_predictions, numpy_predictions, rtol=10 - 3).all() - - # Test: dynamics between layers is working (quantized input and activations) - torch_input_2 = torch.randn(input_shape) - # Make sure both inputs are different - assert (torch_input_1 != torch_input_2).any() - # Predict with torch - torch_predictions = torch_fc_model(torch_input_2).detach().numpy() - # Torch input to numpy - numpy_input_2 = torch_input_2.detach().numpy() - # Numpy predictions using the previous model - numpy_predictions = numpy_fc_model(numpy_input_2) - assert numpy.isclose(torch_predictions, numpy_predictions, rtol=10 - 3).all() - - -@pytest.mark.parametrize( - "model, incompatible_layer", - [pytest.param(CNN, "Conv2d")], -) -def test_raises(model, incompatible_layer, seed_torch): - """Function to test incompatible layers.""" - - seed_torch() - torch_incompatible_model = model() - expected_errmsg = ( - f"The following module is currently not implemented: {incompatible_layer}. " - f"Please stick to the available torch modules: " - f"{', '.join(sorted(module.__name__ for module in NumpyModule.IMPLEMENTED_MODULES))}." - ) - with pytest.raises(ValueError, match=expected_errmsg): - NumpyModule(torch_incompatible_model)