Skip to content

CKKS Bootstrapping

When a ciphertext is created, it has a fixed multiplication count called a level. When two cipehrtexts are multiplied, the resulting ciphertext will have the level reduced by one compared to the input ciphertexts. A ciphertext with level 0 can no longer be multiplied. It is possible to reset this level to a higher value by the bootstrapping operation.

Regular Bootstrap

The Bootstrapping operation requires the bootstrap key. The bootstrap key incorporates every single fixed rotation key that is needed by the bootstrap operation. Because of this, the bootstrap key is quite large (about 12.3GB).

from desilofhe import Engine

engine = Engine(use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key, stage_count=3)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)

Small Bootstrap Key

Alternatively, the bootstrapping operation can be performed using the rotation key and the small bootstrap key instead of the bootstrapping key. This approach reduces memory usage to around 3.8GB but makes the operation slower.

from desilofhe import Engine

engine = Engine(use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
rotation_key = engine.create_rotation_key(secret_key)
small_bootstrap_key = engine.create_small_bootstrap_key(secret_key)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
)

bootstrapped_stage_count_5 = engine.bootstrap(
    ciphertext,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
    stage_count=5,
)

Benchmark

Here are the benchmarks of the bootstrapping operation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 102.142 40.127 28.831 1.649 1.116
Small 4 65.433 25.474 17.845 1.080 0.676
Small 5 65.465 24.389 16.970 0.924 0.522
Medium 3 30.083 11.652 8.980 0.366 0.113
Medium 4 24.247 9.328 7.159 0.294 0.092
Medium 5 23.358 8.857 6.559 0.273 0.086
Large 3 27.274 10.899 8.676 0.371 0.110
Large 4 23.764 9.253 7.345 0.314 0.095
Large 5 21.490 8.222 6.275 0.270 0.083

Compact Representation

Here are the benchmarks of the bootstrapping operation using the compact representation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 95.531 39.116 27.229 1.758 1.173
Small 4 62.911 25.058 17.496 1.118 0.704
Small 5 61.700 23.474 16.829 0.967 0.551
Medium 3 28.842 11.591 9.065 0.385 0.124
Medium 4 23.613 9.659 7.426 0.322 0.104
Medium 5 22.763 9.123 6.828 0.306 0.099
Large 3 26.814 11.193 9.044 0.389 0.122
Large 4 23.227 9.753 7.841 0.339 0.109
Large 5 21.066 8.704 6.726 0.312 0.097

Bootstrap To 14 Levels

The original parameter set for bootstrapping leaves 10 levels after 3 stages. To overcome this, a new parameter set has been introduced to have 14 levels after the bootstrapping operation. Due to the limitations of the new parameter set, the only supported stage count is 3.

from desilofhe import Engine

engine = Engine(use_bootstrap_to_14_levels=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)

Small Bootstrap Key

The small boostrap key can also be used for this parameter set.

from desilofhe import Engine

engine = Engine(use_bootstrap_to_14_levels=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
rotation_key = engine.create_rotation_key(secret_key)
small_bootstrap_key = engine.create_small_bootstrap_key(secret_key)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
)

Benchmark

Here are the benchmarks of the bootstrapping operation to 14 levels. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 126.981 48.639 34.610 1.758 1.184
Medium 3 36.930 14.124 11.013 0.411 0.139
Large 3 32.933 13.135 10.591 0.417 0.135

Compact Representation

Here are the benchmarks of the bootstrapping operation to 14 levels using the compact representation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 102.617 40.055 27.818 1.703 1.155
Medium 3 29.043 11.619 9.073 0.412 0.119
Large 3 27.360 10.979 8.879 0.402 0.116

Bootstrap To 17 Levels

A new parameter set has been introduced to have 17 levels after the bootstrapping operation. Due to the limitations of the new parameter set, the only supported stage count is 3.

from desilofhe import Engine

engine = Engine(use_bootstrap_to_17_levels=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)

Small Bootstrap Key

The small bootstrap key can also be used for this parameter set.

from desilofhe import Engine

engine = Engine(use_bootstrap_to_17_levels=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
rotation_key = engine.create_rotation_key(secret_key)
small_bootstrap_key = engine.create_small_bootstrap_key(secret_key)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
)

Benchmark

Here are the benchmarks of the bootstrapping operation to 17 levels. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 150.139 58.579 41.691 1.882 1.245
Medium 3 44.242 17.483 13.500 0.470 0.163
Large 3 40.459 16.071 12.887 0.474 0.157

Compact Representation

