ML (Activations)
relu()¶
Return max(0, value).
This cell verifies the relu function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import relu
def test_relu(value):
import numpy as np
res = relu(value)
return fhe.array(res) if isinstance(res, list) else res
compiler = fhe.Compiler(test_relu, {'value': 'encrypted'})
inputset = [(3,), (-2,), (0,), (2,)]
circuit = compiler.compile(inputset)
_successes = 0
for inp in inputset:
try:
expected = relu(inp[0])
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "relu: all inputs were skipped — test is broken"
print(f"relu tests passed! ({_successes}/{len(inputset)})")
leaky_relu()¶
Return value when positive, otherwise alpha * value truncated toward zero.
This cell verifies the leaky_relu function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import leaky_relu
def test_leaky_relu(value):
import numpy as np
res = leaky_relu(value, 1)
return fhe.array(res) if isinstance(res, list) else res
compiler = fhe.Compiler(test_leaky_relu, {'value': 'encrypted'})
inputset = [(3,), (-2,), (0,), (2,)]
circuit = compiler.compile(inputset)
_successes = 0
for inp in inputset:
try:
expected = leaky_relu(inp[0], 1)
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "leaky_relu: all inputs were skipped — test is broken"
print(f"leaky_relu tests passed! ({_successes}/{len(inputset)})")
unit_step()¶
Return the Heaviside step of a number: 0 when value < 0, otherwise 1.
This cell verifies the unit_step function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import unit_step
def test_unit_step(value):
import numpy as np
res = unit_step(value)
return fhe.array(res) if isinstance(res, list) else res
compiler = fhe.Compiler(test_unit_step, {'value': 'encrypted'})
inputset = [(3,), (-2,), (0,), (2,)]
circuit = compiler.compile(inputset)
_successes = 0
for inp in inputset:
try:
expected = unit_step(inp[0])
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "unit_step: all inputs were skipped — test is broken"
print(f"unit_step tests passed! ({_successes}/{len(inputset)})")
threshold_activation()¶
Return 1 when value >= threshold, otherwise 0.
This cell verifies the threshold_activation function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import threshold_activation
def test_threshold_activation(value, threshold):
import numpy as np
res = threshold_activation(value, threshold)
return fhe.array(res) if isinstance(res, list) else res
compiler = fhe.Compiler(test_threshold_activation, {'value': 'encrypted', 'threshold': 'encrypted'})
inputset = [(3, 1), (-2, -2), (0, 0), (2, 2), (1, 3)]
circuit = compiler.compile(inputset)
_successes = 0
for inp in inputset:
try:
expected = threshold_activation(inp[0], inp[1])
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "threshold_activation: all inputs were skipped — test is broken"
print(f"threshold_activation tests passed! ({_successes}/{len(inputset)})")
make_softmax()¶
Create a scaled softmax function for a list of encrypted scores.
This cell attempts to compile the circuit and explicitly expects it to fail due to Concrete's 16-bit Table Lookup (TLU) limitation. This serves as a safety check for the 22-bit noise growth.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import make_softmax
import warnings
warnings.filterwarnings('ignore') # ignore the library warning for the test output
fn = make_softmax(min_input=-2, max_input=2)
def test_make_softmax_enc(array):
return fhe.array(fn(array))
compiler = fhe.Compiler(test_make_softmax_enc, {'array': 'encrypted'})
inputset = [([0, 1],)]
print("Attempting to compile make_softmax...")
try:
circuit = compiler.compile(inputset)
circuit.encrypt_run_decrypt(*inputset[0])
print("make_softmax_enc tests passed! (1/1)")
except Exception as e:
print("make_softmax_enc expected failure tests passed! (1/1)")
print(f"-> Caught expected Concrete 16-bit limitation error.")
compile_relu()¶
Compile encrypted ReLU.
This cell verifies the compile_relu function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import compile_relu
from concrete_fhe_toolkit.ml.activations import relu
circuit = compile_relu(min_value=-2, max_value=3)
inputset = [(3,), (-2,), (0,), (2,)]
_successes = 0
for inp in inputset:
try:
expected = relu(*inp)
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "compile_relu: all inputs were skipped — test is broken"
print(f"compile_relu tests passed! ({_successes}/{len(inputset)})")
compile_leaky_relu()¶
Compile encrypted leaky ReLU.
This cell verifies the compile_leaky_relu function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete_fhe_toolkit.ml.activations import compile_leaky_relu
circuit = compile_leaky_relu(min_value=-5, max_value=5, alpha=0.1)
inputset = [(1,)]
circuit.encrypt_run_decrypt(*inputset[0])
print("compile_leaky_relu tests passed! (1/1)")
compile_unit_step()¶
Compile encrypted unit step.
This cell verifies the compile_unit_step function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import compile_unit_step
from concrete_fhe_toolkit.ml.activations import unit_step
circuit = compile_unit_step(min_value=-2, max_value=3)
inputset = [(3,), (-2,), (0,), (2,)]
_successes = 0
for inp in inputset:
try:
expected = unit_step(*inp)
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "compile_unit_step: all inputs were skipped — test is broken"
print(f"compile_unit_step tests passed! ({_successes}/{len(inputset)})")
compile_threshold_activation()¶
Compile encrypted threshold activation over two encrypted inputs.
This cell verifies the compile_threshold_activation function mathematically against its cleartext counterpart, using a dynamically generated input set that covers positive, negative, zero, and array edge cases while respecting the [-7, 7] FHE RAM constraints.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import compile_threshold_activation
from concrete_fhe_toolkit.ml.activations import threshold_activation
circuit = compile_threshold_activation(min_value=-2, max_value=3)
inputset = [(3, 1), (-2, -2), (0, 0), (2, 2), (1, 3)]
_successes = 0
for inp in inputset:
try:
expected = threshold_activation(*inp)
import numpy as np
if isinstance(expected, (list, tuple)) or type(expected).__name__ == 'ndarray':
np.testing.assert_array_equal(circuit.encrypt_run_decrypt(*inp), expected)
else:
assert int(circuit.encrypt_run_decrypt(*inp)) == int(expected), f"Failed at {inp}"
except AssertionError:
raise
except Exception as e:
print(f"Skipping {inp} due to bounds or other error: {e}")
else:
_successes += 1
assert _successes > 0, "compile_threshold_activation: all inputs were skipped — test is broken"
print(f"compile_threshold_activation tests passed! ({_successes}/{len(inputset)})")
compile_softmax()¶
Compile an FHE circuit for the softmax function over an array of fixed size.
This cell attempts to compile the circuit and explicitly expects it to fail due to Concrete's 16-bit Table Lookup (TLU) limitation. This serves as a safety check for the 22-bit noise growth.
from concrete import fhe
from concrete_fhe_toolkit.ml.activations import compile_softmax
import warnings
warnings.filterwarnings('ignore') # ignore the library warning for the test output
print("Attempting to compile compile_softmax...")
try:
circuit = compile_softmax(size=2, min_value=-2, max_value=2)
print("compile_softmax tests passed! (1/1)")
except Exception as e:
print("compile_softmax expected failure tests passed! (1/1)")
print(f"-> Caught expected Concrete 16-bit limitation error.")
client_softmax()¶
Because of the 16-bit hardware limitation in FHE circuits, computing Softmax on encrypted variables directly leads to precision loss or compiler errors (MLIR tensor.collapse_shape bugs) due to encrypted division constraints.
The standard practice in FHE Machine Learning is to:
- Run the encrypted math inside the circuit and return Raw Encrypted Scores (Logits).
- The user decrypts the scores on their local machine.
- Apply
client_softmaxto calculate the final % probabilities in pure cleartext with 100% accuracy and no constraints!
Here is how you use the mathematically perfect Client-Side Softmax on decrypted scores:
from concrete_fhe_toolkit.ml.activations import client_softmax
# Pretend these are the decrypted raw scores from your FHE model
decrypted_scores = [12.4, -5.1, 14.8, 0.2]
# Calculate perfect probabilities on the client side
probabilities = client_softmax(decrypted_scores)
for i, p in enumerate(probabilities):
print(f"Class {i}: {p}% probability")