Concrete
WebsiteLibrariesProducts & ServicesDevelopersSupport
2.6
2.6
  • Welcome
  • Get Started
    • What is Concrete?
    • Installation
    • Quick start
    • Compatibility
    • Terminology
  • Core features
    • Overview
    • Table lookups
    • Bit extraction
    • Rounding
    • Truncating
    • Floating points
    • Comparisons
    • Min/Max operations
    • Bitwise operations
    • Common tips
    • Extensions
    • Tagging
  • Compilation
    • Composition
    • Compression
    • Reuse arguments
    • Multi precision
    • Multi parameters
    • Modules
    • Decorator
    • Direct circuits
  • Execution / Analysis
    • Simulation
    • Progressbar
    • Statistics
    • Formatting and drawing
    • Debug
  • Guides
    • Configure
    • Manage keys
    • Deploy
  • Tutorials
    • See all tutorials
    • Part I: Concrete - FHE compiler
    • Part II: The Architecture of Concrete
  • References
    • API
  • Explanations
    • Compiler workflow
    • Frontend fusing
    • Compiler backend
      • Adding a new backend
    • Optimizer
    • MLIR FHE dialects
      • FHELinalg dialect
      • FHE dialect
      • TFHE dialect
      • Concrete dialect
      • Tracing dialect
      • Runtime dialect
      • SDFG dialect
    • Security
    • Call FHE circuits from other languages
    • Project layout
  • Developers
    • Contributing
    • Release note
    • Feature request
    • Bug report
Powered by GitBook

Libraries

  • TFHE-rs
  • Concrete
  • Concrete ML
  • fhEVM

Developers

  • Blog
  • Documentation
  • Github
  • FHE resources

Company

  • About
  • Introduction to FHE
  • Media
  • Careers
On this page

Was this helpful?

Export as PDF
  1. Explanations

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()))
PreviousSecurityNextProject layout

Last updated 1 year ago

Was this helpful?

There is also a couple of tests in that can show how to both compile and run a circuit between a client and server using serialization.

test_compilation.py