Here are the benchmarks of the bootstrapping operation to 17 levels using the compact representation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 122.399 47.465 32.798 1.835 1.206
Medium 3 35.447 13.923 10.789 0.416 0.136
Large 3 32.417 13.038 10.507 0.424 0.133

Sparse Bootstrap

The bootstrapping operation is faster with a reduced slot_count. The sparse bootstrapping operation can be done with either a bootstrap key or a small bootstrap key.

Bootstrap Key

from desilofhe import Engine

engine = Engine(slot_count=1024, use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key, stage_count=3)

message = [-1, 0, 1, 0] * 256
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)

Small Bootstrap Key

from desilofhe import Engine

engine = Engine(slot_count=1024, use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
rotation_key = engine.create_rotation_key(secret_key)
small_bootstrap_key = engine.create_small_bootstrap_key(secret_key)

message = [-1, 0, 1, 0] * 256
ciphertext = engine.encrypt(message, public_key, level=0)
bootstrapped = engine.bootstrap(
    ciphertext,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
    stage_count=1,
)

Benchmark

Here are the benchmarks of the sparse bootstrapping operation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Slot
Count
Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
32 Small 1 41.213 14.350 10.087 0.383 0.135
32 Small 2 30.841 11.170 7.912 0.289 0.104
32 Medium 1 26.582 9.929 7.222 0.268 0.091
32 Medium 2 25.266 9.044 6.607 0.231 0.084
32 Large 1 26.179 9.861 7.300 0.262 0.092
1024 Small 2 65.439 22.929 16.271 0.635 0.234
1024 Small 3 46.454 16.356 11.544 0.438 0.163
1024 Small 4 42.714 15.238 10.738 0.415 0.152
1024 Medium 2 28.282 10.890 8.205 0.322 0.103
1024 Medium 3 25.252 9.393 6.911 0.262 0.088
1024 Medium 4 23.901 8.794 6.430 0.246 0.083
1024 Large 2 26.636 10.282 8.063 0.314 0.102
1024 Large 3 23.719 9.119 6.910 0.265 0.088

Lossy Bootstap

The lossy bootstrap operation is faster while sacrificing some precision. The resulting significant figures below the decimal is about halved. In general, the regular bootstrapping is the recommended method, but depending on the precision requirements this method could also be useful. The lossy bootstrapping operation can be done with either a lossy bootstrap key or a small bootstrap key. The lossy bootstrapping operation is not supported for the bootstrap to 14 levels or bootstrap to 17 levels parameter sets.

Lossy Bootstrap Key

from desilofhe import Engine

engine = Engine(use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
lossy_bootstrap_key = engine.create_lossy_bootstrap_key(
    secret_key, stage_count=3
)

message = [-1, 0, 1]
ciphertext = engine.encrypt(message, public_key, level=3)
bootstrapped = engine.lossy_bootstrap(
    ciphertext, relinearization_key, conjugation_key, lossy_bootstrap_key
)

Small Bootstrap Key

from desilofhe import Engine

engine = Engine(use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
rotation_key = engine.create_rotation_key(secret_key)
small_bootstrap_key = engine.create_small_bootstrap_key(secret_key)

message = [-1, 0, 1]
ciphertext_level_3 = engine.encrypt(message, public_key, level=3)
bootstrapped = engine.lossy_bootstrap(
    ciphertext_level_3,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
)

ciphertext_level_5 = engine.encrypt(message, public_key, level=5)
bootstrapped_stage_count_5 = engine.lossy_bootstrap(
    ciphertext_level_5,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
    stage_count=5,
)

Benchmark

Here are the benchmarks of the lossy bootstrapping operation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 78.999 31.057 22.661 1.522 1.062
Small 4 53.710 20.687 14.660 0.973 0.637
Small 5 54.320 20.802 14.579 0.829 0.490
Medium 3 23.747 9.153 7.016 0.304 0.092
Medium 4 19.993 7.719 5.789 0.253 0.076
Medium 5 20.154 7.452 5.488 0.230 0.072
Large 3 21.661 8.603 6.812 0.309 0.090
Large 4 19.268 7.601 5.954 0.267 0.079
Large 5 17.877 6.770 5.225 0.231 0.070

Lossy bootstrapping is optimized for ciphertexts that contain only real values.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 73.190 28.853 20.887 1.482 1.045
Small 4 48.441 18.609 13.271 0.905 0.620
Small 5 49.166 18.561 13.326 0.757 0.473
Medium 3 17.696 6.994 5.392 0.247 0.071
Medium 4 14.633 5.610 4.282 0.187 0.057
Medium 5 14.644 5.489 4.057 0.170 0.054
Large 3 15.831 6.367 5.177 0.253 0.069
Large 4 13.851 5.444 4.423 0.210 0.060
Large 5 12.825 4.871 3.797 0.173 0.051

Sign Bootstrap

The sign bootstrapping operation is a variant of the lossy bootstrap that enables bootstrapping of sign values, i.e., -1 and 1, with higher precision. Although this is applicable only to sign values, the resulting ciphertext achieves roughly three times more significant digits. By efficiently reducing the noise of sign values, this operation facilitates fast and high-precision comparison functions such as min and max. The sign bootstrapping can be performed with either a small bootstrap key or a lossy bootstrap key. The sign bootstrapping operation is not supported for the bootstrap to 14 levels or bootstrap to 17 levels parameter sets.

Sign Bootstrap with Lossy Bootstrap Key

from desilofhe import Engine

engine = Engine(use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
lossy_bootstrap_key = engine.create_lossy_bootstrap_key(
    secret_key, stage_count=3
)

message = [-1, 1]
ciphertext = engine.encrypt(message, public_key, level=3)
bootstrapped = engine.sign_bootstrap(
    ciphertext, relinearization_key, conjugation_key, lossy_bootstrap_key
)

Small Bootstrap Key

from desilofhe import Engine

engine = Engine(use_bootstrap=True)
secret_key = engine.create_secret_key()
public_key = engine.create_public_key(secret_key)
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
rotation_key = engine.create_rotation_key(secret_key)
small_bootstrap_key = engine.create_small_bootstrap_key(secret_key)

message = [-1, 1]
ciphertext_level_3 = engine.encrypt(message, public_key, level=3)
bootstrapped = engine.sign_bootstrap(
    ciphertext_level_3,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
)

ciphertext_level_5 = engine.encrypt(message, public_key, level=5)
bootstrapped_stage_count_5 = engine.sign_bootstrap(
    ciphertext_level_5,
    relinearization_key,
    conjugation_key,
    rotation_key,
    small_bootstrap_key,
    stage_count=5,
)

Benchmark

Here are the benchmarks of the sign bootstrapping operation. The experiments were performed on an Intel(R) Core(TM) i7-10700K CPU @ 3.80GHz for the CPU measurements and an NVIDIA GeForce RTX 5090 for the GPU measurements. These values are the averages of 10 runs.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 79.299 30.850 22.544 1.537 1.064
Small 4 54.102 20.695 14.614 1.000 0.641
Small 5 55.350 20.688 14.595 0.846 0.491
Medium 3 24.177 9.278 7.014 0.317 0.091
Medium 4 20.456 7.764 5.802 0.261 0.076
Medium 5 20.064 7.484 5.481 0.240 0.072
Large 3 22.121 8.581 6.808 0.327 0.090
Large 4 19.574 7.630 5.957 0.269 0.079
Large 5 17.791 6.831 5.236 0.236 0.070

Sign bootstrapping is optimized for ciphertexts that contain only real values.

Key
Size
Stage
Count
Runtime (s)
1 Thread

4 Threads

16 Threads
Sync
GPU
Async
GPU
Small 3 73.082 28.868 21.077 1.478 1.047
Small 4 48.573 18.599 13.242 0.914 0.624
Small 5 49.913 18.935 13.304 0.766 0.476
Medium 3 18.074 6.931 5.392 0.242 0.071
Medium 4 14.644 5.619 4.268 0.184 0.057
Medium 5 14.433 5.479 4.051 0.172 0.054
Large 3 15.927 6.404 5.184 0.242 0.069
Large 4 13.647 5.481 4.421 0.202 0.060
Large 5 12.571 4.904 3.805 0.174 0.051

Comparison of Bootstrapping Error for Sign Messages

The following shows the average error observed when bootstrapping sign messages (i.e., -1 and 1) using the regular, lossy, and sign bootstrapping methods, respectively.

Regular
Bootstrap
Lossy
Bootstrap
Sign
Bootstrap
Average error 8.03008e-4 1.60562e-3 1.98124e-08
Precision 10.3 bits 9.3 bits 25.6 bits

Precision of Bootstrapping

The following represents the precision of the bootstrapping operation. It is the average of the bootstrapping error measured over messages uniformly distributed in the interval [-1, 1]. When evaluating the precision of the sign bootstrapping, we use only the sign values (i.e., -1 and 1).

Average error Precision Compact
Average error
Compact
Precision
Bootstrap 6.72878e-07 20.5 bits 6.67666e-07 20.5 bits
Bootstrap to 14 Levels 7.29874e-06 17.1 bits 2.67900e-05 15.2 bits
Bootstrap to 17 Levels 1.08748e-06 19.8 bits 7.02514e-07 20.4 bits
Sparse Bootstrap 2.59681e-07 21.9 bits 2.53088e-07 21.9 bits
Lossy Bootstrap 4.01552e-4 11.3 bits 3.80117e-4 11.4 bits
Sign Bootstrap 1.98124e-08 25.6 bits 2.20190e-08 25.4 bits

The errors were measured with the following code.

Bootstrap
import numpy as np
from desilofhe import Engine

engine = Engine(use_bootstrap=True)

secret_key = engine.create_secret_key()
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key)

message = np.linspace(-1, 1, engine.slot_count)

ciphertext = engine.encrypt(message, secret_key)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)
decrypted = engine.decrypt(bootstrapped, secret_key)

average_noise = np.mean(abs(message - decrypted))
print(f"Bootstrap Average Noise :{average_noise}")
print(f"Bootstrap Average Noise (Bits) :{np.log2(average_noise)}")
Bootstrap to 14 Levels
import numpy as np
from desilofhe import Engine

engine = Engine(use_bootstrap_to_14_levels=True)

secret_key = engine.create_secret_key()
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key)

message = np.linspace(-1, 1, engine.slot_count)

ciphertext = engine.encrypt(message, secret_key)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)
decrypted = engine.decrypt(bootstrapped, secret_key)

