Hello @Laser_beam,
Let’s go over them one by one!
programmable_bootstrap_count_per_parameter
Imagine having a circuit with a 4-bit table lookup and a 2-bit table lookup. These table lookups will use different bootstrapping parameters by default to be as fast as possible. programmable_bootstrap_count_per_parameter
statistic count the number of table lookups per bootstrapping parameter in the circuit:
import numpy as np
from concrete import fhe
inputset = [
(
np.random.randint(0, 2**4, size=()), # x is 4 bits
np.random.randint(0, 2**2, size=()), # y is 2 bits
)
for _ in range(100)
]
def f(x, y):
a = x ** 2 # 4-bit tlu
b = y ** 2 # 2-bit tlu
c = y // 2 # 2-bit tlu
return a + b + c
compiler = fhe.Compiler(f, {"x": "encrypted", "y": "encrypted"})
circuit = compiler.compile(inputset)
print(circuit.programmable_bootstrap_count_per_parameter)
prints
{
BootstrapKeyParam(polynomial_size=1024, glwe_dimension=2, input_lwe_dimension=813, level=1, base_log=23, variance=9.940977002694397e-32): 1,
BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=671, level=1, base_log=23, variance=9.940977002694397e-32): 2
}
which means there will be:
- 1 TLU which use
BootstrapKeyParam(polynomial_size=1024, glwe_dimension=2, input_lwe_dimension=813, level=1, base_log=23, variance=9.940977002694397e-32)
- 2 TLUs which use
BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=671, level=1, base_log=23, variance=9.940977002694397e-32)
programmable_bootstrap_count_per_tag
This statistic requires usage of tags (see Tagging). It’ll count the number of TLUs per tag:
import numpy as np
from concrete import fhe
inputset = [
(
np.random.randint(0, 2**4, size=()), # x is 4 bits
np.random.randint(0, 2**2, size=()), # y is 2 bits
)
for _ in range(100)
]
def f(x, y):
with fhe.tag("processing_x"):
a = x ** 2 # 4-bit tlu
with fhe.tag("processing_y"):
b = y ** 2 # 2-bit tlu
c = y // 2 # 2-bit tlu
return a + b + c
compiler = fhe.Compiler(f, {"x": "encrypted", "y": "encrypted"})
circuit = compiler.compile(inputset)
print(circuit.programmable_bootstrap_count_per_tag)
prints
{'processing_x': 1, 'processing_y': 2}
which means there will be:
- 1 TLU in
processing_x
region of the code
- 2 TLUs in
processing_y
region of the code
programmable_bootstrap_count_per_tag_per_parameter
And this one will first group by tag and then by parameter:
import numpy as np
from concrete import fhe
inputset = [
(
np.random.randint(0, 2**4, size=()), # x is 4 bits
np.random.randint(0, 2**2, size=()), # y is 2 bits
)
for _ in range(100)
]
def f(x, y):
with fhe.tag("squaring"):
a = x ** 2 # 4-bit tlu
b = y ** 2 # 2-bit tlu
with fhe.tag("dividing"):
c = y // 2 # 2-bit tlu
return a + b + c
compiler = fhe.Compiler(f, {"x": "encrypted", "y": "encrypted"})
circuit = compiler.compile(inputset)
print(circuit.programmable_bootstrap_count_per_tag_per_parameter)
prints
{
'squaring': {
BootstrapKeyParam(polynomial_size=1024, glwe_dimension=2, input_lwe_dimension=813, level=1, base_log=23, variance=9.940977002694397e-32): 1,
BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=671, level=1, base_log=23, variance=9.940977002694397e-32): 1
},
'dividing': {
BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=671, level=1, base_log=23, variance=9.940977002694397e-32): 1
}
}
which means there will be:
- 1 TLU in
squaring
region of the code which use BootstrapKeyParam(polynomial_size=1024, glwe_dimension=2, input_lwe_dimension=813, level=1, base_log=23, variance=9.940977002694397e-32)
- 1 TLU in
squaring
region of the code which use BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=671, level=1, base_log=23, variance=9.940977002694397e-32)
- 1 TLU in
dividing
region of the code which use BootstrapKeyParam(polynomial_size=512, glwe_dimension=4, input_lwe_dimension=671, level=1, base_log=23, variance=9.940977002694397e-32)
Let us know if you have more questions!