From 1c73b61b1d457ceba629a16df14eab64c5a5be32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20P=C3=A9r=C3=A9?= Date: Thu, 28 Mar 2024 10:24:58 +0100 Subject: [PATCH] doc(frontend): add documentation for fhe modules --- docs/SUMMARY.md | 1 + docs/compilation/modules.md | 57 ++++++++++++++++++++++++++++++++++ docs/guides/deploy.md | 61 +++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 docs/compilation/modules.md diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 2456e5a742..a3d75540e6 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -31,6 +31,7 @@ * [Reuse arguments](compilation/reuse_arguments.md) * [Multi precision](compilation/multi_precision.md) * [Multi parameters](compilation/multi_parameters.md) +* [Modules](compilation/modules.md) * [Decorator](compilation/decorator.md) * [Direct circuits](compilation/direct_circuits.md) diff --git a/docs/compilation/modules.md b/docs/compilation/modules.md new file mode 100644 index 0000000000..b5bfb7047d --- /dev/null +++ b/docs/compilation/modules.md @@ -0,0 +1,57 @@ +# Modules + +{% hint style="warning" %} +Modules are still experimental. They are only compatible with composition, that is, all function outputs can be used as inputs for every functions. The crypto-parameters used in this mode are pretty large, and will likely give slow execution time. +{% endhint %} + +In some cases, it might be interesting to deploy a server that is able to execute different functions. With *Concrete*, it is possible to compile fhe _modules_, that can contain many different functions, at once. All the functions are compiled in a single step and can be [deployed with the same artifacts](../guides/deploy.md#deployment-of-modules). Here is an example: + +```python +from concrete import fhe + +@fhe.module() +class MyModule: + @fhe.function({"x": "encrypted"}) + def inc(x): + return x + 1 % 20 + + @fhe.function({"x": "encrypted"}) + def dec(x): + return x - 1 % 20 +``` + +The `MyModule` fhe module can then be compiled using the `compile` method, by providing a dictionnary of input sets for every functions: + +```python +inputset = list(range(20)) +configuration = fhe.Configuration( + parameter_selection_strategy="v0", + composable=True, +) +my_module = MyModule.compile( + {"inc": inputset, "dec": inputset}, + configuration, +``` + +{% hint style="warning" %} +Note that here we can see a current limitation of modules: The configuration must use the `parameter_selection_strategy` of `v0`, and activate the `composable` flag. +{% endhint %} + +After the module has been compiled, we can encrypt and call the different functions in the following way: + +```python +x = 5 +x_enc = my_module.inc.encrypt(x) +x_inc_enc = my_module.inc.run(x_enc) +x_inc = my_module.inc.decrypt(x_inc_enc) +assert x_inc == 6 + +x_inc_dec_enc = my_module.dec.run(x_inc_enc) +x_inc_dec = my_module.dec.decrypt(x_inc_dec_enc) +assert x_inc_dec == 5 + +for _ in range(10): + x_enc = my_module.inc.run(x_enc) +x_dec = my_module.inc.decrypt(x_enc) +assert x_dec == 15 +``` diff --git a/docs/guides/deploy.md b/docs/guides/deploy.md index 734c5a8267..5e5bfc2d19 100644 --- a/docs/guides/deploy.md +++ b/docs/guides/deploy.md @@ -131,3 +131,64 @@ Then, decrypt the result: decrypted_result = client.decrypt(deserialized_result) assert decrypted_result == 49 ``` + +# Deployment of modules + +Deploying a [module](../compilation/modules.md#modules) follows the same logic presented above. Assuming a module compiled in the following way: + + +```python +from concrete import fhe + +@fhe.module() +class MyModule: + @fhe.function({"x": "encrypted"}) + def inc(x): + return x + 1 + + @fhe.function({"x": "encrypted"}) + def dec(x): + return x - 1 + +inputset = list(range(20)) +configuration = fhe.Configuration( + parameter_selection_strategy="v0", + composable=True, +) +my_module = MyModule.compile( + {"inc": inputset, "dec": inputset}, + configuration, +) +``` + +The server can be extracted from the module and saved to a file: + + +```python +my_module.server.save("server.zip") +``` + +The only noticeable difference with the workflow presented above is the `Client::encrypt`, `Client::decrypt` and `Server::run` methods must be given an extra `function_name` argument specifying the name of the targeted function. + +The encryption of an argument for the `inc` function of the module would be: + + +```python +arg = client.encrypt(7, function_name="inc") +serialized_arg = arg.serialize() +``` + +The execution of the `inc` function would be : + + +```python +result = server.run(deserialized_arg, evaluation_keys=deserialized_evaluation_keys, function_name="inc") +serialized_result = result.serialize() +``` + +Finally, decrypting a result from the execution of `dec` would be: + + +```python +decrypted_result = client.decrypt(deserialized_result, function_name="dec") +``` \ No newline at end of file