average_noise = np.mean(abs(message - decrypted))
print(f"BootstrapTo14Levels Average Noise :{average_noise}")
print(f"BootstrapTo14Levels Average Noise (Bits) :{np.log2(average_noise)}")
Bootstrap to 17 Levels
import numpy as np
from desilofhe import Engine

engine = Engine(use_bootstrap_to_17_levels=True)

secret_key = engine.create_secret_key()
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key)

message = np.linspace(-1, 1, engine.slot_count)

ciphertext = engine.encrypt(message, secret_key)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)
decrypted = engine.decrypt(bootstrapped, secret_key)

average_noise = np.mean(abs(message - decrypted))
print(f"BootstrapTo17Levels Average Noise :{average_noise}")
print(f"BootstrapTo17Levels Average Noise (Bits) :{np.log2(average_noise)}")
Sparse Bootstrap
import numpy as np
from desilofhe import Engine

engine = Engine(slot_count=1024, use_bootstrap=True)

secret_key = engine.create_secret_key()
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
bootstrap_key = engine.create_bootstrap_key(secret_key)

message = np.linspace(-1, 1, engine.slot_count)

ciphertext = engine.encrypt(message, secret_key)
bootstrapped = engine.bootstrap(
    ciphertext, relinearization_key, conjugation_key, bootstrap_key
)
decrypted = engine.decrypt(bootstrapped, secret_key)

