This guide explains how to optimize cryptographic parameters by specifying composition when using modules.
When using modules, make sure to specify composition so that the compiler can select more optimal parameters based on how the functions in the module would be used.
For example:
import numpy as npfrom concrete import fhe@fhe.module()classPowerWithoutComposition:@fhe.function({"x": "encrypted"})defsquare(x):return x **2@fhe.function({"x": "encrypted"})defcube(x):return x **3without_composition = PowerWithoutComposition.compile( {"square": fhe.inputset(fhe.uint2),"cube": fhe.inputset(fhe.uint4), })print(f"without composition -> {int(without_composition.complexity):>10_} complexity")@fhe.module()classPowerWithComposition:@fhe.function({"x": "encrypted"})defsquare(x):return x **2@fhe.function({"x": "encrypted"})defcube(x):return x **3 composition = fhe.Wired( [ fhe.Wire(fhe.Output(square, 0), fhe.Input(cube, 0)) ] )with_composition = PowerWithComposition.compile( {"square": fhe.inputset(fhe.uint2),"cube": fhe.inputset(fhe.uint4), })print(f" with composition -> {int(with_composition.complexity):>10_} complexity")
This prints:
without composition -> 185_863_835 complexity
with composition -> 135_871_612 complexity
It means that specifying composition resulted in ~35% improvement to complexity for computing cube(square(x)).