Number of bootstrapps

Hi,
I was wondering how I can get the number of bootstrapps in a NN. Is that equal to the number of Relu operations times the number of ciphertexts per message?
Also, I assume we have multiple ciphertexts per each messge, since each cipher encrypts 2-4bits of data. Is that true?

Hi @nina,

You can use advanced options during the compilation to get more information about your circuit:

import torch
import torch.nn as nn
from concrete.ml.torch.compile import compile_torch_model
from concrete.fhe import Configuration
import numpy as np

# Define a simple neural network
class SmallNN(nn.Module):
    def __init__(self):
        super(SmallNN, self).__init__()
        self.fc1 = nn.Linear(2, 1)  # Input layer with 2 features

    def forward(self, x):
        x = torch.relu(self.fc1(x))
        return x

inpuset = torch.tensor([np.random.rand(1, 2) for _ in range(2)])

circuit = compile_torch_model(model, 
                              inpuset, 
                              n_bits=3, 
                              show_mlir=True,  # Displays the precision of each layer
                                               # Note: `FHELinalg.apply_lookup_table` indicates where non-linear operations occur (like, relu)
                              configuration=Configuration(show_statistics=True)  # Provides the total number of PBS operations (see `programmable_bootstrap_count`)
)

Keep in mind that these are advanced features and require some expertise in cryptography. I recommend checking out the Concrete documentation for more details.

Thanks for your response.
I printed out the statistics for a network with 3 convolution layers followed by a fully connected layer. The output looks like below:

Statistics
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
size_of_secret_keys: 42656
size_of_bootstrap_keys: 799014912
size_of_keyswitch_keys: 251052032
p_error: 0.09680426355045492
global_p_error: 0.9999999999999999
complexity: 445263397376.0
functions: {
    _clear_forward_proxy: {
        size_of_inputs: 1049088
        size_of_outputs: 163920
        programmable_bootstrap_count: 5056
        programmable_bootstrap_count_per_parameter: {
            BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=512, level=2, base_log=16, variance=8.442253112932959e-31): 4672
            BootstrapKeyParam(polynomial_size=2048, glwe_dimension=1, input_lwe_dimension=724, level=8, base_log=5, variance=8.442253112932959e-31): 384
        }
        key_switch_count: 5408
        key_switch_count_per_parameter: {
            KeyswitchKeyParam(level=4, base_log=2, variance=4.896863592478985e-07): 4672
            KeyswitchKeyParam(level=7, base_log=2, variance=2.5707835032807777e-10): 384
            KeyswitchKeyParam(level=4, base_log=10, variance=8.442253112932959e-31): 352
        }
        packing_key_switch_count: 0
        clear_addition_count: 10112
        clear_addition_count_per_parameter: {
            LweSecretKeyParam(dimension=2048): 10112
        }
        encrypted_addition_count: 14240
        encrypted_addition_count_per_parameter: {
            LweSecretKeyParam(dimension=2048): 14240
        }
        clear_multiplication_count: 14240
        clear_multiplication_count_per_parameter: {
            LweSecretKeyParam(dimension=2048): 14240
        }
        encrypted_negation_count: 4672
        encrypted_negation_count_per_parameter: {
            LweSecretKeyParam(dimension=2048): 4672
        }
    }
}

I’m curious why there are two different bootstrap key parameters and three key switch key parameters. Is there a different bootstrap key for each layer? If so, why aren’t there three of them?

Thanks!

Hello @nina,

I can’t say for sure why the compiler have choosen several bootstrap without seeing the (MLIR) program that is compiled but the more probable explanation with compiler default options would be that you have several layers which run on two different bitwidths. By default the optimizer create one partition by bitwidth, as lut is implemented with a keyswitch then a bootstrap is why you see to pair of keyswitch/bootstrap with the same number of op, while the third keyswitch is probably a keyswitch beetween the two partitions.

If you want to confirm or understand more how the compiler transform your program you can set the compilation option compiler_verbose_mode and/or compiler_debug_mode to True.