average_noise = np.mean(abs(message - decrypted))
print(f"SparseBootstrap Average Noise :{average_noise}")
print(f"SparseBootstrap Average Noise (Bits) :{np.log2(average_noise)}")
Lossy Bootstrap
import numpy as np
from desilofhe import Engine

engine = Engine(use_bootstrap=True)

secret_key = engine.create_secret_key()
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
lossy_bootstrap_key = engine.create_lossy_bootstrap_key(secret_key)

message = np.linspace(-1, 1, engine.slot_count)

ciphertext = engine.encrypt(message, secret_key)
bootstrapped = engine.lossy_bootstrap(
    ciphertext, relinearization_key, conjugation_key, lossy_bootstrap_key
)
decrypted = engine.decrypt(bootstrapped, secret_key)

average_noise = np.mean(abs(message - decrypted))
print(f"Lossy Bootstrap Average Noise :{average_noise}")
print(f"Lossy Bootstrap Average Noise (Bits) :{np.log2(average_noise)}")
Sign Bootstrap
import numpy as np
from desilofhe import Engine

engine = Engine(use_bootstrap=True)

secret_key = engine.create_secret_key()
relinearization_key = engine.create_relinearization_key(secret_key)
conjugation_key = engine.create_conjugation_key(secret_key)
lossy_bootstrap_key = engine.create_lossy_bootstrap_key(secret_key)

message = [-1, 1] * (engine.slot_count // 2)

ciphertext = engine.encrypt(message, secret_key)
bootstrapped = engine.sign_bootstrap(
    ciphertext, relinearization_key, conjugation_key, lossy_bootstrap_key
)
decrypted = engine.decrypt(bootstrapped, secret_key)

average_noise = np.mean(abs(message - decrypted))
print(f"Sign Bootstrap Average Noise :{average_noise}")
print(f"Sign Bootstrap Average Noise (Bits) :{np.log2(average_noise)}")