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 103.213 46.088 35.201 2.422 1.816
Small 4 66.176 29.221 21.871 1.609 1.188
Small 5 66.119 27.987 20.869 1.485 1.070
Medium 3 30.272 13.230 10.561 0.716 0.451
Medium 4 25.032 10.650 8.507 0.583 0.375
Medium 5 23.820 10.068 7.848 0.546 0.356
Large 3 27.786 12.404 10.208 0.695 0.436
Large 4 24.004 10.489 8.714 0.604 0.384
Large 5 21.640 9.320 7.467 0.537 0.341

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 99.087 44.605 33.660 2.999 2.370
Small 4 63.003 28.632 21.374 1.958 1.524
Small 5 61.917 27.276 20.613 1.846 1.406
Medium 3 29.705 13.053 10.647 0.850 0.568
Medium 4 24.732 10.818 8.800 0.727 0.500
Medium 5 23.750 10.224 8.108 0.707 0.492
Large 3 27.861 12.518 10.577 0.825 0.551
Large 4 24.084 11.049 9.277 0.751 0.513
Large 5 21.743 9.788 8.000 0.684 0.475

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 127.611 56.577 42.487 2.652 1.993
Medium 3 38.002 16.288 13.097 0.788 0.507
Large 3 34.272 15.098 12.553 0.772 0.483

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 104.424 46.327 34.195 2.812 2.180
Medium 3 30.574 13.205 10.695 0.775 0.481
Large 3 28.132 12.390 10.434 0.743 0.453

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 155.920 71.682 52.690 2.863 2.141
Medium 3 46.515 20.325 16.255 0.875 0.562
Large 3 42.055 18.741 15.530 0.853 0.533

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 124.762 55.706 41.575 2.945 2.284
Medium 3 36.085 15.950 12.888 0.805 0.521
Large 3 33.789 14.929 12.523 0.783 0.499

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 42.099 16.677 12.298 0.711 0.465
32 Small 2 31.533 12.958 9.681 0.565 0.383
32 Medium 1 26.922 11.371 8.739 0.519 0.344
32 Medium 2 25.555 10.611 8.036 0.478 0.324
32 Large 1 26.656 11.347 8.826 0.523 0.348
1024 Small 2 66.919 27.450 20.099 1.156 0.762
1024 Small 3 47.080 19.071 14.227 0.856 0.565
1024 Small 4 44.208 17.607 13.242 0.819 0.545
1024 Medium 2 29.103 12.533 9.802 0.622 0.401
1024 Medium 3 25.528 10.727 8.319 0.533 0.349
1024 Medium 4 24.675 10.095 7.750 0.508 0.335
1024 Large 2 28.140 11.917 9.610 0.616 0.395
1024 Large 3 25.017 10.528 8.305 0.538 0.349

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 80.393 35.824 27.201 2.147 1.616
Small 4 54.902 23.813 17.947 1.435 1.060
Small 5 55.422 24.012 18.029 1.340 0.958
Medium 3 24.908 10.580 8.370 0.587 0.370
Medium 4 20.686 8.827 6.970 0.489 0.309
Medium 5 20.658 8.530 6.629 0.456 0.296
Large 3 23.004 9.821 8.112 0.585 0.359
Large 4 20.489 8.745 7.156 0.512 0.317
Large 5 18.849 7.859 6.313 0.446 0.284

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 74.870 33.265 25.455 2.032 1.538
Small 4 48.805 21.394 16.274 1.320 0.986
Small 5 50.834 21.780 16.450 1.200 0.882
Medium 3 18.595 8.024 6.428 0.457 0.287
Medium 4 14.924 6.443 5.136 0.359 0.230
Medium 5 15.177 6.264 4.924 0.337 0.218
Large 3 16.656 7.329 6.147 0.448 0.276
Large 4 14.372 6.370 5.315 0.379 0.238
Large 5 13.174 5.622 4.595 0.327 0.207

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 80.957 35.826 27.411 2.151 1.617
Small 4 54.923 23.899 17.920 1.439 1.063
Small 5 56.165 24.126 18.038 1.325 0.957
Medium 3 24.448 10.613 8.393 0.586 0.369
Medium 4 20.627 8.877 6.994 0.491 0.309
Medium 5 20.730 8.505 6.630 0.461 0.296
Large 3 22.912 9.917 8.140 0.590 0.359
Large 4 20.566 8.787 7.170 0.507 0.317
Large 5 18.477 7.898 6.322 0.447 0.284

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 74.348 36.405 25.561 2.020 1.537
Small 4 48.724 21.518 16.298 1.310 0.985
Small 5 50.442 21.850 16.424 1.193 0.882
Medium 3 18.668 8.071 6.430 0.458 0.287
Medium 4 14.947 6.448 5.137 0.360 0.230
Medium 5 15.140 6.317 4.926 0.336 0.218
Large 3 16.469 7.312 6.156 0.459 0.276
Large 4 14.581 6.328 5.327 0.386 0.238
Large 5 13.376 5.657 4.595 0.328 0.207

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)}")