Extensions

Concrete supports native Python and NumPy operations as much as possible, but not everything in Python or NumPy is available. Therefore, we provide some extensions ourselves to improve your experience.

fhe.univariate(function)

Allows you to wrap any univariate function into a single table lookup:

import numpy as np
from concrete import fhe

def complex_univariate_function(x):

    def per_element(element):
        result = 0
        for i in range(element):
            result += i
        return result

    return np.vectorize(per_element)(x)

@fhe.compiler({"x": "encrypted"})
def f(x):
    return fhe.univariate(complex_univariate_function)(x)

inputset = [np.random.randint(0, 5, size=(3, 2)) for _ in range(10)]
circuit = f.compile(inputset)

sample = np.array([
    [0, 4],
    [2, 1],
    [3, 0],
])
assert np.array_equal(circuit.encrypt_run_decrypt(sample), complex_univariate_function(sample))
triangle-exclamation

fhe.multivariate(function)

Allows you to wrap any multivariate function into a table lookup:

triangle-exclamation
circle-exclamation

fhe.conv(...)

Allows you to perform a convolution operation, with the same semantic as onnx.Convarrow-up-right:

triangle-exclamation

fhe.maxpool(...)

Allows you to perform a maxpool operation, with the same semantic as onnx.MaxPoolarrow-up-right:

triangle-exclamation

fhe.array(...)

Allows you to create encrypted arrays:

triangle-exclamation

fhe.zero()

Allows you to create an encrypted scalar zero:

fhe.zeros(shape)

Allows you to create an encrypted tensor of zeros:

fhe.one()

Allows you to create an encrypted scalar one:

fhe.ones(shape)

Allows you to create an encrypted tensor of ones:

fhe.hint(value, **kwargs)

Allows you to hint properties of a value. Imagine you have this circuit:

You'd expect all of a, b, and c to be 8-bits, but because inputset is very small, this code could print:

The first solution in these cases should be to use a bigger inputset, but it can still be tricky to solve with the inputset. That's where the hint extension comes into play. Hints are a way to provide extra information to compilation process:

  • Bit-width hints are for constraining the minimum number of bits in the encoded value. If you hint a value to be 8-bits, it means it should be at least uint8 or int8.

To fix f using hints, you can do:

circle-exclamation

you'll always see:

regardless of the bounds.

Alternatively, you can use it to make sure a value can store certain integers:

fhe.relu(value)

Allows you to perform ReLU operation, with the same semantic as x if x >= 0 else 0:

ReLU extension can be converted in two different ways:

  • With a single TLU on the original bit-width.

  • With multiple TLUs on smaller bit-widths.

For small bit-widths, the first one is better as it'll have a single TLU on a small bit-width. For big bit-widths, the second one is better as it won't have a TLU on a big bit-width.

The decision between the two can be controlled with relu_on_bits_threshold: int = 7 configuration option:

  • relu_on_bits_threshold=5 means:

    • 1-bit to 4-bits would be converted using the first way (i.e., using TLU)

    • 5-bits and more would be converted using the second way (i.e., using bits)

There is another option to customize the implementation relu_on_bits_chunk_size: int = 2:

  • relu_on_bits_chunk_size=4 means:

    • When using the second implementation:

      • The input would be split to 4-bit chunks using fhe.bits, and then the ReLU would be applied to those chunks, which are then combined back.

Here is a script showing how execution cost is impacted when changing these values:

circle-info

You might need to run the script twice to avoid crashing when plotting.

The script will show the following figure:

circle-info

The default values of these options are set based on simple circuits. How they affect performance will depend on the circuit, so play around with them to get the most out of this extension.

circle-exclamation

fhe.if_then_else(condition, x, y)

Allows you to perform ternary if operation, with the same semantic as x if condition else y:

circle-info

fhe.if_then_else is just an alias for np.wherearrow-up-right.

fhe.identity(value)

Allows you to copy the value:

circle-info

Identity extension can be used to clone an input while changing its bit-width. Imagine you have return x**2, x+100 where x is 2-bits. Because of x+100, x will be assigned 7-bits and x**2 would be more expensive than it needs to be. If return x**2, fhe.identity(x)+100 is used instead, x will be assigned 2-bits as it should and fhe.identity(x) will be assigned 7-bits as necessary.

circle-exclamation

fhe.inputset(...)

Used for creating a random inputset with the given specifications:

The result will have 100 inputs by default which can be customized using the size keyword argument:

Last updated

Was this helpful?