Skip to main content

Bounded integer helpers for compiling common Concrete FHE circuits

Project description

concrete-fhe-toolkit

concrete-fhe-toolkit is an unofficial helper package for Zama Concrete. It provides reusable circuit builders for common bounded integer operations on encrypted Concrete inputs.

The package focuses on small, explicit, integer-only FHE circuits:

  • compare two encrypted integers
  • compare-swap two encrypted integers
  • sort encrypted integer arrays
  • find encrypted-array min/max values
  • find encrypted-array argmin/argmax indices
  • perform bounded encrypted floor division with a table lookup
  • perform bounded numerator // (left * right)

Installation

pip install concrete-fhe-toolkit

To install the latest source directly from GitHub:

pip install git+https://github.com/ErkanIsikB/concrete-fhe-toolkit.git

Or clone the repository and install it locally:

git clone https://github.com/ErkanIsikB/concrete-fhe-toolkit.git
cd concrete-fhe-toolkit
pip install .

Concrete 2.11 supports Python 3.9 through 3.12 on Linux and macOS.

Important Concept

The compile_* helpers compile Concrete circuits whose inputs are declared as encrypted. For example, compile_sort(...) internally compiles with {"x": "encrypted"}.

The make_* helpers return traceable Python functions that can be composed into larger Concrete programs. They can also run on clear Python or NumPy values for ordinary testing, but they are designed to be compiled into encrypted-input Concrete circuits.

All operations use integer inputs. You must choose fixed input bounds when compiling a circuit, and runtime inputs must stay inside those bounds.

The bounds are not the hidden minimum and maximum of a particular encrypted array. They are public, application-level limits that you know before compilation. For example, if your encrypted scores are always percentages, use min_value=0 and max_value=100. If your private balances are stored in a range from -1_000 to 1_000, use those as the bounds. Wider bounds are more flexible, but they usually make the compiled circuit more expensive.

Quick Start

import numpy as np

from concrete_fhe_toolkit import compile_argmin, compile_sort

values = np.array([12, 3, 7, 1, 15, 0, 4, 9], dtype=np.int64)

sort_circuit = compile_sort(size=8, min_value=0, max_value=100)
print(sort_circuit.encrypt_run_decrypt(values))
# [ 0  1  3  4  7  9 12 15]

argmin_circuit = compile_argmin(size=8, min_value=0, max_value=100)
print(argmin_circuit.encrypt_run_decrypt(values))
# 5

Public API

Scalar arithmetic:

  • sign(x, y)
  • compile_sign(min_value=-15, max_value=15, configuration=None)
  • make_floor_divide(zero_result=0)
  • compile_floor_divide(max_numerator, max_denominator, zero_result=0, configuration=None)
  • make_floor_divide_by_product(zero_result=0)
  • compile_floor_divide_by_product(max_numerator, max_left, max_right, zero_result=0, configuration=None)

Array operations:

  • make_compare_swap(min_value=0, max_value=15)
  • compile_compare_swap(min_value=0, max_value=15, configuration=None)
  • make_sort(size, min_value=0, max_value=15, descending=False)
  • compile_sort(size, min_value=0, max_value=15, descending=False, configuration=None)
  • make_minimum(size, min_value=0, max_value=15)
  • compile_minimum(size, min_value=0, max_value=15, configuration=None)
  • make_maximum(size, min_value=0, max_value=15)
  • compile_maximum(size, min_value=0, max_value=15, configuration=None)
  • make_argmin(size, min_value=0, max_value=15, tie_break="first")
  • compile_argmin(size, min_value=0, max_value=15, tie_break="first", configuration=None)
  • make_argmax(size, min_value=0, max_value=15, tie_break="first")
  • compile_argmax(size, min_value=0, max_value=15, tie_break="first", configuration=None)

Examples

Sign Comparison

compile_sign returns 1 when x > y, 0 when x == y, and -1 when x < y.

from concrete_fhe_toolkit import compile_sign

circuit = compile_sign(min_value=-20, max_value=20)

print(circuit.encrypt_run_decrypt(15, 3))
# 1

print(circuit.encrypt_run_decrypt(3, 15))
# -1

print(circuit.encrypt_run_decrypt(7, 7))
# 0

Compare-Swap

compile_compare_swap returns two values in ascending order.

from concrete_fhe_toolkit import compile_compare_swap

circuit = compile_compare_swap(min_value=0, max_value=100)

print(circuit.encrypt_run_decrypt(12, 3))
# (3, 12)

Sort

compile_sort sorts a fixed-size encrypted array. The size must be a power of two.

import numpy as np

from concrete_fhe_toolkit import compile_sort

values = np.array([12, 3, 7, 1, 15, 0, 4, 9], dtype=np.int64)

ascending = compile_sort(size=8, min_value=0, max_value=100)
print(ascending.encrypt_run_decrypt(values))
# [ 0  1  3  4  7  9 12 15]

descending = compile_sort(size=8, min_value=0, max_value=100, descending=True)
print(descending.encrypt_run_decrypt(values))
# [15 12  9  7  4  3  1  0]

Minimum and Maximum

compile_minimum and compile_maximum return the smallest or largest value in a fixed-size encrypted array.

import numpy as np

from concrete_fhe_toolkit import compile_maximum, compile_minimum

values = np.array([12, 5, 7, 2, 15, 9, 4, 14], dtype=np.int64)

minimum = compile_minimum(size=8, min_value=0, max_value=100)
print(minimum.encrypt_run_decrypt(values))
# 2

