Multidimensional fhe.LookupTable()?

Is it possible to have an fhe.LookupTable() with two dimensions? I’m trying and can’t seem to get the right syntax.

table = fhe.LookupTable([[2, -1, 3, 0],[1,2,3,4]])

@fhe.compiler({"x": "encrypted", "y":"encrypted"})
def f(x):
    return table [ x ][ y ]

My objective is to pre-calculate a table of values to accelerate some operations that seem to take a long time under FHE. For example, additions happen very quickly, but my //2 or floor_divide(x,2), or np.right_shift(x,1) all take a really long time. But given my finite inputset, I can precalculate these to obtain a faster result.

I’m thinking I’m missing something very obvious here syntax-wise. But I am getting this error message:
ValueError: LookupTable cannot be constructed with [[2, -1, 3, 0], [1, 2, 3, 4]]

Any suggestions?
Ron.

Hey @ronhume,

That kind of table lookup isn’t supported at the moment. We’ll add it to the list of things to do and we might support it in the upcoming releases!

In the meantime, if you know the bit-widths of x and y, you can flatten the table into a linear one and pack x and y into a single integer and perform the table lookup there!

See Key Value Database Tutorial, search for packed :slight_smile:

Let us know if you have any other question!

Ok thank you - I will look into this as you suggest.

Its just 2 uint8’s. My challenge is that I’m simulating encrypted sample A from system A under client key X and encrypted sample B from system B also under client key X, and trying to calculate the output of a complex operation between them under FHE. The current runtime of the circuit is 7 hours for my 80k samples. But the input space is so small that I could precalculate all the outputs in a table. But I cannot figure out how to concatenate the values under FHE - as doing so ahead of time, in the clear would defeat the purpose of the experiment (ie. operate on two encrypted inputs). Does that make sense?

In any case - thank you for your time and guidance. I will go read the section as you recommend.

Here is an example of doing it for two uint2s:

import numpy as np
from concrete import fhe

configuration = fhe.Configuration(
    enable_unsafe_features=True,
    use_insecure_key_cache=True,
    insecure_key_cache_location=".keys",
)

multi_dimensional_table = [
    [0, 1, 2, 3],
    [3, 2, 1, 0],
    [2, 0, 3, 1],
    [1, 3, 2, 0],
]
flat_table = fhe.LookupTable(np.array(multi_dimensional_table).flatten())

@fhe.compiler({"x": "encrypted", "y": "encrypted"})
def f(x, y):
    packed_x_and_y = (x * (2**2)) + y
    return flat_table[packed_x_and_y]

inputset = [
    (
        np.random.randint(0, 2**2, size=()),
        np.random.randint(0, 2**2, size=()),
    )
    for _ in range(100)
]
circuit = f.compile(inputset, configuration, verbose=True)

for x in range(2**2):
    for y in range(2**2):
        assert circuit.encrypt_run_decrypt(x, y) == multi_dimensional_table[x][y]

If you have uint8s, make sure the table is 256 x 256, and multiply x with 2**8 and you should be good to go :slight_smile:

This is awesome. Thank you. Exactly what I was looking for.

Ron.

1 Like

Happy to help :wink:

Let us know if you have more questions!