Home

Awesome

Matrix inversion for TFHE ZAMA concrete

Installation

poetry install

Run

poetry shell
python matrix_inversion/main.py

Dev

Running tests

poetry shell
python tests/test_qfloat.py
python tests/test_qfloat_fhe.py
python matrix_inversion/qfloat_matrix_inversion.py

Contributing

see TODO in qfloat.py and qfloat_matrix_inversion.py and base_p_arrays.py

About

<div style="text-align: justify">

QFloats to quantize floats in FHE

<center>

Internal representation of a QFloat

</center>

QFloat internal representation

This representation allows to make any operations on the QFloats in FHE, but has a limited range of values. The usual unencrypted float representation that uses an exponent (like 3.442 e-1), cannot be made in FHE to run operations (mostly additions) without disproportionally huge computations.

Why using QFloats is required for matrix inversion

Why the inversion from LU decomposition was chosen

How to set QFloats for the algorithm

Further notice

Benchmarking

The inversion algorithm has been benchmarked in both precision and speed.

Precision benchmarking

FHE computing is so slow that it makes the algorithm impossible to run on large matrices or with good precision parameters. However, there has been no evidence that the results are any different in FHE than in pure python for the small matrices and low precision, so we can expect that a precision benchmarking done with python is representative of the behavior in FHE.

This benchmarking consists in running 10.000 matrix inversions ( normal(0,100) matrix sampler), and calculate the error in comparison to the standard scipy.linalg matrix inversion algorithm. For each matrix size, we computed the mean error over the 10.000 runs, and we also counted the percentage of results with a huge error (bigger than 1) that we can consider totally erroneous (see big error rate).

<center>

Table 1: precision benchmarking

Error \ PrecisionLowMediumMedium+High
n=2 : mean error8.19 e-27.6 e-37.64 e-45.03 e-4
n=2 : big error rate0.04 %0.02 %0.01 %0.0 %
n=3 : mean error9.93 e-24.43 e-22.6 e-37.8 e-6
n=3 : big error rate1.34 %0.4 %0.03 %0.0 %
n=4 : mean error2.04 e-12.34 e-22.778 e-18.6 e-6
n=4 : big error rate3.08 %0.24 %0.11 %0.0 %
n=5 : mean error3.14 e-15.59 e-29.37 e-21.40 e-6
n=5 : big error rate6.36 %0.23 %0.23 %0.0 %
n=10 : mean error1.29 e-06.87 e-12.57 e-01.2 e-4
n=10 : big error rate31.3 %0.44 %0.75 %0.0 %
</center>

The values of the precision parameters are listed below :

<center>

Table 2: precision parameters

Precision \ ValuesArray lengthInteger digitsTrue division
Low239False
Medium3116False
Medium+3116True
High4020True
</center>

After investigation of the algorithm (see function debug_qfloat_inverse_python), we noticed that the errors came from two different factors:

Note that for n=2 the algorithm is different than for other values of n, and that this results may show some statistical oddities.

Computation time benchmarking

To benchmark the FHE computation time (see function time_benchmark in qfloat_matrix_inversion.py), we ran 3 compilation / encryption / run in low precision on a 64 cores computing instance, we kept the times and computed the average. We repeated this for n=2, n=3 and also for parameter tensorize set to True and False (True adds a bit more tensorization in the inverse algorithm than there is when set to False). We could not test higher sizes of matrices or higher precision due to current computational time limitations in FHE.
Note: The benchmarking was done on concrete-numpy 2.1.0 because the 2.2.0and 2.3.0 did not work for compiling with n>=3.

<center>

Table 3: time benchmarking

Computation \ n2233
TensorizeYesNoYesNo
Compilation414242555487
FHE Run (1 run)858517681349
Total (with encryption, 1 run)12612760246837
Total estimated (3 runs)29629795629537
</center>

We see that the algorithm used when n=2 does not seem to change speed with or without tensorization. However, the algorithm for n>=3 took more time to compile without tensorization, but more time to run with tensorization. Overall, for one run (with encryption), setting tensorize=True was faster, but we can expect that for 3 runs or more, setting tensorize=False would be faster.
Given this result, the tensorize parameter was given a False default value.

</div>