maximum = compile_maximum(size=8, min_value=0, max_value=100)
print(maximum.encrypt_run_decrypt(values))
# 15

Argmin and Argmax

compile_argmin and compile_argmax return the index of the smallest or largest value. By default, ties return the first matching index.

import numpy as np

from concrete_fhe_toolkit import compile_argmax, compile_argmin

values = np.array([12, 5, 7, 1, 15, 9, 4, 14], dtype=np.int64)

argmin = compile_argmin(size=8, min_value=0, max_value=100)
print(argmin.encrypt_run_decrypt(values))
# 3

argmax = compile_argmax(size=8, min_value=0, max_value=100)
print(argmax.encrypt_run_decrypt(values))
# 4

Tie handling is explicit:

import numpy as np

from concrete_fhe_toolkit import compile_argmin

values = np.array([4, 1, 1, 3], dtype=np.int64)

first = compile_argmin(size=4, min_value=0, max_value=10, tie_break="first")
print(first.encrypt_run_decrypt(values))
# 1

last = compile_argmin(size=4, min_value=0, max_value=10, tie_break="last")
print(last.encrypt_run_decrypt(values))
# 2

Floor Division

Direct x // y does not compile when both values are encrypted in Concrete 2.11. This package implements bounded floor division with fhe.multivariate.

from concrete_fhe_toolkit import compile_floor_divide

circuit = compile_floor_divide(
    max_numerator=100,
    max_denominator=10,
    zero_result=-1,
)

print(circuit.encrypt_run_decrypt(15, 3))
# 5

print(circuit.encrypt_run_decrypt(15, 0))
# -1

zero_result is returned when the encrypted denominator is zero.

Division by an Encrypted Product

compile_floor_divide_by_product computes numerator // (left * right).

from concrete_fhe_toolkit import compile_floor_divide_by_product

circuit = compile_floor_divide_by_product(
    max_numerator=100,
    max_left=10,
    max_right=10,
    zero_result=-1,
)

print(circuit.encrypt_run_decrypt(20, 2, 5))
# 2

print(circuit.encrypt_run_decrypt(20, 0, 5))
# -1

Composing make_* Helpers

Use make_* helpers when you want to build a larger Concrete function yourself. When compiling manually, your inputset must cover the bounds you declared.

import numpy as np
from concrete import fhe

from concrete_fhe_toolkit import make_minimum

minimum_of_four = make_minimum(size=4, min_value=0, max_value=100)
compiler = fhe.Compiler(minimum_of_four, {"x": "encrypted"})

inputset = [
    np.array([0, 0, 0, 0], dtype=np.int64),
    np.array([100, 100, 100, 100], dtype=np.int64),
    np.array([0, 100, 0, 100], dtype=np.int64),
    np.array([100, 0, 100, 0], dtype=np.int64),
]

circuit = compiler.compile(inputset)
print(circuit.encrypt_run_decrypt(np.array([8, 3, 12, 5], dtype=np.int64)))
# 3

For most use cases, prefer the compile_* helpers because they generate boundary-aware inputsets automatically.

Bounds and Limitations

  • Inputs must stay inside the bounds used at compilation.
  • Sorting requires a power-of-two array size.
  • Min/max and argmin/argmax support any positive fixed size.
  • Division helpers currently support nonnegative bounded inputs.
  • Larger bounds increase table-lookup bit width and cost.
  • Concrete supports integer inputs and outputs, not arbitrary floating point.
  • FHE execution is probabilistic; choose Concrete error parameters appropriate for the application's risk.

Notebook Examples

This package was derived from these Kaggle experiments:

Additional usage notebook:

License and Concrete Terms

The package's original code is available under the MIT License. MIT permits commercial and private use, modification, and redistribution while requiring the copyright and license notice to be preserved.

Concrete is a separate dependency with its own BSD-3-Clause-Clear license and patent terms. This package does not change or grant rights under Concrete's license. Review Zama's current licensing terms before commercial use.

This project is not affiliated with or endorsed by Zama.

Author and Contributors

Author and maintainer

  • Erkan Işık Bacak

Contributors

  • Tolga Büyüktanır
  • Didem Civelek
  • Yusuf Emir Alakuş

Organizational contributor

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

concrete_fhe_toolkit-0.1.2.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

concrete_fhe_toolkit-0.1.2-py3-none-any.whl (11.3 kB view details)

Uploaded Python 3

File details

Details for the file concrete_fhe_toolkit-0.1.2.tar.gz.

File metadata

  • Download URL: concrete_fhe_toolkit-0.1.2.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for concrete_fhe_toolkit-0.1.2.tar.gz
Algorithm Hash digest
SHA256 82229fbc72815d9a31004a39527ad2376e426196cedd057fed8d10240ed6826a
MD5 533a6ca8e43018fac4d905614ef86f84
BLAKE2b-256 5b4f4a8a5e0aa89461a66c5127919768c010c8a61b92af7bbcd55e16473b2aa4

See more details on using hashes here.

File details

Details for the file concrete_fhe_toolkit-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for concrete_fhe_toolkit-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e3622f23259dce382f422a6d9262ea2377e6870ee52a7ba8483aba5361e9be1a
MD5 e8c5c98c3c28ecb99542a4926523a4ba
BLAKE2b-256 e3308e58081f8ea12125c343ccd7e50fb0f295634afdb9a7e3eb2f55ca73292c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page