diff --git a/pyproject.toml b/pyproject.toml index d02ad5745e..d6d8e6c123 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,6 +128,8 @@ filterwarnings = [ "ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning", "ignore:Converting a tensor to a NumPy array might cause the trace to be incorrect.", "ignore:torch.from_numpy results are registered as constants in the trace.", + "ignore:ONNX Preprocess - Removing mutation from node aten*:UserWarning", + "ignore:Liblinear failed to converge, increase the number of iterations.*:sklearn.exceptions.ConvergenceWarning", ] [tool.semantic_release] diff --git a/src/concrete/ml/sklearn/linear_model.py b/src/concrete/ml/sklearn/linear_model.py index f75d3f5cd0..813f83ff33 100644 --- a/src/concrete/ml/sklearn/linear_model.py +++ b/src/concrete/ml/sklearn/linear_model.py @@ -237,12 +237,6 @@ def __init__( if self.fit_encrypted: self.classes_: Optional[numpy.ndarray] = None - warnings.warn( - "FHE training is an experimental feature. Please be aware that the API might " - "change in future versions.", - stacklevel=2, - ) - # Check the presence of mandatory attributes if self.loss != "log_loss": raise ValueError( @@ -687,13 +681,7 @@ def fit( # type: ignore[override] # If the model should be trained using FHE training if self.fit_encrypted: - if fhe is None: - fhe = "disable" - warnings.warn( - "Parameter 'fhe' isn't set while FHE training is enabled.\n" - f"Defaulting to '{fhe=}'", - stacklevel=2, - ) + fhe = "disable" if fhe is None else fhe # Make sure the `fhe` parameter is correct assert FheMode.is_valid(fhe), ( diff --git a/tests/common/test_pbs_error_probability_settings.py b/tests/common/test_pbs_error_probability_settings.py index 85e4128bd1..c511f64da0 100644 --- a/tests/common/test_pbs_error_probability_settings.py +++ b/tests/common/test_pbs_error_probability_settings.py @@ -1,11 +1,9 @@ """Tests for the sklearn linear models.""" -import warnings from inspect import signature import numpy import pytest -from sklearn.exceptions import ConvergenceWarning from torch import nn from concrete.ml.pytest.torch_models import FCSmall @@ -34,10 +32,8 @@ def test_config_sklearn(model_class, parameters, kwargs, load_data): model = model_class() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - # Fit the model - model.fit(x, y) + # Fit the model + model.fit(x, y) if kwargs.get("p_error", None) is not None and kwargs.get("global_p_error", None) is not None: with pytest.raises(ValueError) as excinfo: diff --git a/tests/common/test_serialization.py b/tests/common/test_serialization.py index a39adfc038..ba8a7f7c92 100644 --- a/tests/common/test_serialization.py +++ b/tests/common/test_serialization.py @@ -6,7 +6,6 @@ import inspect import io -import warnings from functools import partial import numpy @@ -18,7 +17,6 @@ from concrete.fhe.compilation import Circuit from numpy.random import RandomState from sklearn.datasets import make_regression -from sklearn.exceptions import ConvergenceWarning from skops.io.exceptions import UntrustedTypesFoundException from skorch.dataset import ValidSplit from torch import nn @@ -123,9 +121,7 @@ def test_serialize_sklearn_model(concrete_model_class, load_data): # Instantiate and fit a Concrete model to recover its underlying Scikit Learn model concrete_model = concrete_model_class() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - _, sklearn_model = concrete_model.fit_benchmark(x, y) + _, sklearn_model = concrete_model.fit_benchmark(x, y) # Both JSON string are not compared as scikit-learn models are serialized using Skops or pickle, # which does not make string comparison possible diff --git a/tests/deployment/test_client_server.py b/tests/deployment/test_client_server.py index dcf6e3e5bd..25f9cf478c 100644 --- a/tests/deployment/test_client_server.py +++ b/tests/deployment/test_client_server.py @@ -3,7 +3,6 @@ import json import os import tempfile -import warnings import zipfile from functools import partial from pathlib import Path @@ -11,7 +10,6 @@ import numpy import pytest -from sklearn.exceptions import ConvergenceWarning from torch import nn from concrete.ml.deployment.fhe_client_server import ( @@ -115,15 +113,10 @@ def test_client_server_sklearn( x_test = x[-1:] # Instantiate the model - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - model = instantiate_model_generic(model_class, n_bits=n_bits) + model = instantiate_model_generic(model_class, n_bits=n_bits) # Fit the model - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - warnings.simplefilter("ignore", category=UserWarning) - model.fit(x_train, y_train) + model.fit(x_train, y_train) key_dir = default_configuration.insecure_key_cache_location @@ -393,19 +386,14 @@ def test_save_mode_handling(n_bits, fit_encrypted, mode, error_message): y_train = y[:-1] # Instantiate the model - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - parameters_range = [-1, 1] if fit_encrypted else None - model = instantiate_model_generic( - partial(SGDClassifier, fit_encrypted=fit_encrypted, parameters_range=parameters_range), - n_bits=n_bits, - ) + parameters_range = [-1, 1] if fit_encrypted else None + model = instantiate_model_generic( + partial(SGDClassifier, fit_encrypted=fit_encrypted, parameters_range=parameters_range), + n_bits=n_bits, + ) # Fit the model - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - warnings.simplefilter("ignore", category=UserWarning) - model.fit(x_train, y_train) + model.fit(x_train, y_train) # Compile model.compile(X=x_train) diff --git a/tests/parameter_search/test_p_error_binary_search.py b/tests/parameter_search/test_p_error_binary_search.py index 57686f29e7..350b72a9cc 100644 --- a/tests/parameter_search/test_p_error_binary_search.py +++ b/tests/parameter_search/test_p_error_binary_search.py @@ -1,14 +1,12 @@ """Test binary search class.""" import os -import warnings from pathlib import Path import numpy import pytest import torch from sklearn.datasets import make_classification -from sklearn.exceptions import ConvergenceWarning from sklearn.metrics import r2_score, top_k_accuracy_score from tensorflow import keras @@ -126,9 +124,8 @@ def test_update_valid_attr_method(attr, value, model_name, quant_type, metric, l predict="predict", n_simulation=1, ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - search.run(x=x_calib, ground_truth=y, strategy=all, **{attr: value}) + + search.run(x=x_calib, ground_truth=y, strategy=all, **{attr: value}) assert getattr(search, attr) == value @@ -167,10 +164,7 @@ def test_non_convergence_for_built_in_models(model_class, parameters, load_data, max_metric_loss=-10, is_qat=False, ) - - warnings.simplefilter("always") - with pytest.warns(UserWarning, match="ConvergenceWarning: .*"): - search.run(x=x_calib, ground_truth=y, strategy=all) + search.run(x=x_calib, ground_truth=y, strategy=all) @pytest.mark.parametrize("model_name, quant_type", [("CustomModel", "qat")]) @@ -205,9 +199,7 @@ def test_non_convergence_for_custom_models(model_name, quant_type): labels=numpy.arange(MODELS_ARGS[model_name]["dataset"]["n_classes"]), ) - warnings.simplefilter("always") - with pytest.warns(UserWarning, match="ConvergenceWarning: .*"): - search.run(x=x_calib, ground_truth=y, strategy=all) + search.run(x=x_calib, ground_truth=y, strategy=all) @pytest.mark.parametrize( @@ -279,9 +271,8 @@ def test_binary_search_for_custom_models(model_name, quant_type, threshold): k=1, labels=numpy.arange(MODELS_ARGS[model_name]["dataset"]["n_classes"]), ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - largest_perror = search.run(x=x_calib, ground_truth=y, strategy=all) + + largest_perror = search.run(x=x_calib, ground_truth=y, strategy=all) assert 1.0 > largest_perror > 0.0 assert ( @@ -337,9 +328,7 @@ def test_binary_search_for_built_in_models(model_class, parameters, threshold, p # The model does not have `predict` return - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - largest_perror = search.run(x=x_calib, ground_truth=y, strategy=all) + largest_perror = search.run(x=x_calib, ground_truth=y, strategy=all) assert 1.0 > largest_perror > 0.0 assert ( @@ -475,9 +464,7 @@ def test_success_save_option(model_name, quant_type, metric, directory, log_file # When instantiating the class, if the file exists, it is deleted, to avoid overwriting it assert not path.exists() - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - search.run(x=x_calib, ground_truth=y) + search.run(x=x_calib, ground_truth=y) # Check that the file has been properly created assert path.exists() diff --git a/tests/seeding/test_seeding.py b/tests/seeding/test_seeding.py index 703000c329..be44ba5be1 100644 --- a/tests/seeding/test_seeding.py +++ b/tests/seeding/test_seeding.py @@ -2,14 +2,11 @@ import inspect import random -import warnings import numpy import pytest -from sklearn.exceptions import ConvergenceWarning from sklearn.tree import plot_tree -from concrete.ml.common.utils import get_model_name from concrete.ml.pytest.utils import MODELS_AND_DATASETS @@ -93,16 +90,10 @@ def test_seed_sklearn(model_class, parameters, load_data, default_configuration) model_params["random_state"] = numpy.random.randint(0, 2**15) # First case: user gives his own random_state - # Warning skip due to SGDClassifier with fit_encrypted = True - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - model = model_class(**model_params) - - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - warnings.simplefilter("ignore", category=UserWarning) - # Fit the model - model, sklearn_model = model.fit_benchmark(x, y) + model = model_class(**model_params) + + # Fit the model + model, sklearn_model = model.fit_benchmark(x, y) lpvoid_ptr_plot_tree = getattr(model, "plot_tree", None) if callable(lpvoid_ptr_plot_tree): diff --git a/tests/sklearn/test_common.py b/tests/sklearn/test_common.py index 4f7889878c..78409db6a3 100644 --- a/tests/sklearn/test_common.py +++ b/tests/sklearn/test_common.py @@ -1,11 +1,9 @@ """Tests common to all sklearn models.""" import inspect -import warnings import numpy import pytest -from sklearn.exceptions import ConvergenceWarning from concrete.ml.common.utils import get_model_class from concrete.ml.pytest.utils import MODELS_AND_DATASETS @@ -44,10 +42,8 @@ def test_seed_sklearn(model_class, parameters, load_data): # First case: user gives his own random_state model = model_class(random_state=random_state_constructor) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - # Fit the model - model, sklearn_model = model.fit_benchmark(x, y, random_state=random_state_user) + # Fit the model + model, sklearn_model = model.fit_benchmark(x, y, random_state=random_state_user) assert ( model.random_state == random_state_user and sklearn_model.random_state == random_state_user @@ -56,10 +52,8 @@ def test_seed_sklearn(model_class, parameters, load_data): # Second case: user does not give random_state but seeds the constructor model = model_class(random_state=random_state_constructor) - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - # Fit the model - model, sklearn_model = model.fit_benchmark(x, y) + # Fit the model + model, sklearn_model = model.fit_benchmark(x, y) assert (model.random_state == random_state_constructor) and ( sklearn_model.random_state == random_state_constructor @@ -69,10 +63,8 @@ def test_seed_sklearn(model_class, parameters, load_data): model = model_class(random_state=None) assert model.random_state is None - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - # Fit the model - model, sklearn_model = model.fit_benchmark(x, y) + # Fit the model + model, sklearn_model = model.fit_benchmark(x, y) # model.random_state and sklearn_model.random_state should now be seeded with the same value assert model.random_state is not None and sklearn_model.random_state is not None diff --git a/tests/sklearn/test_dump_onnx.py b/tests/sklearn/test_dump_onnx.py index 34e14d242e..53d233390b 100644 --- a/tests/sklearn/test_dump_onnx.py +++ b/tests/sklearn/test_dump_onnx.py @@ -6,7 +6,6 @@ import numpy import onnx import pytest -from sklearn.exceptions import ConvergenceWarning from concrete.ml.common.utils import is_model_class_in_a_list from concrete.ml.pytest.utils import UNIQUE_MODELS_AND_DATASETS, get_model_name @@ -419,11 +418,6 @@ def check_onnx_file_dump( # KNN can only be compiled with small quantization bit numbers for now # FIXME: https://github.com/zama-ai/concrete-ml-internal/issues/3979 model.n_bits = 2 - - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) with warnings.catch_warnings(): diff --git a/tests/sklearn/test_fhe_training.py b/tests/sklearn/test_fhe_training.py index 55b6546171..194e73f3ee 100644 --- a/tests/sklearn/test_fhe_training.py +++ b/tests/sklearn/test_fhe_training.py @@ -1,7 +1,6 @@ """Tests training in FHE.""" import re -import warnings import numpy import pytest @@ -56,26 +55,20 @@ def test_init_error_raises(n_bits, parameter_min_max): parameters_range=parameters_range, ) - with warnings.catch_warnings(): - - # FHE training is an experimental feature and a warning is raised each time `fit_encrypted` - # is set to True - warnings.filterwarnings("ignore", message="FHE training is an experimental feature.*") - - with pytest.raises( - ValueError, - match=re.escape( - "Only 'log_loss' is currently supported if FHE training is enabled" - " (fit_encrypted=True). Got loss='perceptron'" - ), - ): - SGDClassifier( - n_bits=n_bits, - fit_encrypted=True, - loss="perceptron", - random_state=random_state, - parameters_range=parameters_range, - ) + with pytest.raises( + ValueError, + match=re.escape( + "Only 'log_loss' is currently supported if FHE training is enabled" + " (fit_encrypted=True). Got loss='perceptron'" + ), + ): + SGDClassifier( + n_bits=n_bits, + fit_encrypted=True, + loss="perceptron", + random_state=random_state, + parameters_range=parameters_range, + ) with pytest.raises( ValueError, match="Setting 'parameter_range' is mandatory if FHE training is enabled." @@ -109,19 +102,13 @@ def test_fit_error_if_non_binary_targets(n_classes, n_bits, max_iter, parameter_ # Generate a data-set with three target classes x, y = get_blob_data(n_classes=n_classes) - with warnings.catch_warnings(): - - # FHE training is an experimental feature and a warning is raised each time `fit_encrypted` - # is set to True - warnings.filterwarnings("ignore", message="FHE training is an experimental feature.*") - - model = SGDClassifier( - n_bits=n_bits, - fit_encrypted=True, - random_state=random_state, - parameters_range=parameters_range, - max_iter=max_iter, - ) + model = SGDClassifier( + n_bits=n_bits, + fit_encrypted=True, + random_state=random_state, + parameters_range=parameters_range, + max_iter=max_iter, + ) with pytest.raises( NotImplementedError, @@ -148,19 +135,13 @@ def test_fit_single_target_class(n_bits, max_iter, parameter_min_max, use_partia # Generate a data-set with a single target class x, y = get_blob_data(n_classes=2) - with warnings.catch_warnings(): - - # FHE training is an experimental feature and a warning is raised each time `fit_encrypted` - # is set to True - warnings.filterwarnings("ignore", message="FHE training is an experimental feature.*") - - model = SGDClassifier( - n_bits=n_bits, - fit_encrypted=True, - random_state=random_state, - parameters_range=parameters_range, - max_iter=max_iter, - ) + model = SGDClassifier( + n_bits=n_bits, + fit_encrypted=True, + random_state=random_state, + parameters_range=parameters_range, + max_iter=max_iter, + ) if use_partial: with pytest.raises( @@ -219,19 +200,13 @@ def test_encrypted_fit_warning_error_raises(n_bits, max_iter, parameter_min_max) # Generate a data-set with binary target classes x, y = get_blob_data(scale_input=True, parameters_range=parameters_range) - with warnings.catch_warnings(): - - # FHE training is an experimental feature and a warning is raised each time `fit_encrypted` - # is set to True - warnings.filterwarnings("ignore", message="FHE training is an experimental feature.*") - - model = SGDClassifier( - n_bits=n_bits, - fit_encrypted=True, - random_state=random_state, - parameters_range=parameters_range, - max_iter=max_iter, - ) + model = SGDClassifier( + n_bits=n_bits, + fit_encrypted=True, + random_state=random_state, + parameters_range=parameters_range, + max_iter=max_iter, + ) with pytest.warns( UserWarning, @@ -388,73 +363,61 @@ def check_encrypted_fit( fit_kwargs = {} # Initialize the model - with warnings.catch_warnings(): + model = SGDClassifier( + n_bits=n_bits, + fit_encrypted=True, + random_state=random_state, + parameters_range=parameters_range, + max_iter=max_iter, + fit_intercept=fit_intercept, + verbose=True, + **init_kwargs, + ) - # FHE training is an experimental feature and a warning is raised each time `fit_encrypted` - # is set to True - warnings.filterwarnings("ignore", message="FHE training is an experimental feature.*") + # If a RNG instance if provided, use it to set the new model's one + if random_number_generator is not None: + model.random_number_generator = random_number_generator - model = SGDClassifier( - n_bits=n_bits, - fit_encrypted=True, - random_state=random_state, - parameters_range=parameters_range, - max_iter=max_iter, - fit_intercept=fit_intercept, - verbose=True, - **init_kwargs, - ) + # We need to lower the p-error to make sure that the test passes + model.training_p_error = 1e-15 - # If a RNG instance if provided, use it to set the new model's one - if random_number_generator is not None: - model.random_number_generator = random_number_generator + if partial_fit: + # Check that we can swap between disable and simulation modes without any impact on the + # final training performance + for index in range(max_iter): + if index % 2 == 0: + model.partial_fit(x, y, fhe="disable", classes=numpy.unique(y)) + else: - # We need to lower the p-error to make sure that the test passes - model.training_p_error = 1e-15 + # We don't need to provide `classes` if index>=1 + model.partial_fit(x, y, fhe="simulate") - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", - message="ONNX Preprocess - Removing mutation from node aten::sub_ on block input.*", - ) + # Check that we raise an error if we call `partial_fit` with a different classes. + with pytest.raises( + ValueError, + match="classes=.* is not the same as on last call to partial_fit, was: .*", + ): + model.partial_fit( + x, y, fhe="disable", classes=numpy.array(numpy.unique(y).tolist() + [len(y)]) + ) - if partial_fit: - # Check that we can swap between disable and simulation modes without any impact on the - # final training performance - for index in range(max_iter): - if index % 2 == 0: - model.partial_fit(x, y, fhe="disable", classes=numpy.unique(y)) - else: - - # We don't need to provide `classes` if index>=1 - model.partial_fit(x, y, fhe="simulate") - - # Check that we raise an error if we call `partial_fit` with a different classes. - with pytest.raises( - ValueError, - match="classes=.* is not the same as on last call to partial_fit, was: .*", - ): - model.partial_fit( - x, y, fhe="disable", classes=numpy.array(numpy.unique(y).tolist() + [len(y)]) - ) - - elif warm_fit: - - # Check that we can swap between disable and simulation modes without any impact on the - # final training performance - half_iter = max_iter // 2 - model.max_iter = half_iter - model.fit(x, y, fhe="disable") + elif warm_fit: - other_iter = max_iter - half_iter - model.max_iter = other_iter - model.fit(x, y, fhe="simulate") + # Check that we can swap between disable and simulation modes without any impact on the + # final training performance + half_iter = max_iter // 2 + model.max_iter = half_iter + model.fit(x, y, fhe="disable") - assert half_iter + other_iter == max_iter + other_iter = max_iter - half_iter + model.max_iter = other_iter + model.fit(x, y, fhe="simulate") - else: - # Fit the model - model.fit(x, y, fhe=fhe, **fit_kwargs) + assert half_iter + other_iter == max_iter + + else: + # Fit the model + model.fit(x, y, fhe=fhe, **fit_kwargs) y_pred_class = model.predict(x) y_pred_proba = model.predict_proba(x) diff --git a/tests/sklearn/test_sklearn_models.py b/tests/sklearn/test_sklearn_models.py index b9cb33e84a..34f3d0d024 100644 --- a/tests/sklearn/test_sklearn_models.py +++ b/tests/sklearn/test_sklearn_models.py @@ -38,7 +38,7 @@ import pytest import torch from sklearn.decomposition import PCA -from sklearn.exceptions import ConvergenceWarning, UndefinedMetricWarning +from sklearn.exceptions import UndefinedMetricWarning from sklearn.metrics import make_scorer, matthews_corrcoef, top_k_accuracy_score from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline @@ -145,10 +145,7 @@ def preamble(model_class, parameters, n_bits, load_data, is_weekly_option): # Get the data-set. The data generation is seeded in load_data. model = instantiate_model_generic(model_class, n_bits=n_bits) x, y = get_dataset(model_class, parameters, n_bits, load_data, is_weekly_option) - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) return model, x @@ -169,10 +166,7 @@ def get_n_bits_non_correctness(model_class): def fit_and_compile(model, x, y): """Fit the model and compile it.""" - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) model.compile(x) @@ -194,10 +188,7 @@ def check_correctness_with_sklearn( model = instantiate_model_generic(model_class, n_bits=n_bits, **hyper_parameters) - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model, sklearn_model = model.fit_benchmark(x, y) + model, sklearn_model = model.fit_benchmark(x, y) model_name = get_model_name(model_class) acceptance_r2score = 0.9 @@ -270,88 +261,83 @@ def check_double_fit(model_class, n_bits, x_1, x_2, y_1, y_2): model = instantiate_model_generic(model_class, n_bits=n_bits) - # Sometimes, we miss convergence, which is not a problem for our test - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - - # Set the torch seed manually before fitting a neural network - if is_model_class_in_a_list(model_class, _get_sklearn_neural_net_models()): + # Set the torch seed manually before fitting a neural network + if is_model_class_in_a_list(model_class, _get_sklearn_neural_net_models()): - # Generate a seed for PyTorch - main_seed = numpy.random.randint(0, 2**63) - torch.manual_seed(main_seed) + # Generate a seed for PyTorch + main_seed = numpy.random.randint(0, 2**63) + torch.manual_seed(main_seed) - # Fit and predict on the first dataset - model.fit(x_1, y_1) - y_pred_1 = model.predict(x_1) + # Fit and predict on the first dataset + model.fit(x_1, y_1) + y_pred_1 = model.predict(x_1) - # Store the input and output quantizers - input_quantizers_1 = copy.copy(model.input_quantizers) - output_quantizers_1 = copy.copy(model.output_quantizers) + # Store the input and output quantizers + input_quantizers_1 = copy.copy(model.input_quantizers) + output_quantizers_1 = copy.copy(model.output_quantizers) - # Set the same torch seed manually before re-fitting the neural network - if is_model_class_in_a_list(model_class, _get_sklearn_neural_net_models()): - torch.manual_seed(main_seed) + # Set the same torch seed manually before re-fitting the neural network + if is_model_class_in_a_list(model_class, _get_sklearn_neural_net_models()): + torch.manual_seed(main_seed) - # Re-fit on the second dataset - model.fit(x_2, y_2) + # Re-fit on the second dataset + model.fit(x_2, y_2) - # Check that predictions are different - y_pred_2 = model.predict(x_2) - assert not numpy.array_equal(y_pred_1, y_pred_2) + # Check that predictions are different + y_pred_2 = model.predict(x_2) + assert not numpy.array_equal(y_pred_1, y_pred_2) - # Store the new input and output quantizers - input_quantizers_2 = copy.copy(model.input_quantizers) - output_quantizers_2 = copy.copy(model.output_quantizers) + # Store the new input and output quantizers + input_quantizers_2 = copy.copy(model.input_quantizers) + output_quantizers_2 = copy.copy(model.output_quantizers) - # Random forest and decision tree classifiers can have identical output_quantizers - # This is because targets are integers, while these models have a fixed output - # precision, which leads the output scale to be the same between models with similar target - # classes range - if is_model_class_in_a_list( - model_class, - _get_sklearn_tree_models(classifier=True, select=["RandomForest", "DecisionTree"]), - ): - quantizers_1 = input_quantizers_1 - quantizers_2 = input_quantizers_2 - else: - quantizers_1 = input_quantizers_1 + output_quantizers_1 - quantizers_2 = input_quantizers_2 + output_quantizers_2 + # Random forest and decision tree classifiers can have identical output_quantizers + # This is because targets are integers, while these models have a fixed output + # precision, which leads the output scale to be the same between models with similar target + # classes range + if is_model_class_in_a_list( + model_class, + _get_sklearn_tree_models(classifier=True, select=["RandomForest", "DecisionTree"]), + ): + quantizers_1 = input_quantizers_1 + quantizers_2 = input_quantizers_2 + else: + quantizers_1 = input_quantizers_1 + output_quantizers_1 + quantizers_2 = input_quantizers_2 + output_quantizers_2 - # Check that the new quantizers are different from the first ones. This is because we - # currently expect all quantizers to be re-computed when re-fitting a model + # Check that the new quantizers are different from the first ones. This is because we + # currently expect all quantizers to be re-computed when re-fitting a model - assert all( - quantizer_1 != quantizer_2 - for (quantizer_1, quantizer_2) in zip(quantizers_1, quantizers_2) - ) + assert all( + quantizer_1 != quantizer_2 for (quantizer_1, quantizer_2) in zip(quantizers_1, quantizers_2) + ) - # Set the same torch seed manually before re-fitting the neural network - if is_model_class_in_a_list(model_class, _get_sklearn_neural_net_models()): - torch.manual_seed(main_seed) + # Set the same torch seed manually before re-fitting the neural network + if is_model_class_in_a_list(model_class, _get_sklearn_neural_net_models()): + torch.manual_seed(main_seed) - # Re-fit on the first dataset again - model.fit(x_1, y_1) + # Re-fit on the first dataset again + model.fit(x_1, y_1) - # Check that predictions are identical to the first ones - y_pred_3 = model.predict(x_1) - assert numpy.array_equal(y_pred_1, y_pred_3) + # Check that predictions are identical to the first ones + y_pred_3 = model.predict(x_1) + assert numpy.array_equal(y_pred_1, y_pred_3) - # Store the new input and output quantizers - input_quantizers_3 = copy.copy(model.input_quantizers) - output_quantizers_3 = copy.copy(model.output_quantizers) + # Store the new input and output quantizers + input_quantizers_3 = copy.copy(model.input_quantizers) + output_quantizers_3 = copy.copy(model.output_quantizers) - # Check that the new quantizers are identical from the first ones. Again, we expect the - # quantizers to be re-computed when re-fitting. Since we used the same dataset as the first - # fit, we also expect these quantizers to be the same. + # Check that the new quantizers are identical from the first ones. Again, we expect the + # quantizers to be re-computed when re-fitting. Since we used the same dataset as the first + # fit, we also expect these quantizers to be the same. - assert all( - quantizer_1 == quantizer_3 - for (quantizer_1, quantizer_3) in zip( - input_quantizers_1 + output_quantizers_1, - input_quantizers_3 + output_quantizers_3, - ) + assert all( + quantizer_1 == quantizer_3 + for (quantizer_1, quantizer_3) in zip( + input_quantizers_1 + output_quantizers_1, + input_quantizers_3 + output_quantizers_3, ) + ) def check_serialization(model, x, use_dump_method): @@ -487,18 +473,14 @@ def check_offset(model_class, n_bits, x, y): """Check offset.""" model = instantiate_model_generic(model_class, n_bits=n_bits) - # Sometimes, we miss convergence, which is not a problem for our test - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) + # Add the offset: here, we really need to fit, we can't reuse an already fitted model + y += 3 + model.fit(x, y) + model.predict(x[:1]) - # Add the offset: here, we really need to fit, we can't reuse an already fitted model - y += 3 - model.fit(x, y) - model.predict(x[:1]) - - # Another offset: here, we really need to fit, we can't reuse an already fitted model - y -= 2 - model.fit(x, y) + # Another offset: here, we really need to fit, we can't reuse an already fitted model + y -= 2 + model.fit(x, y) def check_inference_methods(model, model_class, x, check_float_array_equal): @@ -701,10 +683,7 @@ def cast_input(x, y, input_type): model = instantiate_model_generic(model_class, n_bits=n_bits) x, y = cast_input(x, y, input_type=input_type) - # Sometimes, we miss convergence, which is not a problem for our test - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) # Make sure `predict` is working when FHE is disabled model.predict(x) @@ -770,11 +749,7 @@ def check_pipeline(model_class, x, y): # We need a small number of splits, especially for the KNN model, which has a small data-set grid_search = GridSearchCV(pipe_cv, param_grid, error_score="raise", cv=2) - # Sometimes, we miss convergence, which is not a problem for our test - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=ConvergenceWarning) - - grid_search.fit(x, y) + grid_search.fit(x, y) def check_grid_search(model_class, x, y, scoring): @@ -804,8 +779,6 @@ def check_grid_search(model_class, x, y, scoring): } with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) warnings.simplefilter("ignore", category=UndefinedMetricWarning) # KNeighborsClassifier does not provide a predict_proba method for now @@ -895,12 +868,8 @@ def check_hyper_parameters( model = instantiate_model_generic(model_class, n_bits=n_bits, **hyper_parameters) # Also fit with these hyper parameters to check it works fine - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - - # Here, we really need to fit, to take into account hyper parameters - model.fit(x, y) + # Here, we really need to fit, to take into account hyper parameters + model.fit(x, y) # Check correctness with sklearn check_correctness_with_sklearn( @@ -966,10 +935,7 @@ def check_fitted_compiled_error_raises(model_class, n_bits, x, y): with pytest.raises(AttributeError, match=".* model is not fitted.*"): model.decision_function(x) - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) # Predicting in FHE using a trained model that is not compiled should not be possible with pytest.raises(AttributeError, match=".* model is not compiled.*"): @@ -994,10 +960,7 @@ def check_class_mapping(model, x, y): assert numpy.array_equal(numpy.arange(len(classes)), classes) # Fit the model - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) # Compute the predictions y_pred = model.predict(x) @@ -1009,10 +972,7 @@ def check_class_mapping(model, x, y): new_y = classes[y] # Fit the model using these new targets - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, new_y) + model.fit(x, new_y) # Compute the predictions y_pred_shuffled = model.predict(x) @@ -1034,10 +994,7 @@ def check_exposition_of_sklearn_attributes(model, x, y): ): getattr(model, training_attribute) - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) for name in vars(model.sklearn_model): if name.endswith("_") and not name.endswith("__"): @@ -1092,10 +1049,7 @@ def check_exposition_structural_methods_decision_trees(model, x, y): ): model.get_depth() - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) # Get the number of leaves from both the scikit-learn and Concrete ML models concrete_value = model.get_n_leaves() @@ -1126,10 +1080,7 @@ def check_load_fitted_sklearn_linear_models(model_class, n_bits, x, y, check_flo model = instantiate_model_generic(model_class, n_bits=n_bits) # Fit the model and retrieve both the Concrete ML and the scikit-learn models - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - concrete_model, sklearn_model = model.fit_benchmark(x, y) + concrete_model, sklearn_model = model.fit_benchmark(x, y) # This step is needed in order to handle partial classes model_class = get_model_class(model_class) @@ -2002,10 +1953,7 @@ def test_valid_n_bits_setting( model = instantiate_model_generic(model_class, n_bits=n_bits) - with warnings.catch_warnings(): - # Sometimes, we miss convergence, which is not a problem for our test - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) # A type error will be raised for NeuralNetworks, which is tested in test_failure_bad_data_types @@ -2068,10 +2016,7 @@ def test_initialization_variables_and_defaults_match( model = instantiate_model_generic(model_class, n_bits=n_bits) # Fit the model to create the equivalent sklearn model - with warnings.catch_warnings(): - # Ignore convergence warnings - warnings.simplefilter("ignore", category=ConvergenceWarning) - model.fit(x, y) + model.fit(x, y) # Assert the sklearn model has been created assert hasattr(model, "sklearn_model"), "Sklearn model not found"