Skip to content

Data Encoding

In CKKS, 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 CKKS scheme encodes multiple data values in a single ciphertext. Element-wise operations such as addition, subtraction, and multiplication each processes data in a SIMD-like manner. 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 rotation. Rotation works on unit ciphertexts instead of entire ciphertexts.

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

Rotate by 1 (local rotation)

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

Rotate by 1 (global rotation)

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

There are ways to have a global rotation, but since the slot count is usually quite large and rotation on large data is rare, the DESILO FHE library has opted for offering a local rotation.

Sparse Encoding

This local rotation does not play that well with padding, as the added zeros will start to fill the front of the data. Therefore, starting with version 1.5, sparse encoding has been introduced.

You can now set the slot_count value of the engine.

from desilofhe import Engine

engine = Engine(slot_count=32)
engine.slot_count  # 32

Note that the slot count has to be a power of 2, so that the input slot count value is always raised to the smallest power of 2 that is greater than or equal to the input slot count value.

from desilofhe import Engine

engine = Engine(slot_count=30)
engine.slot_count  # 32

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

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

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

Rotate by 1 (local rotation)

      Result: [4, 1, 2, 3]

One downside of the sparse encoding is that if two ciphertexts are encoded with different slot counts, they cannot be processed together. Roughly speaking, this is what happens.

Ciphretext with slot count 2: [1 2 1 2]
Ciphertext with slot count 4: [1 2 3 4]
              Sum of the two: [2 4 4 6]
          Product of the two: [1 4 3 8]

So if you try to add the two, the results in the part after the smaller slot count would be strange. Therefore, the DESILO FHE library has decided to not support operations on ciphertexts with different slot count values.