Call FHE circuits from other languages
After doing a compilation, we end up with a couple of artifacts, including crypto parameters and a binary file containing the executable circuit. In order to be able to encrypt and run the circuit properly, we need to know how to interpret these artifacts, and there are a couple of utility functions which can be used to load them. These utility functions can be accessed through a variety of languages, including Python and C++.
Demo
We will use a really simple example for a demo, but the same steps can be done for any other circuit. example.mlir
will contain the MLIR below:
func.func @main(%arg0: tensor<4x4x!FHE.eint<6>>, %arg1: tensor<4x2xi7>) -> tensor<4x2x!FHE.eint<6>> {
%0 = "FHELinalg.matmul_eint_int"(%arg0, %arg1): (tensor<4x4x!FHE.eint<6>>, tensor<4x2xi7>) -> (tensor<4x2x!FHE.eint<6>>)
%tlu = arith.constant dense<[40, 13, 20, 62, 47, 41, 46, 30, 59, 58, 17, 4, 34, 44, 49, 5, 10, 63, 18, 21, 33, 45, 7, 14, 24, 53, 56, 3, 22, 29, 1, 39, 48, 32, 38, 28, 15, 12, 52, 35, 42, 11, 6, 43, 0, 16, 27, 9, 31, 51, 36, 37, 55, 57, 54, 2, 8, 25, 50, 23, 61, 60, 26, 19]> : tensor<64xi64>
%result = "FHELinalg.apply_lookup_table"(%0, %tlu): (tensor<4x2x!FHE.eint<6>>, tensor<64xi64>) -> (tensor<4x2x!FHE.eint<6>>)
return %result: tensor<4x2x!FHE.eint<6>>
}
You can use the concretecompiler
binary to compile this MLIR program. Same can be done with concrete-python
, as we only need the compilation artifacts at the end.
$ concretecompiler --action=compile -o python-demo example.mlir
You should be able to see artifacts listed in the python-demo
directory
$ ls python-demo/
client_parameters.concrete.params.json compilation_feedback.json fhecircuit-client.h sharedlib.so staticlib.a
Now we want to use the Python bindings in order to call the compiled circuit.
from concrete.compiler import (ClientSupport, LambdaArgument, LibrarySupport)
The main struct
to manage compilation artifacts is LibrarySupport
. You will have to create one with the path you used during compilation, then load the result of the compilation
lib_support = LibrarySupport.new("/path/to/your/python-demo/")
compilation_result = lib_support.reload()
Using the compilation result, you can load the server lambda (the entrypoint to the executable compiled circuit) as well as the client parameters (containing crypto parameters)
server_lambda = lib_support.load_server_lambda(compilation_result)
client_params = lib_support.load_client_parameters(compilation_result)
The client parameters will serve the client to generate keys and encrypt arguments for the circuit
client_support = ClientSupport.new()
key_set = client_support.key_set(client_params)
args = [
LambdaArgument.from_tensor_u8([1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], [4, 4]),
LambdaArgument.from_tensor_u8([1, 2, 1, 2, 1, 2, 1, 2], [4, 2])
]
encrypted_args = client_support.encrypt_arguments(client_params, key_set, args)
Only evaluation keys are required for the execution of the circuit. You can execute the circuit on the encrypted arguments via server_lambda_call
eval_keys = key_set.get_evaluation_keys()
encrypted_result = lib_support.server_call(server_lambda, encrypted_args, eval_keys)
At this point you have the encrypted result and can decrypt it using the keyset which holds the secret key
result_arg = client_support.decrypt_result(client_params, key_set, encrypted_result)
print("result tensor dims: {}".format(result_arg.n_values()))
print("result tensor data: {}".format(result_arg.get_values()))
There is also a couple of tests in test_compilation.py that can show how to both compile and run a circuit between a client and server using serialization.
Last updated
Was this helpful?