Skip to content

Data Encoding

In CKKS and GL, a ciphertext needs to contain at least a certain number of values to be safe. This number increases according to the multiplication count, and is usually rather large. In order to leverage this large number of values, the multiple data values are encoded in a single ciphertext. Element-wise operations such as addition, subtraction, and multiplication each processes data in a SIMD-like manner.

Vector (CKKS)

In CKKS, the maximum number of values that can be encoded together is called the slot count in the DESILO FHE library.

You can simply check the slot_count value of the engine.

from desilofhe import Engine

engine = Engine()
engine.slot_count  # 8192

The above code shows that the default engine encodes 8192 values at once. The CKKS scheme works best when the input data size is exactly the slot count value, but the DESILO FHE library supports different sizes as well.

When the Input Data is Shorter

When the input data is shorter than the slot count value, the input data will be padded with zeros to match the slot count.

If the slot count is 4, and the input data size is 2, this is what happens.

  Input Data: [1, 2]

Encoded data: [1, 2, 0, 0]

When the Input Data is Longer

When the input data is longer, the DESILO FHE library splits the data in chunks. Each chunk will have at most slot count values, and will be encoded and encrypted separatedly, then put into a list and returned as a single ciphertext.

If the slot count is 4, and the input data size is 10, this is what happens.

Input Data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Ciphertext: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 0]]

Note that the last chunk also has been padded with zeros.

The DESILO FHE library calls each chunk a unit ciphertext. All Operations on ciphertexts comprised of multiple ciphertext (as in the example) work exactly the same, except for roll and rotation. Roll and rotation works on unit ciphertexts instead of entire ciphertexts.

Ciphertext: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 0, 0]]

Roll by 1 to the right (local roll)

    Result: [[4, 1, 2, 3], [8, 5, 6, 7], [0, 9, 10, 0]]

Roll by 1 to the right (global roll)

    Result: [[10, 1, 2, 3], [4, 5, 6, 7], [8, 9, 0, 0]]

There are ways to have a global roll, but since the slot count is rather large and roll or rotation on long vectors is rare, the DESILO FHE library has opted for offering a local rotation.

Matrix (GL)

In GL, the dimension counts of values that can be encoded together is called the shape in the DESILO FHE library.

You can simply check the shape value of the engine.

from desilofhe import Engine

engine = Engine(form="matrix")
engine.shape  # (256, 16, 16)

The above code shows that the default matrix engine encodes 256 * 16 * 16 values at once.

The outermost dimension is called the batch count. The batch count is 256 in the default matrix engine.

Matrix multiplication multiplies each of the inner matrices. The inner matrix is of size 16 * 16 in the default matrix engine.

When the Input Batch Count is Shorter

When the input data is shorter than the slot count value, the input data will be padded with zeros to match the batch count. The GL scheme requires that the input shape to be at least a multiple of the inner matrix.

If the shape is (2, 2, 2), and the input shpae is (1, 2, 2), this is what happens.

  Input Data: [[[1, 2],
                [3, 4]]]

Encoded data: [[[1, 2],
                [3, 4]],
               [[0, 0],
                [0, 0]]]

Note that the last inner matrix has been filled with zeros.

When the Input Batch Count is Longer

This is not yet supported.