From 9dcf1c4b6fc08930d4c5e97bcda1cc29949e99a9 Mon Sep 17 00:00:00 2001 From: Bourgerie Quentin Date: Tue, 12 Mar 2024 11:30:22 +0100 Subject: [PATCH] feat(frontend-python): Expose compress_input_ciphertexts as a compilation options and tests --- .../lib/Bindings/Python/CompilerAPIModule.cpp | 4 ++ .../concrete/compiler/compilation_options.py | 13 ++++ .../concrete/fhe/compilation/configuration.py | 4 ++ .../concrete/fhe/compilation/server.py | 1 + .../tests/execution/test_compressions.py | 62 +++++++++++++++++++ 5 files changed, 84 insertions(+) create mode 100644 frontends/concrete-python/tests/execution/test_compressions.py diff --git a/compilers/concrete-compiler/compiler/lib/Bindings/Python/CompilerAPIModule.cpp b/compilers/concrete-compiler/compiler/lib/Bindings/Python/CompilerAPIModule.cpp index d850dadfa..d44d07d37 100644 --- a/compilers/concrete-compiler/compiler/lib/Bindings/Python/CompilerAPIModule.cpp +++ b/compilers/concrete-compiler/compiler/lib/Bindings/Python/CompilerAPIModule.cpp @@ -713,6 +713,10 @@ void mlir::concretelang::python::populateCompilerAPISubmodule( [](CompilationOptions &options, bool b) { options.compressEvaluationKeys = b; }) + .def("set_compress_input_ciphertexts", + [](CompilationOptions &options, bool b) { + options.compressInputCiphertexts = b; + }) .def("set_optimize_concrete", [](CompilationOptions &options, bool b) { options.optimizeTFHE = b; }) .def("set_p_error", diff --git a/compilers/concrete-compiler/compiler/lib/Bindings/Python/concrete/compiler/compilation_options.py b/compilers/concrete-compiler/compiler/lib/Bindings/Python/concrete/compiler/compilation_options.py index b3c0294bf..1400b0b81 100644 --- a/compilers/concrete-compiler/compiler/lib/Bindings/Python/concrete/compiler/compilation_options.py +++ b/compilers/concrete-compiler/compiler/lib/Bindings/Python/concrete/compiler/compilation_options.py @@ -113,6 +113,19 @@ class CompilationOptions(WrapperCpp): raise TypeError("can't set the option to a non-boolean value") self.cpp().set_compress_evaluation_keys(compress_evaluation_keys) + def set_compress_input_ciphertexts(self, compress_input_ciphertexts: bool): + """Set option for compression of input ciphertexts. + + Args: + compress_input_ciphertexts (bool): whether to turn it on or off + + Raises: + TypeError: if the value to set is not boolean + """ + if not isinstance(compress_input_ciphertexts, bool): + raise TypeError("can't set the option to a non-boolean value") + self.cpp().set_compress_input_ciphertexts(compress_input_ciphertexts) + def set_verify_diagnostics(self, verify_diagnostics: bool): """Set option for diagnostics verification. diff --git a/frontends/concrete-python/concrete/fhe/compilation/configuration.py b/frontends/concrete-python/concrete/fhe/compilation/configuration.py index ee1b73941..b6440b218 100644 --- a/frontends/concrete-python/concrete/fhe/compilation/configuration.py +++ b/frontends/concrete-python/concrete/fhe/compilation/configuration.py @@ -956,6 +956,7 @@ class Configuration: dataflow_parallelize: bool auto_parallelize: bool compress_evaluation_keys: bool + compress_input_ciphertexts: bool p_error: Optional[float] global_p_error: Optional[float] insecure_key_cache_location: Optional[str] @@ -1005,6 +1006,7 @@ class Configuration: dataflow_parallelize: bool = False, auto_parallelize: bool = False, compress_evaluation_keys: bool = False, + compress_input_ciphertexts: bool = False, p_error: Optional[float] = None, global_p_error: Optional[float] = None, auto_adjust_rounders: bool = False, @@ -1068,6 +1070,7 @@ class Configuration: self.dataflow_parallelize = dataflow_parallelize self.auto_parallelize = auto_parallelize self.compress_evaluation_keys = compress_evaluation_keys + self.compress_input_ciphertexts = compress_input_ciphertexts self.p_error = p_error self.global_p_error = global_p_error self.auto_adjust_rounders = auto_adjust_rounders @@ -1165,6 +1168,7 @@ class Configuration: dataflow_parallelize: Union[Keep, bool] = KEEP, auto_parallelize: Union[Keep, bool] = KEEP, compress_evaluation_keys: Union[Keep, bool] = KEEP, + compress_input_ciphertexts: Union[Keep, bool] = KEEP, p_error: Union[Keep, Optional[float]] = KEEP, global_p_error: Union[Keep, Optional[float]] = KEEP, auto_adjust_rounders: Union[Keep, bool] = KEEP, diff --git a/frontends/concrete-python/concrete/fhe/compilation/server.py b/frontends/concrete-python/concrete/fhe/compilation/server.py index 8ec8ea007..a5083fb92 100644 --- a/frontends/concrete-python/concrete/fhe/compilation/server.py +++ b/frontends/concrete-python/concrete/fhe/compilation/server.py @@ -122,6 +122,7 @@ class Server: options.set_dataflow_parallelize(configuration.dataflow_parallelize) options.set_auto_parallelize(configuration.auto_parallelize) options.set_compress_evaluation_keys(configuration.compress_evaluation_keys) + options.set_compress_input_ciphertexts(configuration.compress_input_ciphertexts) options.set_composable(configuration.composable) if configuration.auto_parallelize or configuration.dataflow_parallelize: diff --git a/frontends/concrete-python/tests/execution/test_compressions.py b/frontends/concrete-python/tests/execution/test_compressions.py new file mode 100644 index 000000000..9e6885cd8 --- /dev/null +++ b/frontends/concrete-python/tests/execution/test_compressions.py @@ -0,0 +1,62 @@ +""" +Basic tests of compressions feature. +""" + +from concrete import fhe + + +def test_circuit_compress_input_ciphertexts(helpers): + """ + Test running circuit with compressed inpu ciphertexts + """ + + configuration = helpers.configuration() + + @fhe.compiler({"x": "encrypted", "y": "encrypted"}) + def function(x, y): + return (x + y) ** 2 + + inputset = fhe.inputset(fhe.uint4, fhe.uint4, size=10) + print(f"inputset {inputset}") + circuit_without_compression = function.compile( + inputset, configuration.fork(compress_input_ciphertexts=False) + ) + circuit_with_compression = function.compile( + inputset, configuration.fork(compress_input_ciphertexts=True) + ) + + x, y = circuit_without_compression.encrypt(1, 2) + x_compressed, y_compressed = circuit_with_compression.encrypt(1, 2) + + assert len(x_compressed.serialize()) < len(x.serialize()) + assert len(y_compressed.serialize()) < len(y.serialize()) + + assert circuit_with_compression.decrypt( + circuit_with_compression.run(x_compressed, y_compressed) + ) == circuit_without_compression.decrypt(circuit_without_compression.run(x, y)) + + +def test_circuit_compress_evaluation_keys(helpers): + """ + Test running circuit with compressed evaluation keys + """ + + configuration = helpers.configuration() + + @fhe.compiler({"x": "encrypted", "y": "encrypted"}) + def function(x, y): + return (x + y) ** 2 + + inputset = fhe.inputset(fhe.uint4, fhe.uint4, size=10) + print(f"inputset {inputset}") + circuit_without_compression = function.compile( + inputset, configuration.fork(compress_evaluation_keys=False) + ) + circuit_with_compression = function.compile( + inputset, configuration.fork(compress_evaluation_keys=True) + ) + + evaluation_keys = circuit_without_compression.client.evaluation_keys + evaluation_keys_compressed = circuit_with_compression.client.evaluation_keys + + assert len(evaluation_keys_compressed.serialize()) < len(evaluation_keys.serialize())