I have to perform a matrix multiplication between two 8-bit matrices and a subsequent modular reduction. In Concrete, this operation is limited by the maximal usable bitwidth of 8, respectively, 16 bits (when using RNS under the hood).
If I understand correctly, TFHE-rs supports much larger bitwidths. Is it possible to outsource this operation to TFHE-rs using the new "TFHE-rs Interoperability“ feature? Unfortunately, I’m not familiar with TFHE-rs and Rust… I found an example [1] that does it the other way around - outsourcing an operation from TFHE-rs to Concrete. Can someone point me to an example / test case for my scenario? I think that could really help me in figuring out how to implement my operation.
The example you pointed out should be doing what you need. The example does import a ciphertext from TFHE-rs at first, but the result is also exported back to TFHE-rs at the end. For your usecase, if you are starting in Concrete, you only need to convert your result Concrete ciphertext to TFHE-rs. In code, your computation should look similar to this:
As @ayoub said you can use tfhe-rs but the matrix multiplication will be much more slower than using concrete as TFHE-rs use radix encoding and involve several bootsrap for every multiplication/addition. One of the strength of concrete is to use cheap operation for leveled operation. But as you mention is limited to ~8bits for native integer encoding and 16bits for CRT encoding for every (temporary) result, but you can use bits extraction and/or rounding to reduce the bitwitdh of encrypted integer. Below a toy example that show a matmul with a result above 16bits with 4 msb bits extraction.
import concrete.fhe as fhe
from concrete.fhe import tfhers
import numpy as np
matrix_cst = list(np.random.randint(-128, 128, size=(3, 30)))
@fhe.compiler({"x": "encrypted"})
def matmul(x):
result = x @ matrix_cst
return fhe.bits(result)[0:4]
inputset = [
list(np.random.randint(-128, 128, size=(30, 3))) for i in range(0, 10)
]
circuit = matmul.compile(inputset, verbose=True)
print(
circuit.encrypt_run_decrypt(list(np.random.randint(-128, 128, size=(30, 3))))
)
where you can see in the compilation trace that the 17 bits matmul is reduced to 4 bits by bits extraction
@ayoub thanks for the example. I’ll try to implement my operation like that. Since I’ve never used TFHE-rs before, I might come back with a question.
@yundsi thanks for the detailed explanation. I’m a bit confused and probably I misunderstand something. Wouldn’t your code extract the 4LSB bits? If I understand correctly, we’d need to find the largest value in the matrix before we can extract the MSB bits, right? Is that efficiently possible in Concrete for values greater than 16 bits?