mirror of
https://github.com/zama-ai/concrete.git
synced 2026-02-08 11:35:02 -05:00
chore: Move to the mono repo layout
This commit is contained in:
3
frontends/concrete-python/tests/__init__.py
Normal file
3
frontends/concrete-python/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy` namespace.
|
||||
"""
|
||||
3
frontends/concrete-python/tests/compilation/__init__.py
Normal file
3
frontends/concrete-python/tests/compilation/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.compilation` namespace.
|
||||
"""
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
Tests of `DebugArtifacts` class.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from concrete.numpy import DebugArtifacts, compiler
|
||||
|
||||
|
||||
def test_artifacts_export(helpers):
|
||||
"""
|
||||
Test `export` method of `DebugArtifacts` class.
|
||||
"""
|
||||
|
||||
with tempfile.TemporaryDirectory() as path:
|
||||
tmpdir = Path(path)
|
||||
|
||||
configuration = helpers.configuration()
|
||||
artifacts = DebugArtifacts(tmpdir)
|
||||
|
||||
@compiler({"x": "encrypted"})
|
||||
def f(x):
|
||||
a = ((np.sin(x) ** 2) + (np.cos(x) ** 2)).astype(np.int64)
|
||||
b = np.where(x < 5, x * 10, x + 10)
|
||||
return a + b
|
||||
|
||||
inputset = range(10)
|
||||
f.compile(inputset, configuration, artifacts)
|
||||
|
||||
artifacts.export()
|
||||
|
||||
assert (tmpdir / "environment.txt").exists()
|
||||
assert (tmpdir / "requirements.txt").exists()
|
||||
|
||||
assert (tmpdir / "function.txt").exists()
|
||||
assert (tmpdir / "parameters.txt").exists()
|
||||
|
||||
assert (tmpdir / "1.initial.graph.txt").exists()
|
||||
assert (tmpdir / "2.after-fusing.graph.txt").exists()
|
||||
assert (tmpdir / "3.after-fusing.graph.txt").exists()
|
||||
assert (tmpdir / "4.final.graph.txt").exists()
|
||||
|
||||
assert (tmpdir / "mlir.txt").exists()
|
||||
assert (tmpdir / "client_parameters.json").exists()
|
||||
|
||||
artifacts.export()
|
||||
|
||||
assert (tmpdir / "environment.txt").exists()
|
||||
assert (tmpdir / "requirements.txt").exists()
|
||||
|
||||
assert (tmpdir / "function.txt").exists()
|
||||
assert (tmpdir / "parameters.txt").exists()
|
||||
|
||||
assert (tmpdir / "1.initial.graph.txt").exists()
|
||||
assert (tmpdir / "2.after-fusing.graph.txt").exists()
|
||||
assert (tmpdir / "3.after-fusing.graph.txt").exists()
|
||||
assert (tmpdir / "4.final.graph.txt").exists()
|
||||
|
||||
assert (tmpdir / "mlir.txt").exists()
|
||||
assert (tmpdir / "client_parameters.json").exists()
|
||||
345
frontends/concrete-python/tests/compilation/test_circuit.py
Normal file
345
frontends/concrete-python/tests/compilation/test_circuit.py
Normal file
@@ -0,0 +1,345 @@
|
||||
"""
|
||||
Tests of `Circuit` class.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy import Client, ClientSpecs, EvaluationKeys, LookupTable, Server, compiler
|
||||
|
||||
|
||||
def test_circuit_str(helpers):
|
||||
"""
|
||||
Test `__str__` method of `Circuit` class.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@compiler({"x": "encrypted", "y": "encrypted"})
|
||||
def f(x, y):
|
||||
return x + y
|
||||
|
||||
inputset = [(np.random.randint(0, 2**4), np.random.randint(0, 2**5)) for _ in range(100)]
|
||||
circuit = f.compile(inputset, configuration.fork(p_error=6e-5))
|
||||
|
||||
assert str(circuit) == circuit.graph.format()
|
||||
|
||||
|
||||
def test_circuit_feedback(helpers):
|
||||
"""
|
||||
Test feedback properties of `Circuit` class.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
p_error = 0.1
|
||||
global_p_error = 0.05
|
||||
|
||||
@compiler({"x": "encrypted", "y": "encrypted"})
|
||||
def f(x, y):
|
||||
return np.sqrt(((x + y) ** 2) + 10).astype(np.int64)
|
||||
|
||||
inputset = [(np.random.randint(0, 2**2), np.random.randint(0, 2**2)) for _ in range(100)]
|
||||
circuit = f.compile(inputset, configuration, p_error=p_error, global_p_error=global_p_error)
|
||||
|
||||
assert isinstance(circuit.complexity, float)
|
||||
assert isinstance(circuit.size_of_secret_keys, int)
|
||||
assert isinstance(circuit.size_of_bootstrap_keys, int)
|
||||
assert isinstance(circuit.size_of_keyswitch_keys, int)
|
||||
assert isinstance(circuit.size_of_inputs, int)
|
||||
assert isinstance(circuit.size_of_outputs, int)
|
||||
assert isinstance(circuit.p_error, float)
|
||||
assert isinstance(circuit.global_p_error, float)
|
||||
|
||||
assert circuit.p_error <= p_error
|
||||
assert circuit.global_p_error <= global_p_error
|
||||
|
||||
|
||||
def test_circuit_bad_run(helpers):
|
||||
"""
|
||||
Test `run` method of `Circuit` class with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@compiler({"x": "encrypted", "y": "encrypted"})
|
||||
def f(x, y):
|
||||
return x + y
|
||||
|
||||
inputset = [(np.random.randint(0, 2**4), np.random.randint(0, 2**5)) for _ in range(100)]
|
||||
circuit = f.compile(inputset, configuration)
|
||||
|
||||
# with 1 argument
|
||||
# ---------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
circuit.encrypt_run_decrypt(1)
|
||||
|
||||
assert str(excinfo.value) == "Expected 2 inputs but got 1"
|
||||
|
||||
# with 3 arguments
|
||||
# ----------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
circuit.encrypt_run_decrypt(1, 2, 3)
|
||||
|
||||
assert str(excinfo.value) == "Expected 2 inputs but got 3"
|
||||
|
||||
# with negative argument 0
|
||||
# ------------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
circuit.encrypt_run_decrypt(-1, 11)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Expected argument 0 to be EncryptedScalar<uint6> but it's EncryptedScalar<int1>"
|
||||
)
|
||||
|
||||
# with negative argument 1
|
||||
# ------------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
circuit.encrypt_run_decrypt(1, -11)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Expected argument 1 to be EncryptedScalar<uint6> but it's EncryptedScalar<int5>"
|
||||
)
|
||||
|
||||
# with large argument 0
|
||||
# ---------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
circuit.encrypt_run_decrypt(100, 10)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Expected argument 0 to be EncryptedScalar<uint6> but it's EncryptedScalar<uint7>"
|
||||
)
|
||||
|
||||
# with large argument 1
|
||||
# ---------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
circuit.encrypt_run_decrypt(1, 100)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Expected argument 1 to be EncryptedScalar<uint6> but it's EncryptedScalar<uint7>"
|
||||
)
|
||||
|
||||
|
||||
def test_client_server_api(helpers):
|
||||
"""
|
||||
Test client/server API.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x + 42
|
||||
|
||||
inputset = [np.random.randint(0, 10, size=(3,)) for _ in range(10)]
|
||||
circuit = function.compile(inputset, configuration.fork(jit=False))
|
||||
|
||||
# for coverage
|
||||
circuit.keygen()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp_dir_path = Path(tmp_dir)
|
||||
|
||||
server_path = tmp_dir_path / "server.zip"
|
||||
circuit.server.save(server_path)
|
||||
|
||||
client_path = tmp_dir_path / "client.zip"
|
||||
circuit.client.save(client_path)
|
||||
|
||||
circuit.cleanup()
|
||||
|
||||
server = Server.load(server_path)
|
||||
|
||||
serialized_client_specs = server.client_specs.serialize()
|
||||
client_specs = ClientSpecs.unserialize(serialized_client_specs)
|
||||
|
||||
clients = [
|
||||
Client(client_specs, configuration.insecure_key_cache_location),
|
||||
Client.load(client_path, configuration.insecure_key_cache_location),
|
||||
]
|
||||
|
||||
for client in clients:
|
||||
args = client.encrypt([3, 8, 1])
|
||||
|
||||
serialized_args = client.specs.serialize_public_args(args)
|
||||
serialized_evaluation_keys = client.evaluation_keys.serialize()
|
||||
|
||||
unserialized_args = server.client_specs.unserialize_public_args(serialized_args)
|
||||
unserialized_evaluation_keys = EvaluationKeys.unserialize(serialized_evaluation_keys)
|
||||
|
||||
result = server.run(unserialized_args, unserialized_evaluation_keys)
|
||||
serialized_result = server.client_specs.serialize_public_result(result)
|
||||
|
||||
unserialized_result = client.specs.unserialize_public_result(serialized_result)
|
||||
output = client.decrypt(unserialized_result)
|
||||
|
||||
assert np.array_equal(output, [45, 50, 43])
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
server.save("UNUSED", via_mlir=True)
|
||||
|
||||
assert str(excinfo.value) == "Loaded server objects cannot be saved again via MLIR"
|
||||
|
||||
server.cleanup()
|
||||
|
||||
|
||||
def test_client_server_api_via_mlir(helpers):
|
||||
"""
|
||||
Test client/server API.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x + 42
|
||||
|
||||
inputset = [np.random.randint(0, 10, size=(3,)) for _ in range(10)]
|
||||
circuit = function.compile(inputset, configuration.fork(jit=False))
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
tmp_dir_path = Path(tmp_dir)
|
||||
|
||||
server_path = tmp_dir_path / "server.zip"
|
||||
circuit.server.save(server_path, via_mlir=True)
|
||||
|
||||
client_path = tmp_dir_path / "client.zip"
|
||||
circuit.client.save(client_path)
|
||||
|
||||
circuit.cleanup()
|
||||
|
||||
server = Server.load(server_path)
|
||||
|
||||
serialized_client_specs = server.client_specs.serialize()
|
||||
client_specs = ClientSpecs.unserialize(serialized_client_specs)
|
||||
|
||||
clients = [
|
||||
Client(client_specs, configuration.insecure_key_cache_location),
|
||||
Client.load(client_path, configuration.insecure_key_cache_location),
|
||||
]
|
||||
|
||||
for client in clients:
|
||||
args = client.encrypt([3, 8, 1])
|
||||
|
||||
serialized_args = client.specs.serialize_public_args(args)
|
||||
serialized_evaluation_keys = client.evaluation_keys.serialize()
|
||||
|
||||
unserialized_args = server.client_specs.unserialize_public_args(serialized_args)
|
||||
unserialized_evaluation_keys = EvaluationKeys.unserialize(serialized_evaluation_keys)
|
||||
|
||||
result = server.run(unserialized_args, unserialized_evaluation_keys)
|
||||
serialized_result = server.client_specs.serialize_public_result(result)
|
||||
|
||||
unserialized_result = client.specs.unserialize_public_result(serialized_result)
|
||||
output = client.decrypt(unserialized_result)
|
||||
|
||||
assert np.array_equal(output, [45, 50, 43])
|
||||
|
||||
server.cleanup()
|
||||
|
||||
|
||||
def test_bad_server_save(helpers):
|
||||
"""
|
||||
Test `save` method of `Server` class with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x + 42
|
||||
|
||||
inputset = range(10)
|
||||
circuit = function.compile(inputset, configuration)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
circuit.server.save("test.zip")
|
||||
|
||||
assert str(excinfo.value) == "Just-in-Time compilation cannot be saved"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("p_error", [0.75, 0.5, 0.4, 0.25, 0.2, 0.1, 0.01, 0.001])
|
||||
@pytest.mark.parametrize("bit_width", [5])
|
||||
@pytest.mark.parametrize("sample_size", [1_000_000])
|
||||
@pytest.mark.parametrize("tolerance", [0.075])
|
||||
def test_p_error_simulation(p_error, bit_width, sample_size, tolerance, helpers):
|
||||
"""
|
||||
Test p_error simulation.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration().fork(global_p_error=None)
|
||||
|
||||
table = LookupTable([0] + [x - 1 for x in range(1, 2**bit_width)])
|
||||
|
||||
@compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return table[x + 1]
|
||||
|
||||
inputset = [np.random.randint(0, (2**bit_width) - 1, size=(sample_size,)) for _ in range(100)]
|
||||
circuit = function.compile(inputset, configuration=configuration, p_error=p_error)
|
||||
|
||||
assert circuit.p_error < p_error
|
||||
|
||||
sample = np.random.randint(0, (2**bit_width) - 1, size=(sample_size,))
|
||||
output = circuit.simulate(sample)
|
||||
|
||||
errors = np.sum(output != sample)
|
||||
|
||||
expected_number_of_errors_on_average = sample_size * circuit.p_error
|
||||
tolerated_difference = expected_number_of_errors_on_average * tolerance
|
||||
|
||||
acceptable_number_of_errors = [
|
||||
round(expected_number_of_errors_on_average - tolerated_difference),
|
||||
round(expected_number_of_errors_on_average + tolerated_difference),
|
||||
]
|
||||
assert acceptable_number_of_errors[0] <= errors <= acceptable_number_of_errors[1]
|
||||
|
||||
|
||||
def test_circuit_run_with_unused_arg(helpers):
|
||||
"""
|
||||
Test `encrypt_run_decrypt` method of `Circuit` class with unused arguments.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@compiler({"x": "encrypted", "y": "encrypted"})
|
||||
def f(x, y): # pylint: disable=unused-argument
|
||||
return x + 10
|
||||
|
||||
inputset = [
|
||||
(np.random.randint(2**3, 2**4), np.random.randint(2**4, 2**5)) for _ in range(100)
|
||||
]
|
||||
circuit = f.compile(inputset, configuration)
|
||||
|
||||
with pytest.raises(ValueError, match="Expected 2 inputs but got 1"):
|
||||
circuit.encrypt_run_decrypt(10)
|
||||
|
||||
assert circuit.encrypt_run_decrypt(10, 0) == 20
|
||||
assert circuit.encrypt_run_decrypt(10, 10) == 20
|
||||
assert circuit.encrypt_run_decrypt(10, 20) == 20
|
||||
|
||||
|
||||
def test_dataflow_circuit(helpers):
|
||||
"""
|
||||
Test execution with dataflow_parallelize=True.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration().fork(dataflow_parallelize=True)
|
||||
|
||||
@compiler({"x": "encrypted", "y": "encrypted"})
|
||||
def f(x, y):
|
||||
return (x**2) + (y // 2)
|
||||
|
||||
inputset = [(np.random.randint(0, 2**3), np.random.randint(0, 2**3)) for _ in range(100)]
|
||||
circuit = f.compile(inputset, configuration)
|
||||
|
||||
assert circuit.encrypt_run_decrypt(5, 6) == 28
|
||||
332
frontends/concrete-python/tests/compilation/test_compiler.py
Normal file
332
frontends/concrete-python/tests/compilation/test_compiler.py
Normal file
@@ -0,0 +1,332 @@
|
||||
"""
|
||||
Tests of `Compiler` class.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.compilation import Compiler
|
||||
|
||||
|
||||
def test_compiler_bad_init():
|
||||
"""
|
||||
Test `__init__` method of `Compiler` class with bad parameters.
|
||||
"""
|
||||
|
||||
def f(x, y, z):
|
||||
return x + y + z
|
||||
|
||||
# missing all
|
||||
# -----------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
Compiler(f, {})
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption statuses of parameters 'x', 'y' and 'z' of function 'f' are not provided"
|
||||
)
|
||||
|
||||
# missing x and y
|
||||
# ---------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
Compiler(f, {"z": "clear"})
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption statuses of parameters 'x' and 'y' of function 'f' are not provided"
|
||||
)
|
||||
|
||||
# missing x
|
||||
# ---------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
Compiler(f, {"y": "encrypted", "z": "clear"})
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption status of parameter 'x' of function 'f' is not provided"
|
||||
)
|
||||
|
||||
# additional a, b, c
|
||||
# ------------------
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
Compiler(
|
||||
f,
|
||||
{
|
||||
"x": "encrypted",
|
||||
"y": "encrypted",
|
||||
"z": "encrypted",
|
||||
"a": "encrypted",
|
||||
"b": "encrypted",
|
||||
"c": "encrypted",
|
||||
},
|
||||
)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption statuses of 'a', 'b' and 'c' are provided "
|
||||
"but they are not a parameter of function 'f'"
|
||||
)
|
||||
|
||||
# additional a and b
|
||||
# ------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
Compiler(
|
||||
f,
|
||||
{
|
||||
"x": "encrypted",
|
||||
"y": "encrypted",
|
||||
"z": "encrypted",
|
||||
"a": "encrypted",
|
||||
"b": "encrypted",
|
||||
},
|
||||
)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption statuses of 'a' and 'b' are provided "
|
||||
"but they are not a parameter of function 'f'"
|
||||
)
|
||||
|
||||
# additional a
|
||||
# ------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
Compiler(
|
||||
f,
|
||||
{
|
||||
"x": "encrypted",
|
||||
"y": "encrypted",
|
||||
"z": "encrypted",
|
||||
"a": "encrypted",
|
||||
},
|
||||
)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption status of 'a' is provided but it is not a parameter of function 'f'"
|
||||
)
|
||||
|
||||
|
||||
def test_compiler_bad_call():
|
||||
"""
|
||||
Test `__call__` method of `Compiler` class with bad parameters.
|
||||
"""
|
||||
|
||||
def f(x, y, z):
|
||||
return x + y + z
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
compiler = Compiler(f, {"x": "encrypted", "y": "encrypted", "z": "clear"})
|
||||
compiler(1, 2, 3, invalid=4)
|
||||
|
||||
assert str(excinfo.value) == "Calling function 'f' with kwargs is not supported"
|
||||
|
||||
|
||||
def test_compiler_bad_trace(helpers):
|
||||
"""
|
||||
Test `trace` method of `Compiler` class with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# without inputset
|
||||
# ----------------
|
||||
|
||||
def f(x, y, z):
|
||||
return x + y + z
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
compiler = Compiler(
|
||||
f,
|
||||
{"x": "encrypted", "y": "encrypted", "z": "clear"},
|
||||
)
|
||||
compiler.trace(configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == "Tracing function 'f' without an inputset is not supported"
|
||||
|
||||
# bad return
|
||||
# ----------
|
||||
|
||||
def g():
|
||||
return np.array([{}, ()], dtype=object)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler = Compiler(g, {})
|
||||
compiler.trace(inputset=[()], configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == "Function 'g' returned '[{} ()]', which is not supported"
|
||||
|
||||
|
||||
def test_compiler_bad_compile(helpers):
|
||||
"""
|
||||
Test `compile` method of `Compiler` class with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
def f(x, y, z):
|
||||
return x + y + z
|
||||
|
||||
# without inputset
|
||||
# ----------------
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
compiler = Compiler(
|
||||
f,
|
||||
{"x": "encrypted", "y": "encrypted", "z": "clear"},
|
||||
)
|
||||
compiler.compile(configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == "Compiling function 'f' without an inputset is not supported"
|
||||
|
||||
# with bad inputset at the first input
|
||||
# ------------------------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler = Compiler(
|
||||
f,
|
||||
{"x": "encrypted", "y": "encrypted", "z": "clear"},
|
||||
)
|
||||
inputset = [1]
|
||||
compiler.compile(inputset, configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Input #0 of your inputset is not well formed "
|
||||
"(expected a tuple of 3 values got a single value)"
|
||||
)
|
||||
|
||||
# with bad inputset at the second input
|
||||
# -------------------------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler = Compiler(
|
||||
f,
|
||||
{"x": "encrypted", "y": "encrypted", "z": "clear"},
|
||||
)
|
||||
inputset = [(1, 2, 3), (1, 2)]
|
||||
compiler.compile(inputset, configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Input #1 of your inputset is not well formed "
|
||||
"(expected a tuple of 3 values got a tuple of 2 values)"
|
||||
)
|
||||
|
||||
|
||||
def test_compiler_compile_bad_inputset(helpers):
|
||||
"""
|
||||
Test `compile` method of `Compiler` class with bad inputset.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# with inf
|
||||
# --------
|
||||
|
||||
def f(x):
|
||||
return (x + np.inf).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
compiler = Compiler(f, {"x": "encrypted"})
|
||||
compiler.compile(range(10), configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == "Bound measurement using inputset[0] failed"
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
Evaluation of the graph failed
|
||||
|
||||
%0 = x # EncryptedScalar<uint1>
|
||||
%1 = subgraph(%0) # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of this node failed
|
||||
return %1
|
||||
|
||||
Subgraphs:
|
||||
|
||||
%1 = subgraph(%0):
|
||||
|
||||
%0 = input # EncryptedScalar<uint1>
|
||||
%1 = inf # ClearScalar<float64>
|
||||
%2 = add(%0, %1) # EncryptedScalar<float64>
|
||||
%3 = astype(%2, dtype=int_) # EncryptedScalar<uint1>
|
||||
return %3
|
||||
|
||||
""".strip(),
|
||||
str(excinfo.value.__cause__).strip(),
|
||||
)
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
Evaluation of the graph failed
|
||||
|
||||
%0 = input # EncryptedScalar<uint1>
|
||||
%1 = inf # ClearScalar<float64>
|
||||
%2 = add(%0, %1) # EncryptedScalar<float64>
|
||||
%3 = astype(%2, dtype=int_) # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of this node failed
|
||||
return %3
|
||||
|
||||
""".strip(),
|
||||
str(excinfo.value.__cause__.__cause__).strip(),
|
||||
)
|
||||
|
||||
assert (
|
||||
str(excinfo.value.__cause__.__cause__.__cause__)
|
||||
== "An `Inf` value is tried to be converted to integer"
|
||||
)
|
||||
|
||||
# with nan
|
||||
# --------
|
||||
|
||||
def g(x):
|
||||
return (x + np.nan).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
compiler = Compiler(g, {"x": "encrypted"})
|
||||
compiler.compile(range(10), configuration=configuration)
|
||||
|
||||
assert str(excinfo.value) == "Bound measurement using inputset[0] failed"
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
Evaluation of the graph failed
|
||||
|
||||
%0 = x # EncryptedScalar<uint1>
|
||||
%1 = subgraph(%0) # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of this node failed
|
||||
return %1
|
||||
|
||||
Subgraphs:
|
||||
|
||||
%1 = subgraph(%0):
|
||||
|
||||
%0 = input # EncryptedScalar<uint1>
|
||||
%1 = nan # ClearScalar<float64>
|
||||
%2 = add(%0, %1) # EncryptedScalar<float64>
|
||||
%3 = astype(%2, dtype=int_) # EncryptedScalar<uint1>
|
||||
return %3
|
||||
|
||||
""".strip(),
|
||||
str(excinfo.value.__cause__).strip(),
|
||||
)
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
Evaluation of the graph failed
|
||||
|
||||
%0 = input # EncryptedScalar<uint1>
|
||||
%1 = nan # ClearScalar<float64>
|
||||
%2 = add(%0, %1) # EncryptedScalar<float64>
|
||||
%3 = astype(%2, dtype=int_) # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of this node failed
|
||||
return %3
|
||||
|
||||
""".strip(),
|
||||
str(excinfo.value.__cause__.__cause__).strip(),
|
||||
)
|
||||
|
||||
assert (
|
||||
str(excinfo.value.__cause__.__cause__.__cause__)
|
||||
== "A `NaN` value is tried to be converted to integer"
|
||||
)
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Tests of `Configuration` class.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.compilation import Configuration
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"kwargs,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
{"enable_unsafe_features": False, "use_insecure_key_cache": True},
|
||||
RuntimeError,
|
||||
"Insecure key cache cannot be used without enabling unsafe features",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"enable_unsafe_features": True,
|
||||
"use_insecure_key_cache": True,
|
||||
"insecure_key_cache_location": None,
|
||||
},
|
||||
RuntimeError,
|
||||
"Insecure key cache cannot be enabled without specifying its location",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_configuration_bad_init(kwargs, expected_error, expected_message):
|
||||
"""
|
||||
Test `__init__` method of `Configuration` class with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Configuration(**kwargs)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
def test_configuration_fork():
|
||||
"""
|
||||
Test `fork` method of `Configuration` class.
|
||||
"""
|
||||
|
||||
config1 = Configuration(enable_unsafe_features=True, loop_parallelize=False)
|
||||
config2 = config1.fork(enable_unsafe_features=False, loop_parallelize=True)
|
||||
|
||||
assert config1 is not config2
|
||||
|
||||
assert config1.enable_unsafe_features is True
|
||||
assert config1.loop_parallelize is False
|
||||
|
||||
assert config2.enable_unsafe_features is False
|
||||
assert config2.loop_parallelize is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"kwargs,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
{"foo": False},
|
||||
TypeError,
|
||||
"Unexpected keyword argument 'foo'",
|
||||
),
|
||||
pytest.param(
|
||||
{"dump_artifacts_on_unexpected_failures": "yes"},
|
||||
TypeError,
|
||||
"Unexpected type for keyword argument 'dump_artifacts_on_unexpected_failures' "
|
||||
"(expected 'bool', got 'str')",
|
||||
),
|
||||
pytest.param(
|
||||
{"insecure_key_cache_location": 3},
|
||||
TypeError,
|
||||
"Unexpected type for keyword argument 'insecure_key_cache_location' "
|
||||
"(expected 'Optional[str]', got 'int')",
|
||||
),
|
||||
pytest.param(
|
||||
{"p_error": "yes"},
|
||||
TypeError,
|
||||
"Unexpected type for keyword argument 'p_error' "
|
||||
"(expected 'Optional[float]', got 'str')",
|
||||
),
|
||||
pytest.param(
|
||||
{"global_p_error": "mamma mia"},
|
||||
TypeError,
|
||||
"Unexpected type for keyword argument 'global_p_error' "
|
||||
"(expected 'Optional[float]', got 'str')",
|
||||
),
|
||||
pytest.param(
|
||||
{"show_optimizer": "please"},
|
||||
TypeError,
|
||||
"Unexpected type for keyword argument 'show_optimizer' "
|
||||
"(expected 'Optional[bool]', got 'str')",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_configuration_bad_fork(kwargs, expected_error, expected_message):
|
||||
"""
|
||||
Test `fork` method of `Configuration` class with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Configuration().fork(**kwargs)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
260
frontends/concrete-python/tests/compilation/test_decorators.py
Normal file
260
frontends/concrete-python/tests/compilation/test_decorators.py
Normal file
@@ -0,0 +1,260 @@
|
||||
"""
|
||||
Tests of `compiler` and `circuit` decorators.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def test_compiler_call_and_compile(helpers):
|
||||
"""
|
||||
Test `__call__` and `compile` methods of `compiler` decorator back to back.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x + 42
|
||||
|
||||
for i in range(10):
|
||||
function(i)
|
||||
|
||||
circuit = function.compile(configuration=configuration)
|
||||
|
||||
sample = 5
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
def test_compiler_verbose_trace(helpers, capsys):
|
||||
"""
|
||||
Test `trace` method of `compiler` decorator with verbose flag.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
artifacts = cnp.DebugArtifacts()
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x + 42
|
||||
|
||||
inputset = range(10)
|
||||
function.trace(inputset, configuration, artifacts, show_graph=True)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out.strip() == (
|
||||
f"""
|
||||
|
||||
Computation Graph
|
||||
------------------------------------------------------------------
|
||||
{str(list(artifacts.textual_representations_of_graphs.values())[-1][-1])}
|
||||
------------------------------------------------------------------
|
||||
|
||||
""".strip()
|
||||
)
|
||||
|
||||
|
||||
def test_compiler_verbose_compile(helpers, capsys):
|
||||
"""
|
||||
Test `compile` method of `compiler` decorator with verbose flag.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
artifacts = cnp.DebugArtifacts()
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x + 42
|
||||
|
||||
inputset = range(10)
|
||||
function.compile(inputset, configuration, artifacts, verbose=True)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out.strip().startswith(
|
||||
f"""
|
||||
|
||||
Computation Graph
|
||||
--------------------------------------------------------------------------------
|
||||
{list(artifacts.textual_representations_of_graphs.values())[-1][-1]}
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
MLIR
|
||||
--------------------------------------------------------------------------------
|
||||
{artifacts.mlir_to_compile}
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Optimizer
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
""".strip()
|
||||
)
|
||||
|
||||
|
||||
def test_circuit(helpers):
|
||||
"""
|
||||
Test circuit decorator.
|
||||
"""
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit1(x: cnp.uint2):
|
||||
return x + 42
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
%0 = x # EncryptedScalar<uint2>
|
||||
%1 = 42 # ClearScalar<uint6>
|
||||
%2 = add(%0, %1) # EncryptedScalar<uint6>
|
||||
return %2
|
||||
|
||||
""".strip(),
|
||||
str(circuit1),
|
||||
)
|
||||
|
||||
# ======================================================================
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit2(x: cnp.tensor[cnp.uint2, 3, 2]):
|
||||
return x + 42
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
%0 = x # EncryptedTensor<uint2, shape=(3, 2)>
|
||||
%1 = 42 # ClearScalar<uint6>
|
||||
%2 = add(%0, %1) # EncryptedTensor<uint6, shape=(3, 2)>
|
||||
return %2
|
||||
|
||||
""".strip(),
|
||||
str(circuit2),
|
||||
)
|
||||
|
||||
# ======================================================================
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit3(x: cnp.uint3):
|
||||
def square(x):
|
||||
return x**2
|
||||
|
||||
return cnp.univariate(square, outputs=cnp.uint7)(x)
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
%0 = x # EncryptedScalar<uint3>
|
||||
%1 = square(%0) # EncryptedScalar<uint7>
|
||||
return %1
|
||||
|
||||
""".strip(),
|
||||
str(circuit3),
|
||||
)
|
||||
|
||||
# ======================================================================
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit4(x: cnp.uint3):
|
||||
return ((np.sin(x) ** 2) + (np.cos(x) ** 2)).astype(cnp.uint3)
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
%0 = x # EncryptedScalar<uint3>
|
||||
%1 = subgraph(%0) # EncryptedScalar<uint3>
|
||||
return %1
|
||||
|
||||
Subgraphs:
|
||||
|
||||
%1 = subgraph(%0):
|
||||
|
||||
%0 = input # EncryptedScalar<uint3>
|
||||
%1 = sin(%0) # EncryptedScalar<float64>
|
||||
%2 = 2 # ClearScalar<uint2>
|
||||
%3 = power(%1, %2) # EncryptedScalar<float64>
|
||||
%4 = cos(%0) # EncryptedScalar<float64>
|
||||
%5 = 2 # ClearScalar<uint2>
|
||||
%6 = power(%4, %5) # EncryptedScalar<float64>
|
||||
%7 = add(%3, %6) # EncryptedScalar<float64>
|
||||
%8 = astype(%7) # EncryptedScalar<uint3>
|
||||
return %8
|
||||
|
||||
""".strip(),
|
||||
str(circuit4),
|
||||
)
|
||||
|
||||
# ======================================================================
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit5(x: cnp.int2):
|
||||
return x + 42
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
%0 = x # EncryptedScalar<int2>
|
||||
%1 = 42 # ClearScalar<uint6>
|
||||
%2 = add(%0, %1) # EncryptedScalar<int6>
|
||||
return %2
|
||||
|
||||
""".strip(),
|
||||
str(circuit5),
|
||||
)
|
||||
|
||||
|
||||
def test_bad_circuit(helpers):
|
||||
"""
|
||||
Test circuit decorator with bad parameters.
|
||||
"""
|
||||
|
||||
# bad annotation
|
||||
# --------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit1(x: int):
|
||||
return x + 42
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
f"Annotation {str(int)} for argument 'x' is not valid "
|
||||
f"(please use a cnp type such as `cnp.uint4` or 'cnp.tensor[cnp.uint4, 3, 2]')"
|
||||
)
|
||||
|
||||
# missing encryption status
|
||||
# -------------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
@cnp.circuit({}, helpers.configuration())
|
||||
def circuit2(x: cnp.uint3):
|
||||
return x + 42
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Encryption status of parameter 'x' of function 'circuit2' is not provided"
|
||||
)
|
||||
|
||||
# bad astype
|
||||
# ----------
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit3(x: cnp.uint3):
|
||||
return x.astype(np.int64)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"`astype` method must be called with a concrete.numpy type "
|
||||
"for direct circuit definition (e.g., value.astype(cnp.uint4))"
|
||||
)
|
||||
|
||||
# round
|
||||
# -----
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def circuit4(x: cnp.uint3):
|
||||
return round(x)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"'round(x)' cannot be used in direct definition (you may use np.around instead)"
|
||||
)
|
||||
331
frontends/concrete-python/tests/conftest.py
Normal file
331
frontends/concrete-python/tests/conftest.py
Normal file
@@ -0,0 +1,331 @@
|
||||
"""
|
||||
Configuration of `pytest`.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Tuple, Union
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
import tests
|
||||
|
||||
tests_directory = os.path.dirname(tests.__file__)
|
||||
|
||||
|
||||
INSECURE_KEY_CACHE_LOCATION = None
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
"""
|
||||
Add CLI options.
|
||||
"""
|
||||
|
||||
parser.addoption(
|
||||
"--global-coverage",
|
||||
type=str,
|
||||
default=None,
|
||||
action="store",
|
||||
help="JSON file to dump pytest-cov terminal report.",
|
||||
)
|
||||
parser.addoption(
|
||||
"--key-cache",
|
||||
type=str,
|
||||
default=None,
|
||||
action="store",
|
||||
help="Specify the location of the key cache",
|
||||
)
|
||||
|
||||
|
||||
def pytest_sessionstart(session):
|
||||
"""
|
||||
Initialize insecure key cache.
|
||||
"""
|
||||
# pylint: disable=global-statement
|
||||
global INSECURE_KEY_CACHE_LOCATION
|
||||
# pylint: enable=global-statement
|
||||
|
||||
key_cache_location = session.config.getoption("--key-cache", default=None)
|
||||
if key_cache_location is not None:
|
||||
if key_cache_location.lower() == "disable":
|
||||
key_cache_location = None
|
||||
else:
|
||||
key_cache_location = Path(key_cache_location).expanduser().resolve()
|
||||
else:
|
||||
key_cache_location = Path.home().resolve() / ".cache" / "concrete-numpy" / "pytest"
|
||||
|
||||
if key_cache_location:
|
||||
key_cache_location.mkdir(parents=True, exist_ok=True)
|
||||
print(f"INSECURE_KEY_CACHE_LOCATION={str(key_cache_location)}")
|
||||
|
||||
INSECURE_KEY_CACHE_LOCATION = str(key_cache_location)
|
||||
|
||||
|
||||
def pytest_sessionfinish(session, exitstatus): # pylint: disable=unused-argument
|
||||
"""
|
||||
Save global coverage info after testing is finished.
|
||||
"""
|
||||
|
||||
# Hacked together from the source code, they don't have an option to export to file,
|
||||
# and it's too much work to get a PR in for such a little thing.
|
||||
# https://github.com/pytest-dev/pytest-cov/blob/ec344d8adf2d78238d8f07cb20ed2463d7536970/src/pytest_cov/plugin.py#L329
|
||||
if session.config.pluginmanager.hasplugin("_cov"):
|
||||
global_coverage_option = session.config.getoption("--global-coverage", default=None)
|
||||
if global_coverage_option is not None:
|
||||
coverage_plugin = session.config.pluginmanager.getplugin("_cov")
|
||||
coverage_txt = coverage_plugin.cov_report.getvalue()
|
||||
|
||||
coverage_status = 0
|
||||
if (
|
||||
coverage_plugin.options.cov_fail_under is not None
|
||||
and coverage_plugin.options.cov_fail_under > 0
|
||||
and coverage_plugin.cov_total < coverage_plugin.options.cov_fail_under
|
||||
):
|
||||
coverage_status = 1
|
||||
|
||||
global_coverage_file_path = Path(global_coverage_option).resolve()
|
||||
with open(global_coverage_file_path, "w", encoding="utf-8") as f:
|
||||
json.dump({"exit_code": coverage_status, "content": coverage_txt}, f)
|
||||
|
||||
|
||||
class Helpers:
|
||||
"""
|
||||
Helpers class, which provides various helpers to tests.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def configuration() -> cnp.Configuration:
|
||||
"""
|
||||
Get the test configuration to use during testing.
|
||||
|
||||
Returns:
|
||||
cnp.Configuration:
|
||||
test configuration
|
||||
"""
|
||||
|
||||
return cnp.Configuration(
|
||||
dump_artifacts_on_unexpected_failures=False,
|
||||
enable_unsafe_features=True,
|
||||
use_insecure_key_cache=True,
|
||||
loop_parallelize=True,
|
||||
dataflow_parallelize=False,
|
||||
auto_parallelize=False,
|
||||
jit=True,
|
||||
insecure_key_cache_location=INSECURE_KEY_CACHE_LOCATION,
|
||||
global_p_error=(1 / 10_000),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def generate_encryption_statuses(parameters: Dict[str, Dict[str, Any]]) -> Dict[str, str]:
|
||||
"""
|
||||
Generate parameter encryption statuses accoring to a parameter specification.
|
||||
|
||||
Args:
|
||||
parameters (Dict[str, Dict[str, Any]]):
|
||||
parameter specification to use
|
||||
|
||||
e.g.,
|
||||
|
||||
{
|
||||
"param1": {"range": [0, 10], "status": "clear"},
|
||||
"param2": {"range": [3, 30], "status": "encrypted", "shape": (3,)},
|
||||
}
|
||||
|
||||
Returns:
|
||||
Dict[str, str]:
|
||||
parameter encryption statuses
|
||||
generated according to the given parameter specification
|
||||
"""
|
||||
|
||||
return {
|
||||
parameter: details["status"] if "status" in details else "encrypted"
|
||||
for parameter, details in parameters.items()
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_inputset(
|
||||
parameters: Dict[str, Dict[str, Any]],
|
||||
size: int = 128,
|
||||
) -> List[Union[Tuple[Union[int, np.ndarray], ...], Union[int, np.ndarray]]]:
|
||||
"""
|
||||
Generate a random inputset of desired size accoring to a parameter specification.
|
||||
|
||||
Args:
|
||||
parameters (Dict[str, Dict[str, Any]]):
|
||||
parameter specification to use
|
||||
|
||||
e.g.,
|
||||
|
||||
{
|
||||
"param1": {"range": [0, 10], "status": "clear"},
|
||||
"param2": {"range": [3, 30], "status": "encrypted", "shape": (3,)},
|
||||
}
|
||||
|
||||
size (int):
|
||||
size of the resulting inputset
|
||||
|
||||
Returns:
|
||||
List[Union[Tuple[Union[int, np.ndarray], ...], Union[int, np.ndarray]]]:
|
||||
random inputset of desired size
|
||||
generated according to the given parameter specification
|
||||
"""
|
||||
|
||||
inputset = []
|
||||
|
||||
for _ in range(size):
|
||||
sample = Helpers.generate_sample(parameters)
|
||||
inputset.append(tuple(sample) if len(sample) > 1 else sample[0])
|
||||
|
||||
return inputset
|
||||
|
||||
@staticmethod
|
||||
def generate_sample(parameters: Dict[str, Dict[str, Any]]) -> List[Union[int, np.ndarray]]:
|
||||
"""
|
||||
Generate a random sample accoring to a parameter specification.
|
||||
|
||||
Args:
|
||||
parameters (Dict[str, Dict[str, Any]]):
|
||||
parameter specification to use
|
||||
|
||||
e.g.,
|
||||
|
||||
{
|
||||
"param1": {"range": [0, 10], "status": "clear"},
|
||||
"param2": {"range": [3, 30], "status": "encrypted", "shape": (3,)},
|
||||
}
|
||||
|
||||
Returns:
|
||||
List[Union[int, np.ndarray]]:
|
||||
random sample
|
||||
generated according to the given parameter specification
|
||||
"""
|
||||
|
||||
sample = []
|
||||
|
||||
for description in parameters.values():
|
||||
minimum, maximum = description.get("range", [0, (2**16) - 1])
|
||||
|
||||
if "shape" in description:
|
||||
shape = description["shape"]
|
||||
sample.append(np.random.randint(minimum, maximum + 1, size=shape, dtype=np.int64))
|
||||
else:
|
||||
sample.append(np.int64(random.randint(minimum, maximum)))
|
||||
|
||||
return sample
|
||||
|
||||
@staticmethod
|
||||
def check_execution(
|
||||
circuit: cnp.Circuit,
|
||||
function: Callable,
|
||||
sample: Union[Any, List[Any]],
|
||||
retries: int = 1,
|
||||
simulate: bool = False,
|
||||
):
|
||||
"""
|
||||
Assert that `circuit` is behaves the same as `function` on `sample`.
|
||||
|
||||
Args:
|
||||
circuit (cnp.Circuit):
|
||||
compiled circuit
|
||||
|
||||
function (Callable):
|
||||
original function
|
||||
|
||||
sample (List[Any]):
|
||||
inputs
|
||||
|
||||
retries (int, default = 1):
|
||||
number of times to retry (for probabilistic execution)
|
||||
|
||||
simulate (bool, default = False):
|
||||
whether to simulate instead of fhe execution
|
||||
"""
|
||||
|
||||
if not isinstance(sample, list):
|
||||
sample = [sample]
|
||||
|
||||
def sanitize(values):
|
||||
if not isinstance(values, tuple):
|
||||
values = (values,)
|
||||
|
||||
result = []
|
||||
for value in values:
|
||||
if isinstance(value, (bool, np.bool_)):
|
||||
value = int(value)
|
||||
elif isinstance(value, np.ndarray) and value.dtype == np.bool_:
|
||||
value = value.astype(np.int64)
|
||||
|
||||
result.append(value)
|
||||
|
||||
return tuple(result)
|
||||
|
||||
for i in range(retries):
|
||||
expected = sanitize(function(*sample))
|
||||
actual = sanitize(
|
||||
circuit.simulate(*sample) if simulate else circuit.encrypt_run_decrypt(*sample)
|
||||
)
|
||||
|
||||
if all(np.array_equal(e, a) for e, a in zip(expected, actual)):
|
||||
break
|
||||
|
||||
if i == retries - 1:
|
||||
message = f"""
|
||||
|
||||
Expected Output
|
||||
===============
|
||||
{expected}
|
||||
|
||||
Actual Output
|
||||
=============
|
||||
{actual}
|
||||
|
||||
"""
|
||||
raise AssertionError(message)
|
||||
|
||||
@staticmethod
|
||||
def check_str(expected: str, actual: str):
|
||||
"""
|
||||
Assert that `circuit` is behaves the same as `function` on `sample`.
|
||||
|
||||
Args:
|
||||
expected (str):
|
||||
expected str
|
||||
|
||||
actual (str):
|
||||
actual str
|
||||
"""
|
||||
|
||||
# remove error line information
|
||||
# there are explicit tests to make sure the line information is correct
|
||||
# however, it would have been very hard to keep the other tests up to date
|
||||
|
||||
actual = "\n".join(
|
||||
line for line in actual.splitlines() if not line.strip().startswith(tests_directory)
|
||||
)
|
||||
|
||||
assert (
|
||||
actual.strip() == expected.strip()
|
||||
), f"""
|
||||
|
||||
Expected Output
|
||||
===============
|
||||
{expected}
|
||||
|
||||
Actual Output
|
||||
=============
|
||||
{actual}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def helpers():
|
||||
"""
|
||||
Fixture that provides `Helpers` class to tests.
|
||||
"""
|
||||
|
||||
return Helpers
|
||||
3
frontends/concrete-python/tests/dtypes/__init__.py
Normal file
3
frontends/concrete-python/tests/dtypes/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.dtypes` namespace.
|
||||
"""
|
||||
92
frontends/concrete-python/tests/dtypes/test_float.py
Normal file
92
frontends/concrete-python/tests/dtypes/test_float.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Tests of `Float` data type.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Float
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"bit_width,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
128,
|
||||
ValueError,
|
||||
"Float(128) is not supported (bit width must be one of 16, 32 or 64)",
|
||||
),
|
||||
pytest.param(
|
||||
"abc",
|
||||
ValueError,
|
||||
"Float('abc') is not supported (bit width must be one of 16, 32 or 64)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_float_bad_init(bit_width, expected_error, expected_message):
|
||||
"""
|
||||
Test `__init__` method of `Float` data type with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Float(bit_width)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs,rhs,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
Float(32),
|
||||
Float(32),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
Float(32),
|
||||
Float(64),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
Float(32),
|
||||
"Float(32)",
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"Float(32)",
|
||||
Float(32),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_float_eq(lhs, rhs, expected_result):
|
||||
"""
|
||||
Test `__eq__` method of `Float` data type.
|
||||
"""
|
||||
|
||||
assert (lhs == rhs) == expected_result
|
||||
assert (rhs == lhs) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
Float(16),
|
||||
"float16",
|
||||
),
|
||||
pytest.param(
|
||||
Float(32),
|
||||
"float32",
|
||||
),
|
||||
pytest.param(
|
||||
Float(64),
|
||||
"float64",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_float_str(data_type, expected_result):
|
||||
"""
|
||||
Test `__str__` method of `Float` data type.
|
||||
"""
|
||||
|
||||
assert str(data_type) == expected_result
|
||||
691
frontends/concrete-python/tests/dtypes/test_integer.py
Normal file
691
frontends/concrete-python/tests/dtypes/test_integer.py
Normal file
@@ -0,0 +1,691 @@
|
||||
"""
|
||||
Tests of `Integer` data type.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Integer, SignedInteger, UnsignedInteger
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,force_signed,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
-4,
|
||||
False,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-3,
|
||||
False,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-2,
|
||||
False,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
-1,
|
||||
False,
|
||||
SignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
False,
|
||||
UnsignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
1,
|
||||
False,
|
||||
UnsignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
2,
|
||||
False,
|
||||
UnsignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
3,
|
||||
False,
|
||||
UnsignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
4,
|
||||
False,
|
||||
UnsignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-4,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-3,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-2,
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
-1,
|
||||
True,
|
||||
SignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
True,
|
||||
SignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
1,
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
2,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
3,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
4,
|
||||
True,
|
||||
SignedInteger(4),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0, 1]),
|
||||
False,
|
||||
UnsignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0, 1]),
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
[-1, 1],
|
||||
False,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
[-1, 1],
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_that_can_represent(value, force_signed, expected_result):
|
||||
"""
|
||||
Test `that_can_represent` function of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert Integer.that_can_represent(value, force_signed) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,force_signed,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
"abc",
|
||||
False,
|
||||
ValueError,
|
||||
"Integer cannot represent 'abc'",
|
||||
),
|
||||
pytest.param(
|
||||
"abc",
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent 'abc'",
|
||||
),
|
||||
pytest.param(
|
||||
4.2,
|
||||
False,
|
||||
ValueError,
|
||||
"Integer cannot represent 4.2",
|
||||
),
|
||||
pytest.param(
|
||||
4.2,
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent 4.2",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([2.2, 1.1]),
|
||||
False,
|
||||
ValueError,
|
||||
"Integer cannot represent array([2.2, 1.1])",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([2.2, 1.1]),
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent array([2.2, 1.1])",
|
||||
),
|
||||
pytest.param(
|
||||
[1, (), 3],
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent [1, (), 3]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_bad_that_can_represent(value, force_signed, expected_error, expected_message):
|
||||
"""
|
||||
Test `that_can_represent` function of `Integer` data type with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Integer.that_can_represent(value, force_signed)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constructor,bit_width,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
SignedInteger,
|
||||
0,
|
||||
ValueError,
|
||||
"SignedInteger(0) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger,
|
||||
0,
|
||||
ValueError,
|
||||
"UnsignedInteger(0) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger,
|
||||
-1,
|
||||
ValueError,
|
||||
"SignedInteger(-1) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger,
|
||||
-1,
|
||||
ValueError,
|
||||
"UnsignedInteger(-1) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger,
|
||||
"abc",
|
||||
ValueError,
|
||||
"SignedInteger('abc') is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger,
|
||||
"abc",
|
||||
ValueError,
|
||||
"UnsignedInteger('abc') is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_bad_init(constructor, bit_width, expected_error, expected_message):
|
||||
"""
|
||||
Test `__init__` method of `Integer` data type with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
constructor(bit_width)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs,rhs,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
SignedInteger(5),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
UnsignedInteger(5),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
SignedInteger(6),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(6),
|
||||
SignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
UnsignedInteger(6),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(6),
|
||||
UnsignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
UnsignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
SignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
"SignedInteger(5)",
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"SignedInteger(5)",
|
||||
SignedInteger(5),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_eq(lhs, rhs, expected_result):
|
||||
"""
|
||||
Test `__eq__` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert (lhs == rhs) == expected_result
|
||||
assert (rhs == lhs) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(4),
|
||||
"uint4",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(7),
|
||||
"uint7",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(4),
|
||||
"int4",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(7),
|
||||
"int7",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_str(data_type, expected_result):
|
||||
"""
|
||||
Test `__str__` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert str(data_type) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-1,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-4,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
-16,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_min(data_type, expected_result):
|
||||
"""
|
||||
Test `min` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert data_type.min() == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
31,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
3,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
15,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_max(data_type, expected_result):
|
||||
"""
|
||||
Test `max` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert data_type.max() == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
4,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-4,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_can_represent(data_type, value, expected_result):
|
||||
"""
|
||||
Test `can_represent` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert data_type.can_represent(value) == expected_result
|
||||
77
frontends/concrete-python/tests/dtypes/test_utils.py
Normal file
77
frontends/concrete-python/tests/dtypes/test_utils.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Tests of utilities related to data types.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Float, SignedInteger, UnsignedInteger
|
||||
from concrete.numpy.dtypes.utils import combine_dtypes
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dtypes,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
[Float(64), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(32), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(16), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(32), Float(16)],
|
||||
Float(32),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(16), Float(16)],
|
||||
Float(16),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(32), SignedInteger(5)],
|
||||
Float(32),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), Float(16)],
|
||||
Float(16),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), SignedInteger(6)],
|
||||
SignedInteger(6),
|
||||
),
|
||||
pytest.param(
|
||||
[UnsignedInteger(5), UnsignedInteger(6)],
|
||||
UnsignedInteger(6),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), UnsignedInteger(6)],
|
||||
SignedInteger(7),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), UnsignedInteger(4)],
|
||||
SignedInteger(5),
|
||||
),
|
||||
pytest.param(
|
||||
[UnsignedInteger(6), SignedInteger(5)],
|
||||
SignedInteger(7),
|
||||
),
|
||||
pytest.param(
|
||||
[UnsignedInteger(4), SignedInteger(5)],
|
||||
SignedInteger(5),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_combine_dtypes(dtypes, expected_result):
|
||||
"""
|
||||
Test `combine_dtypes` function.
|
||||
"""
|
||||
|
||||
assert combine_dtypes(dtypes) == expected_result
|
||||
3
frontends/concrete-python/tests/execution/__init__.py
Normal file
3
frontends/concrete-python/tests/execution/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of execution.
|
||||
"""
|
||||
181
frontends/concrete-python/tests/execution/test_add.py
Normal file
181
frontends/concrete-python/tests/execution/test_add.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""
|
||||
Tests of execution of add operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x + 42,
|
||||
id="x + 42",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 42 + x,
|
||||
id="42 + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + np.array([1, 2, 3]),
|
||||
id="x + [1, 2, 3]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([1, 2, 3]) + x,
|
||||
id="[1, 2, 3] + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + np.array([[1, 2, 3], [4, 5, 6]]),
|
||||
id="x + [[1, 2, 3], [4, 5, 6]]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([[1, 2, 3], [4, 5, 6]]) + x,
|
||||
id="[[1, 2, 3], [4, 5, 6]] + x",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 85], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 85], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 85], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-50, 10], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-50, 10], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-50, 10], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 1000000], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 1000000], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 1000000], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_constant_add(function, parameters, helpers):
|
||||
"""
|
||||
Test add where one of the operators is a constant.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x + y,
|
||||
id="x + y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear"},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "clear"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "clear"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear"},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear", "shape": (2, 1)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (2, 1)},
|
||||
"y": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (2, 1)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-30, 30], "status": "encrypted", "shape": (3, 2)},
|
||||
"y": {"range": [-30, 30], "status": "encrypted", "shape": (3, 2)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_add(function, parameters, helpers):
|
||||
"""
|
||||
Test add where both of the operators are dynamic.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
61
frontends/concrete-python/tests/execution/test_array.py
Normal file
61
frontends/concrete-python/tests/execution/test_array.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
Tests of execution of array operation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: cnp.array([x, x + 1, 1]),
|
||||
{
|
||||
"x": {"range": [0, 10], "status": "encrypted", "shape": ()},
|
||||
},
|
||||
id="cnp.array([x, x + 1, 1])",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: cnp.array([x, y]),
|
||||
{
|
||||
"x": {"range": [0, 10], "status": "encrypted", "shape": ()},
|
||||
"y": {"range": [0, 10], "status": "clear", "shape": ()},
|
||||
},
|
||||
id="cnp.array([x, y])",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: cnp.array([[x, y], [y, x]]),
|
||||
{
|
||||
"x": {"range": [0, 10], "status": "encrypted", "shape": ()},
|
||||
"y": {"range": [0, 10], "status": "clear", "shape": ()},
|
||||
},
|
||||
id="cnp.array([[x, y], [y, x]])",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: cnp.array([[x, 1], [y, 2], [z, 3]]),
|
||||
{
|
||||
"x": {"range": [0, 10], "status": "encrypted", "shape": ()},
|
||||
"y": {"range": [0, 10], "status": "clear", "shape": ()},
|
||||
"z": {"range": [0, 10], "status": "encrypted", "shape": ()},
|
||||
},
|
||||
id="cnp.array([[x, 1], [y, 2], [z, 3]])",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_array(function, parameters, helpers):
|
||||
"""
|
||||
Test array.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
62
frontends/concrete-python/tests/execution/test_bitwise.py
Normal file
62
frontends/concrete-python/tests/execution/test_bitwise.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""
|
||||
Tests of execution of bitwise operations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x & y,
|
||||
id="x & y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x | y,
|
||||
id="x | y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x ^ y,
|
||||
id="x ^ y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 255], "status": "encrypted"},
|
||||
"y": {"range": [0, 255], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 7], "status": "encrypted"},
|
||||
"y": {"range": [0, 7], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 7], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 7], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 7], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 7], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_bitwise(function, parameters, helpers):
|
||||
"""
|
||||
Test bitwise operations between encrypted integers.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Tests of execution of broadcast to operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"from_shape,to_shape",
|
||||
[
|
||||
pytest.param((), (2,)),
|
||||
pytest.param((), (2, 3)),
|
||||
pytest.param((3,), (2, 3)),
|
||||
pytest.param((3,), (4, 2, 3)),
|
||||
pytest.param((1, 2), (4, 3, 2)),
|
||||
pytest.param((3, 2), (4, 3, 2)),
|
||||
pytest.param((3, 1), (4, 3, 5)),
|
||||
pytest.param((3, 1, 4), (3, 2, 4)),
|
||||
pytest.param((3, 1, 1), (5, 3, 1, 3)),
|
||||
],
|
||||
)
|
||||
def test_broadcast_to(from_shape, to_shape, helpers):
|
||||
"""
|
||||
Test broadcast to.
|
||||
"""
|
||||
|
||||
def function(x):
|
||||
return np.broadcast_to(x, to_shape)
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**2, size=from_shape) for _ in range(100)]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**2, size=from_shape)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
161
frontends/concrete-python/tests/execution/test_comparison.py
Normal file
161
frontends/concrete-python/tests/execution/test_comparison.py
Normal file
@@ -0,0 +1,161 @@
|
||||
"""
|
||||
Tests of execution of comparison operations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x == y,
|
||||
id="x == y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x != y,
|
||||
id="x != y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x < y,
|
||||
id="x < y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x <= y,
|
||||
id="x <= y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x > y,
|
||||
id="x > y",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x >= y,
|
||||
id="x >= y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 3], "status": "encrypted"},
|
||||
"y": {"range": [0, 3], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 255], "status": "encrypted"},
|
||||
"y": {"range": [0, 255], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-128, 127], "status": "encrypted"},
|
||||
"y": {"range": [-128, 127], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-128, 127], "status": "encrypted"},
|
||||
"y": {"range": [0, 255], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 255], "status": "encrypted"},
|
||||
"y": {"range": [-128, 127], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-8, 7], "status": "encrypted"},
|
||||
"y": {"range": [-8, 7], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-8, 7], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [-8, 7], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-8, 7], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [-8, 7], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_comparison(function, parameters, helpers):
|
||||
"""
|
||||
Test comparison operations between encrypted integers.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: (x == y) + 200,
|
||||
id="(x == y) + 200",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: (x != y) + 200,
|
||||
id="(x != y) + 200",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: (x < y) + 200,
|
||||
id="(x < y) + 200",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: (x <= y) + 200,
|
||||
id="(x <= y) + 200",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: (x > y) + 200,
|
||||
id="(x > y) + 200",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: (x >= y) + 200,
|
||||
id="(x >= y) + 200",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 15], "status": "encrypted"},
|
||||
"y": {"range": [0, 15], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-8, 7], "status": "encrypted"},
|
||||
"y": {"range": [-8, 7], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 15], "status": "encrypted"},
|
||||
"y": {"range": [0, 15], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-8, 7], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [-8, 7], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-10, 10], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [-10, 10], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_optimized_comparison(function, parameters, helpers):
|
||||
"""
|
||||
Test comparison operations between encrypted integers with a single TLU.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
176
frontends/concrete-python/tests/execution/test_concat.py
Normal file
176
frontends/concrete-python/tests/execution/test_concat.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""
|
||||
Tests of execution of concatenate operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y)),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=0),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-2),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=None),
|
||||
{
|
||||
"x": {"shape": (3, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z)),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
"z": {"shape": (5, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=0),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
"z": {"shape": (5, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
"z": {"shape": (2, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=-1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
"z": {"shape": (2, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=-2),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
"z": {"shape": (5, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=None),
|
||||
{
|
||||
"x": {"shape": (3, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
"z": {"shape": (5, 1)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y)),
|
||||
{
|
||||
"x": {"shape": (3, 4, 2)},
|
||||
"y": {"shape": (5, 4, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=0),
|
||||
{
|
||||
"x": {"shape": (3, 4, 2)},
|
||||
"y": {"shape": (5, 4, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=1),
|
||||
{
|
||||
"x": {"shape": (2, 4, 5)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=2),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-1),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-2),
|
||||
{
|
||||
"x": {"shape": (2, 4, 5)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-3),
|
||||
{
|
||||
"x": {"shape": (3, 4, 2)},
|
||||
"y": {"shape": (5, 4, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=None),
|
||||
{
|
||||
"x": {"shape": (3, 4, 5)},
|
||||
"y": {"shape": (5, 2, 3)},
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_concatenate(function, parameters, helpers):
|
||||
"""
|
||||
Test concatenate.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
504
frontends/concrete-python/tests/execution/test_convolution.py
Normal file
504
frontends/concrete-python/tests/execution/test_convolution.py
Normal file
@@ -0,0 +1,504 @@
|
||||
"""
|
||||
Tests of execution of convolution operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
import concrete.onnx as connx
|
||||
from concrete.numpy.representation.node import Node
|
||||
from concrete.numpy.tracing.tracer import Tracer
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_shape,weight_shape, group",
|
||||
[
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
(4, 3, 4, 4),
|
||||
(2, 3, 2, 2),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
(1, 6, 4, 4),
|
||||
(6, 1, 2, 2),
|
||||
6,
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"strides",
|
||||
[
|
||||
(2, 2),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"dilations",
|
||||
[
|
||||
(1, 1),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"has_bias",
|
||||
[
|
||||
True,
|
||||
False,
|
||||
],
|
||||
)
|
||||
def test_conv2d(input_shape, weight_shape, group, strides, dilations, has_bias, helpers):
|
||||
"""
|
||||
Test conv2d.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
weight = np.random.randint(0, 4, size=weight_shape)
|
||||
|
||||
if has_bias:
|
||||
bias = np.random.randint(0, 4, size=(weight_shape[0],))
|
||||
else:
|
||||
bias = None
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return connx.conv(x, weight, bias, strides=strides, dilations=dilations, group=group)
|
||||
|
||||
inputset = [np.random.randint(0, 4, size=input_shape) for i in range(100)]
|
||||
circuit = function.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 4, size=input_shape)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_shape,weight_shape,bias_shape,pads,strides,dilations,kernel_shape,group,auto_pad,"
|
||||
"expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"VALID",
|
||||
ValueError,
|
||||
"auto_pad should be in {'NOTSET'}, but got 'VALID'",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 1, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(1, 0, 2, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
RuntimeError,
|
||||
"padding should be the same for the beginning of the dimension and its end, but got "
|
||||
"1 in the beginning, and 2 at the end for dimension 0",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4),
|
||||
(1, 1, 2),
|
||||
(1,),
|
||||
(),
|
||||
(1,),
|
||||
(1,),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"pads should be of form (D_begin_pad, D_end_pad) when performing 1D convolution, "
|
||||
"but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4),
|
||||
(1, 1, 2),
|
||||
(1,),
|
||||
(0, 0),
|
||||
(),
|
||||
(1,),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"strides should be of form (D_stride,) when performing 1D " "convolution, but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4),
|
||||
(1, 1, 2),
|
||||
(1,),
|
||||
(0, 0),
|
||||
(1,),
|
||||
(),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"dilations should be of form (D_dilation,) when performing 1D "
|
||||
"convolution, but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"pads should be of form (height_begin_pad, width_begin_pad, "
|
||||
"height_end_pad, width_end_pad) when performing 2D convolution, "
|
||||
"but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"strides should be of form (height_stride, width_stride) when performing 2D "
|
||||
"convolution, but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"dilations should be of form (height_dilation, width_dilation) when performing 2D "
|
||||
"convolution, but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4, 4),
|
||||
(1, 1, 2, 2, 4),
|
||||
(1,),
|
||||
(),
|
||||
(1, 1, 1),
|
||||
(1, 1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"pads should be of form (D_begin_pad, height_begin_pad, width_begin_pad, "
|
||||
"D_end_pad, height_end_pad, width_end_pad) when performing 3D convolution, "
|
||||
"but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4, 4),
|
||||
(1, 1, 2, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0, 0, 0),
|
||||
(),
|
||||
(1, 1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"strides should be of form (D_stride, height_stride, width_stride) when performing 3D "
|
||||
"convolution, but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4, 4),
|
||||
(1, 1, 2, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0, 0, 0),
|
||||
(1, 1, 1),
|
||||
(),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"dilations should be of form (D_dilation, height_dilation, width_dilation) when "
|
||||
"performing 3D convolution, but it's ()",
|
||||
),
|
||||
pytest.param(
|
||||
(),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected input x to have at least 3 dimensions (N, C, D1, ...), but got 0",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected weight to have at least 3 dimensions (F, C / group, K1, ...), but got 0",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected bias to have a single dimension (F,), but got 0",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
(1, 2),
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected kernel_shape to be (2, 2), but got (1, 2)",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
None,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected group to be an integer > 0, but got None",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4),
|
||||
(1, 2, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected number of channel in weight to be 1.0 (C / group), but got 2",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4),
|
||||
(1, 1, 2),
|
||||
(1,),
|
||||
(0, 0),
|
||||
(1,),
|
||||
(1,),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
NotImplementedError,
|
||||
"conv1d conversion to MLIR is not yet implemented",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4, 4),
|
||||
(1, 1, 2, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0, 0, 0),
|
||||
(1, 1, 1),
|
||||
(1, 1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
NotImplementedError,
|
||||
"conv3d conversion to MLIR is not yet implemented",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 4, 4, 4, 4),
|
||||
(1, 1, 2, 2, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
(1, 1, 1, 1),
|
||||
(1, 1, 1, 1),
|
||||
None,
|
||||
1,
|
||||
"NOTSET",
|
||||
NotImplementedError,
|
||||
"only 1D, 2D, and 3D convolutions are supported",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 2, 4, 4),
|
||||
(1, 1, 2, 2),
|
||||
(1,),
|
||||
(0, 0, 0, 0),
|
||||
(1, 1),
|
||||
(1, 1),
|
||||
None,
|
||||
2,
|
||||
"NOTSET",
|
||||
ValueError,
|
||||
"expected number of feature maps (1) to be a multiple of group (2)",
|
||||
),
|
||||
],
|
||||
)
|
||||
# pylint: disable=too-many-arguments
|
||||
def test_bad_conv_compilation(
|
||||
input_shape,
|
||||
weight_shape,
|
||||
bias_shape,
|
||||
pads,
|
||||
strides,
|
||||
dilations,
|
||||
kernel_shape,
|
||||
group,
|
||||
auto_pad,
|
||||
expected_error,
|
||||
expected_message,
|
||||
helpers,
|
||||
):
|
||||
# pylint: enable=too-many-arguments
|
||||
"""
|
||||
Test conv with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
weight = np.random.randint(0, 4, size=weight_shape)
|
||||
|
||||
if bias_shape is not None:
|
||||
bias = np.random.randint(0, 4, size=bias_shape)
|
||||
else:
|
||||
bias = None
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return connx.conv(
|
||||
x,
|
||||
weight,
|
||||
bias=bias,
|
||||
pads=pads,
|
||||
strides=strides,
|
||||
dilations=dilations,
|
||||
kernel_shape=kernel_shape,
|
||||
group=group,
|
||||
auto_pad=auto_pad,
|
||||
)
|
||||
|
||||
inputset = [np.random.randint(0, 4, size=input_shape) for i in range(100)]
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
function.compile(inputset, configuration)
|
||||
|
||||
# Get the root cause error
|
||||
current_error = excinfo.value
|
||||
cause = current_error.__cause__
|
||||
while cause:
|
||||
current_error = cause
|
||||
cause = current_error.__cause__
|
||||
|
||||
assert str(current_error) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"conv_func_name",
|
||||
[
|
||||
"conv",
|
||||
223,
|
||||
None,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"func",
|
||||
[
|
||||
# pylint: disable=protected-access
|
||||
connx.convolution._evaluate_conv,
|
||||
connx.convolution._trace_conv,
|
||||
# pylint: enable=protected-access
|
||||
],
|
||||
)
|
||||
def test_bad_conv_func_name(conv_func_name, func):
|
||||
"""
|
||||
Test invalid conv function name.
|
||||
"""
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
func(None, None, None, None, None, None, None, conv_func_name)
|
||||
assert (
|
||||
str(excinfo.value) == f"expected conv_func to be one of ['conv1d', 'conv2d', 'conv3d'], "
|
||||
f"but got {conv_func_name}"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x,weight,bias,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
np.array([1, 2, 3]),
|
||||
"not same type as x",
|
||||
None,
|
||||
TypeError,
|
||||
"expected weight to be of same type as x",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([1, 2, 3]),
|
||||
np.array([1, 2, 3]),
|
||||
"not same type as x",
|
||||
TypeError,
|
||||
"expected bias to be of same type as x",
|
||||
),
|
||||
pytest.param(
|
||||
Tracer(Node.constant(np.array([1, 2, 3])), []),
|
||||
"not same type as x",
|
||||
None,
|
||||
TypeError,
|
||||
"expected weight to be of type Tracer or ndarray",
|
||||
),
|
||||
pytest.param(
|
||||
Tracer(Node.constant(np.array([1, 2, 3])), []),
|
||||
np.array([1, 2, 3]),
|
||||
"not same type as x",
|
||||
TypeError,
|
||||
"expected bias to be of type Tracer or ndarray",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_inconsistent_input_types(
|
||||
x,
|
||||
weight,
|
||||
bias,
|
||||
expected_error,
|
||||
expected_message,
|
||||
):
|
||||
"""
|
||||
Test conv with inconsistent input types.
|
||||
"""
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
connx.conv(
|
||||
x,
|
||||
weight,
|
||||
bias=bias,
|
||||
)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
@@ -0,0 +1,323 @@
|
||||
"""
|
||||
Tests of execution of direct table lookup operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def identity_table_lookup_generator(n):
|
||||
"""
|
||||
Get identity table lookup function.
|
||||
"""
|
||||
|
||||
return lambda x: cnp.LookupTable(range(2**n))[x]
|
||||
|
||||
|
||||
def random_table_lookup_1b(x):
|
||||
"""
|
||||
Lookup on a random table with 1-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable([10, 12])
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def random_table_lookup_2b(x):
|
||||
"""
|
||||
Lookup on a random table with 2-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable([3, 8, 22, 127])
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def random_table_lookup_3b(x):
|
||||
"""
|
||||
Lookup on a random table with 3-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable([30, 52, 125, 23, 17, 12, 90, 4])
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def random_table_lookup_4b(x):
|
||||
"""
|
||||
Lookup on a random table with 4-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable([30, 52, 125, 23, 17, 12, 90, 4, 21, 51, 22, 15, 53, 100, 75, 90])
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def random_table_lookup_5b(x):
|
||||
"""
|
||||
Lookup on a random table with 5-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable(
|
||||
[
|
||||
1, 5, 2, 3, 10, 2, 4, 8, 1, 12, 15, 12, 10, 1, 0, 2,
|
||||
4, 3, 8, 7, 10, 11, 6, 13, 9, 0, 2, 1, 15, 11, 12, 5
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def random_table_lookup_6b(x):
|
||||
"""
|
||||
Lookup on a random table with 6-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable(
|
||||
[
|
||||
95, 74, 11, 83, 24, 116, 28, 75, 26, 85, 114, 121, 91, 123, 78, 69,
|
||||
72, 115, 67, 5, 39, 11, 120, 88, 56, 43, 74, 16, 72, 85, 103, 92,
|
||||
44, 115, 50, 56, 107, 77, 25, 71, 52, 45, 80, 35, 69, 8, 40, 87,
|
||||
26, 85, 84, 53, 73, 95, 86, 22, 16, 45, 59, 112, 53, 113, 98, 116
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def random_table_lookup_7b(x):
|
||||
"""
|
||||
Lookup on a random table with 7-bit input.
|
||||
"""
|
||||
|
||||
# fmt: off
|
||||
table = cnp.LookupTable(
|
||||
[
|
||||
13, 58, 38, 58, 15, 15, 77, 86, 80, 94, 108, 27, 126, 60, 65, 95,
|
||||
50, 79, 22, 97, 38, 60, 25, 48, 73, 112, 27, 45, 88, 20, 67, 17,
|
||||
16, 6, 71, 60, 77, 43, 93, 40, 41, 31, 99, 122, 120, 40, 94, 13,
|
||||
111, 44, 96, 62, 108, 91, 34, 90, 103, 58, 3, 103, 19, 69, 55, 108,
|
||||
0, 111, 113, 0, 0, 73, 22, 52, 81, 2, 88, 76, 36, 121, 97, 121,
|
||||
123, 79, 82, 120, 12, 65, 54, 101, 90, 52, 84, 106, 23, 15, 110, 79,
|
||||
85, 101, 30, 61, 104, 35, 81, 30, 98, 44, 111, 32, 68, 18, 45, 123,
|
||||
84, 80, 68, 27, 31, 38, 126, 61, 51, 7, 49, 37, 63, 114, 22, 18,
|
||||
]
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
return table[x]
|
||||
|
||||
|
||||
def negative_identity_table_lookup_generator(n):
|
||||
"""
|
||||
Get negative identity table lookup function.
|
||||
"""
|
||||
|
||||
return lambda x: cnp.LookupTable([-i for i in range(2**n)])[x]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"bits,function",
|
||||
[
|
||||
pytest.param(1, identity_table_lookup_generator(1)),
|
||||
pytest.param(2, identity_table_lookup_generator(2)),
|
||||
pytest.param(3, identity_table_lookup_generator(3)),
|
||||
pytest.param(4, identity_table_lookup_generator(4)),
|
||||
pytest.param(5, identity_table_lookup_generator(5)),
|
||||
pytest.param(6, identity_table_lookup_generator(6)),
|
||||
pytest.param(7, identity_table_lookup_generator(7)),
|
||||
pytest.param(1, random_table_lookup_1b),
|
||||
pytest.param(2, random_table_lookup_2b),
|
||||
pytest.param(3, random_table_lookup_3b),
|
||||
pytest.param(4, random_table_lookup_4b),
|
||||
pytest.param(5, random_table_lookup_5b),
|
||||
pytest.param(6, random_table_lookup_6b),
|
||||
pytest.param(7, random_table_lookup_7b),
|
||||
pytest.param(1, negative_identity_table_lookup_generator(1)),
|
||||
pytest.param(2, negative_identity_table_lookup_generator(2)),
|
||||
pytest.param(3, negative_identity_table_lookup_generator(3)),
|
||||
pytest.param(4, negative_identity_table_lookup_generator(4)),
|
||||
pytest.param(5, negative_identity_table_lookup_generator(5)),
|
||||
pytest.param(6, negative_identity_table_lookup_generator(6)),
|
||||
],
|
||||
)
|
||||
def test_direct_table_lookup(bits, function, helpers):
|
||||
"""
|
||||
Test direct table lookup.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# scalar
|
||||
# ------
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = range(2**bits)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = int(np.random.randint(0, 2**bits))
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
# tensor
|
||||
# ------
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**bits, size=(3, 2)) for _ in range(100)]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**bits, size=(3, 2))
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
# negative scalar
|
||||
# ---------------
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = range(-(2 ** (bits - 1)), 2 ** (bits - 1))
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = int(np.random.randint(-(2 ** (bits - 1)), 2 ** (bits - 1)))
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
# negative tensor
|
||||
# ---------------
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [
|
||||
np.random.randint(-(2 ** (bits - 1)), 2 ** (bits - 1), size=(3, 2)) for _ in range(100)
|
||||
]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(-(2 ** (bits - 1)), 2 ** (bits - 1), size=(3, 2))
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
def test_direct_multi_table_lookup(helpers):
|
||||
"""
|
||||
Test direct multi table lookup.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
square = cnp.LookupTable([i * i for i in range(4)])
|
||||
cube = cnp.LookupTable([i * i * i for i in range(4)])
|
||||
|
||||
table = cnp.LookupTable(
|
||||
[
|
||||
[square, cube],
|
||||
[cube, square],
|
||||
[square, cube],
|
||||
]
|
||||
)
|
||||
|
||||
def function(x):
|
||||
return table[x]
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**2, size=(3, 2)) for _ in range(100)]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**2, size=(3, 2))
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
def test_bad_direct_table_lookup(helpers):
|
||||
"""
|
||||
Test direct table lookup with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# empty table
|
||||
# -----------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
cnp.LookupTable([])
|
||||
|
||||
assert str(excinfo.value) == "LookupTable cannot be constructed with []"
|
||||
|
||||
# invalid table
|
||||
# -------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
cnp.LookupTable([[0, 1], [2, 3]])
|
||||
|
||||
assert str(excinfo.value) == "LookupTable cannot be constructed with [[0, 1], [2, 3]]"
|
||||
|
||||
# invalid multi table
|
||||
# -------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
cnp.LookupTable(["abc", 3.2])
|
||||
|
||||
assert str(excinfo.value) == "LookupTable cannot be constructed with ['abc', 3.2]"
|
||||
|
||||
# simulation with float value
|
||||
# ---------------------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
random_table_lookup_3b(1.1)
|
||||
|
||||
assert str(excinfo.value) == "LookupTable cannot be looked up with 1.1"
|
||||
|
||||
# simulation with invalid shape
|
||||
# -----------------------------
|
||||
|
||||
square = cnp.LookupTable([i * i for i in range(4)])
|
||||
cube = cnp.LookupTable([i * i * i for i in range(4)])
|
||||
|
||||
table = cnp.LookupTable(
|
||||
[
|
||||
[square, cube],
|
||||
[cube, square],
|
||||
[square, cube],
|
||||
]
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
_ = table[np.array([1, 2])]
|
||||
|
||||
assert str(excinfo.value) == "LookupTable of shape (3, 2) cannot be looked up with [1 2]"
|
||||
|
||||
# compilation with float value
|
||||
# ----------------------------
|
||||
|
||||
compiler = cnp.Compiler(random_table_lookup_3b, {"x": "encrypted"})
|
||||
|
||||
inputset = [1.5]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == "LookupTable cannot be looked up with EncryptedScalar<float64>"
|
||||
|
||||
# compilation with invalid shape
|
||||
# ------------------------------
|
||||
|
||||
compiler = cnp.Compiler(lambda x: table[x], {"x": "encrypted"})
|
||||
|
||||
inputset = [10, 5, 6, 2]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"LookupTable of shape (3, 2) cannot be looked up with EncryptedScalar<uint4>"
|
||||
)
|
||||
47
frontends/concrete-python/tests/execution/test_dot.py
Normal file
47
frontends/concrete-python/tests/execution/test_dot.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Tests of execution of dot operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"size",
|
||||
[1, 4, 6, 10],
|
||||
)
|
||||
def test_dot(size, helpers):
|
||||
"""
|
||||
Test dot.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
bound = int(np.floor(np.sqrt(127 / size)))
|
||||
cst = np.random.randint(0, bound, size=(size,))
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def left_function(x):
|
||||
return np.dot(x, cst)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def right_function(x):
|
||||
return np.dot(cst, x)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def method(x):
|
||||
return x.dot(cst)
|
||||
|
||||
inputset = [np.random.randint(0, bound, size=(size,)) for i in range(100)]
|
||||
|
||||
left_function_circuit = left_function.compile(inputset, configuration)
|
||||
right_function_circuit = right_function.compile(inputset, configuration)
|
||||
method_circuit = method.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, bound, size=(size,))
|
||||
|
||||
helpers.check_execution(left_function_circuit, left_function, sample)
|
||||
helpers.check_execution(right_function_circuit, right_function, sample)
|
||||
helpers.check_execution(method_circuit, method, sample)
|
||||
30
frontends/concrete-python/tests/execution/test_iter.py
Normal file
30
frontends/concrete-python/tests/execution/test_iter.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Tests of execution of iteration of tracer.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shape", [(3,), (3, 2), (3, 2, 4)])
|
||||
def test_iter(shape, helpers):
|
||||
"""
|
||||
Test iteration of tracers.
|
||||
"""
|
||||
|
||||
def function(x):
|
||||
result = cnp.zeros(x.shape[1:])
|
||||
for value in x:
|
||||
result += value
|
||||
return result
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**2, size=shape) for _ in range(100)]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**2, size=shape)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
158
frontends/concrete-python/tests/execution/test_matmul.py
Normal file
158
frontends/concrete-python/tests/execution/test_matmul.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
Tests of execution of matmul operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs_shape,rhs_shape,bounds",
|
||||
[
|
||||
pytest.param(
|
||||
(3, 2),
|
||||
(2, 3),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(1, 2),
|
||||
(2, 1),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 3),
|
||||
(3, 3),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 1),
|
||||
(1, 2),
|
||||
(0, 7),
|
||||
),
|
||||
pytest.param(
|
||||
(2,),
|
||||
(2,),
|
||||
(0, 7),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 5),
|
||||
(5,),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(5, 5),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(5, 3),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 3),
|
||||
(3,),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(4, 5, 3),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(4, 5, 3),
|
||||
(3,),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(2, 4, 5, 3),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 4, 5, 3),
|
||||
(3,),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 4, 3),
|
||||
(3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(4, 3),
|
||||
(5, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 5, 4, 3),
|
||||
(3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 4, 3),
|
||||
(1, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(1, 4, 3),
|
||||
(5, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 4, 3),
|
||||
(2, 1, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 1, 4, 3),
|
||||
(5, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_matmul(lhs_shape, rhs_shape, bounds, helpers):
|
||||
"""
|
||||
Test matmul.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
minimum, maximum = bounds
|
||||
|
||||
lhs_cst = list(np.random.randint(minimum, maximum, size=lhs_shape))
|
||||
rhs_cst = list(np.random.randint(minimum, maximum, size=rhs_shape))
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def lhs_operator(x):
|
||||
return x @ rhs_cst
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def rhs_operator(x):
|
||||
return lhs_cst @ x
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def lhs_function(x):
|
||||
return np.matmul(x, rhs_cst)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def rhs_function(x):
|
||||
return np.matmul(lhs_cst, x)
|
||||
|
||||
lhs_inputset = [np.random.randint(minimum, maximum, size=lhs_shape) for i in range(100)]
|
||||
rhs_inputset = [np.random.randint(minimum, maximum, size=rhs_shape) for i in range(100)]
|
||||
|
||||
lhs_operator_circuit = lhs_operator.compile(lhs_inputset, configuration)
|
||||
rhs_operator_circuit = rhs_operator.compile(rhs_inputset, configuration)
|
||||
lhs_function_circuit = lhs_function.compile(lhs_inputset, configuration)
|
||||
rhs_function_circuit = rhs_function.compile(rhs_inputset, configuration)
|
||||
|
||||
lhs_sample = np.random.randint(minimum, maximum, size=lhs_shape)
|
||||
rhs_sample = np.random.randint(minimum, maximum, size=rhs_shape)
|
||||
|
||||
helpers.check_execution(lhs_operator_circuit, lhs_operator, lhs_sample)
|
||||
helpers.check_execution(rhs_operator_circuit, rhs_operator, rhs_sample)
|
||||
helpers.check_execution(lhs_function_circuit, lhs_function, lhs_sample)
|
||||
helpers.check_execution(rhs_function_circuit, rhs_function, rhs_sample)
|
||||
393
frontends/concrete-python/tests/execution/test_maxpool.py
Normal file
393
frontends/concrete-python/tests/execution/test_maxpool.py
Normal file
@@ -0,0 +1,393 @@
|
||||
"""
|
||||
Tests of execution of maxpool operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
import concrete.onnx as connx
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"operation,sample_input,expected_output",
|
||||
[
|
||||
pytest.param(
|
||||
{"kernel_shape": (3,)},
|
||||
[1, 2, 2, 3, 2, 2, 2, 4, 1, 5, 2, 6],
|
||||
[2, 3, 3, 3, 2, 4, 4, 5, 5, 6],
|
||||
),
|
||||
pytest.param(
|
||||
{"kernel_shape": (3,), "strides": (2,)},
|
||||
[1, 2, 2, 3, 2, 2, 2, 4, 1, 5, 2, 6, 7],
|
||||
[2, 3, 2, 4, 5, 7],
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"kernel_shape": (2, 2),
|
||||
},
|
||||
[
|
||||
[3, 1, 2],
|
||||
[1, 1, 1],
|
||||
[2, 3, 4],
|
||||
[4, 1, 2],
|
||||
],
|
||||
[
|
||||
[3, 2],
|
||||
[3, 4],
|
||||
[4, 4],
|
||||
],
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"kernel_shape": (2, 2),
|
||||
"strides": (2, 1),
|
||||
},
|
||||
[
|
||||
[3, 1, 2],
|
||||
[1, 1, 1],
|
||||
[2, 3, 4],
|
||||
[4, 1, 2],
|
||||
],
|
||||
[
|
||||
[3, 2],
|
||||
[4, 4],
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_maxpool(
|
||||
operation,
|
||||
sample_input,
|
||||
expected_output,
|
||||
helpers,
|
||||
):
|
||||
"""
|
||||
Test maxpool.
|
||||
"""
|
||||
|
||||
sample_input = np.expand_dims(np.array(sample_input), axis=(0, 1))
|
||||
expected_output = np.expand_dims(np.array(expected_output), axis=(0, 1))
|
||||
|
||||
assert np.array_equal(connx.maxpool(sample_input, **operation), expected_output)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return connx.maxpool(x, **operation)
|
||||
|
||||
graph = function.trace([sample_input], helpers.configuration())
|
||||
assert np.array_equal(graph(sample_input), expected_output)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_shape,operation,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
(10, 10),
|
||||
{
|
||||
"kernel_shape": (),
|
||||
},
|
||||
ValueError,
|
||||
"Expected input to have at least 3 dimensions (N, C, D1, ...) but it only has 2",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4, 3, 2),
|
||||
{
|
||||
"kernel_shape": (),
|
||||
},
|
||||
NotImplementedError,
|
||||
"4D maximum pooling is not supported yet",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": "",
|
||||
},
|
||||
TypeError,
|
||||
"Expected kernel_shape to be a tuple or a list but it's str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": ["0"],
|
||||
},
|
||||
TypeError,
|
||||
"Expected kernel_shape to consist of integers but it has an element of type str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (3,),
|
||||
},
|
||||
ValueError,
|
||||
"Expected kernel_shape to have 2 elements but it has 1",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"strides": "",
|
||||
},
|
||||
TypeError,
|
||||
"Expected strides to be a tuple or a list but it's str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"strides": ["0"],
|
||||
},
|
||||
TypeError,
|
||||
"Expected strides to consist of integers but it has an element of type str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"strides": (3,),
|
||||
},
|
||||
ValueError,
|
||||
"Expected strides to have 2 elements but it has 1",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"auto_pad": True,
|
||||
},
|
||||
TypeError,
|
||||
"Expected auto_pad to be of type str but it's bool",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"auto_pad": "YES_PLEASE",
|
||||
},
|
||||
ValueError,
|
||||
"Expected auto_pad to be one of NOTSET, SAME_LOWER, SAME_UPPER, VALID "
|
||||
"but it's YES_PLEASE",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"auto_pad": "VALID",
|
||||
},
|
||||
NotImplementedError,
|
||||
"Desired auto_pad of VALID is not supported yet",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"pads": "",
|
||||
},
|
||||
TypeError,
|
||||
"Expected pads to be a tuple or a list but it's str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"pads": ["0"],
|
||||
},
|
||||
TypeError,
|
||||
"Expected pads to consist of integers but it has an element of type str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"pads": (3,),
|
||||
},
|
||||
ValueError,
|
||||
"Expected pads to have 4 elements but it has 1",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"pads": (1, 1, 2, 2),
|
||||
},
|
||||
NotImplementedError,
|
||||
"Desired pads of (1, 1, 2, 2) is not supported yet because of uneven padding",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"dilations": "",
|
||||
},
|
||||
TypeError,
|
||||
"Expected dilations to be a tuple or a list but it's str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"dilations": ["0"],
|
||||
},
|
||||
TypeError,
|
||||
"Expected dilations to consist of integers but it has an element of type str",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"dilations": (3,),
|
||||
},
|
||||
ValueError,
|
||||
"Expected dilations to have 2 elements but it has 1",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"ceil_mode": None,
|
||||
},
|
||||
TypeError,
|
||||
"Expected ceil_mode to be of type int but it's NoneType",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"ceil_mode": 10,
|
||||
},
|
||||
ValueError,
|
||||
"Expected ceil_mode to be one of 0, 1 but it's 10",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"ceil_mode": 1,
|
||||
},
|
||||
NotImplementedError,
|
||||
"Desired ceil_mode of 1 is not supported yet",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"storage_order": None,
|
||||
},
|
||||
TypeError,
|
||||
"Expected storage_order to be of type int but it's NoneType",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"storage_order": 10,
|
||||
},
|
||||
ValueError,
|
||||
"Expected storage_order to be one of 0, 1 but it's 10",
|
||||
),
|
||||
pytest.param(
|
||||
(1, 1, 5, 4),
|
||||
{
|
||||
"kernel_shape": (2, 3),
|
||||
"storage_order": 1,
|
||||
},
|
||||
NotImplementedError,
|
||||
"Desired storage_order of 1 is not supported yet",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_bad_maxpool(
|
||||
input_shape,
|
||||
operation,
|
||||
expected_error,
|
||||
expected_message,
|
||||
helpers,
|
||||
):
|
||||
"""
|
||||
Test maxpool with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
connx.maxpool(np.random.randint(0, 10, size=input_shape), **operation)
|
||||
|
||||
helpers.check_str(expected_message, str(excinfo.value))
|
||||
|
||||
|
||||
def test_bad_maxpool_special(helpers):
|
||||
"""
|
||||
Test maxpool with bad parameters for special cases.
|
||||
"""
|
||||
|
||||
# compile
|
||||
# -------
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def not_compilable(x):
|
||||
return connx.maxpool(x, kernel_shape=(4, 3))
|
||||
|
||||
inputset = [np.random.randint(0, 10, size=(1, 1, 10, 10)) for i in range(100)]
|
||||
with pytest.raises(NotImplementedError) as excinfo:
|
||||
not_compilable.compile(inputset, helpers.configuration())
|
||||
|
||||
helpers.check_str("MaxPool operation cannot be compiled yet", str(excinfo.value))
|
||||
|
||||
# clear input
|
||||
# -----------
|
||||
|
||||
@cnp.compiler({"x": "clear"})
|
||||
def clear_input(x):
|
||||
return connx.maxpool(x, kernel_shape=(4, 3, 2))
|
||||
|
||||
inputset = [np.zeros((1, 1, 10, 10, 10), dtype=np.int64)]
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
clear_input.compile(inputset, helpers.configuration())
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint1, shape=(1, 1, 10, 10, 10)> ∈ [0, 0]
|
||||
%1 = maxpool(%0, kernel_shape=(4, 3, 2), strides=(1, 1, 1), pads=(0, 0, 0, 0, 0, 0), dilations=(1, 1, 1), ceil_mode=False) # ClearTensor<uint1, shape=(1, 1, 7, 8, 9)> ∈ [0, 0]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted maxpool is supported
|
||||
return %1
|
||||
|
||||
""".strip(), # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
|
||||
# badly typed ndarray input
|
||||
# -------------------------
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
connx.maxpool(np.array([{}, None]), ())
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
Expected input elements to be of type np.integer, np.floating, or np.bool_ but it's dtype[object_]
|
||||
|
||||
""".strip(), # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
|
||||
# badly typed input
|
||||
# -----------------
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
connx.maxpool("", ())
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
Expected input to be of type np.ndarray or Tracer but it's str
|
||||
|
||||
""".strip(), # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
76
frontends/concrete-python/tests/execution/test_mul.py
Normal file
76
frontends/concrete-python/tests/execution/test_mul.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Tests of execution of mul operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x * 3,
|
||||
id="x * 3",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 3 * x,
|
||||
id="3 * x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.dot(x, 3),
|
||||
id="np.dot(x, 3)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.dot(3, x),
|
||||
id="np.dot(3, x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x * np.array([1, 2, 3]),
|
||||
id="x * [1, 2, 3]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([1, 2, 3]) * x,
|
||||
id="[1, 2, 3] * x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x * np.array([[1, 2, 3], [3, 1, 2]]),
|
||||
id="x * [[1, 2, 3], [3, 1, 2]]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([[1, 2, 3], [3, 1, 2]]) * x,
|
||||
id="[[1, 2, 3], [3, 1, 2]] * x",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 40], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 40], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 40], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_constant_mul(function, parameters, helpers):
|
||||
"""
|
||||
Test mul where one of the operators is a constant.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
52
frontends/concrete-python/tests/execution/test_neg.py
Normal file
52
frontends/concrete-python/tests/execution/test_neg.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Tests of execution of neg operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 64], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 64], "status": "encrypted", "shape": (3, 2)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-63, 0], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [-63, 0], "status": "encrypted", "shape": (3, 2)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_neg(parameters, helpers):
|
||||
"""
|
||||
Test neg.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@cnp.compiler(parameter_encryption_statuses)
|
||||
def operator(x):
|
||||
return -x
|
||||
|
||||
@cnp.compiler(parameter_encryption_statuses)
|
||||
def function(x):
|
||||
return np.negative(x)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
|
||||
operator_circuit = operator.compile(inputset, configuration)
|
||||
function_circuit = function.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
|
||||
helpers.check_execution(operator_circuit, operator, sample)
|
||||
helpers.check_execution(function_circuit, function, sample)
|
||||
49
frontends/concrete-python/tests/execution/test_ones.py
Normal file
49
frontends/concrete-python/tests/execution/test_ones.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Tests of execution of ones operation.
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: cnp.one() + x,
|
||||
id="cnp.one() + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.ones(()) + x,
|
||||
id="cnp.ones(()) + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.ones(10) + x,
|
||||
id="cnp.ones(10) + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.ones((10,)) + x,
|
||||
id="cnp.ones((10,)) + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.ones((3, 2)) + x,
|
||||
id="cnp.ones((3, 2)) + x",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_ones(function, helpers):
|
||||
"""
|
||||
Test ones.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = range(10)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = random.randint(0, 11)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
902
frontends/concrete-python/tests/execution/test_others.py
Normal file
902
frontends/concrete-python/tests/execution/test_others.py
Normal file
@@ -0,0 +1,902 @@
|
||||
"""
|
||||
Tests of execution of operations converted to table lookups.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def fusable_with_bigger_search(x, y):
|
||||
"""
|
||||
Fusable function that requires a single iteration for fusing.
|
||||
"""
|
||||
|
||||
x = x + 1
|
||||
|
||||
x_1 = x.astype(np.int64)
|
||||
x_1 = x_1 + 1.5
|
||||
|
||||
x_2 = x.astype(np.int64)
|
||||
x_2 = x_2 + 3.4
|
||||
|
||||
add = x_1 + x_2
|
||||
add_int = add.astype(np.int64)
|
||||
|
||||
return add_int + y
|
||||
|
||||
|
||||
def fusable_with_bigger_search_needs_second_iteration(x, y):
|
||||
"""
|
||||
Fusable function that requires more than one iteration for fusing.
|
||||
"""
|
||||
|
||||
x = x + 1
|
||||
x = x + 0.5
|
||||
x = np.cos(x)
|
||||
|
||||
x_1 = x.astype(np.int64)
|
||||
x_1 = x_1 + 1.5
|
||||
|
||||
x_p = x + 1
|
||||
x_p2 = x_p + 1
|
||||
|
||||
x_2 = (x_p + x_p2).astype(np.int64)
|
||||
x_2 = x_2 + 3.4
|
||||
|
||||
add = x_1 + x_2
|
||||
add_int = add.astype(np.int64)
|
||||
|
||||
return add_int + y
|
||||
|
||||
|
||||
def fusable_with_one_of_the_start_nodes_is_lca_generator():
|
||||
"""
|
||||
Generator of a fusable function that has one of the start nodes as lca.
|
||||
"""
|
||||
|
||||
# pylint: disable=invalid-name,too-many-locals,too-many-statements
|
||||
|
||||
def subgraph_18(x):
|
||||
t0 = 0
|
||||
t1 = 3
|
||||
t2 = 2
|
||||
t3 = 2.4688520431518555
|
||||
t4 = 2.4688520431518555
|
||||
t5 = x
|
||||
t6 = np.multiply(t4, t5)
|
||||
t7 = np.true_divide(t6, t3)
|
||||
t8 = np.add(t7, t2)
|
||||
t9 = np.rint(t8)
|
||||
t10 = np.clip(t9, t0, t1)
|
||||
t11 = t10.astype(np.int64)
|
||||
return t11
|
||||
|
||||
def subgraph_24(x):
|
||||
t0 = 0
|
||||
t1 = [0.15588106, -0.01305565]
|
||||
t2 = 1.3664466152828822
|
||||
t3 = [[4, -4]]
|
||||
t4 = 0
|
||||
t5 = x
|
||||
t6 = t5.astype(np.float32)
|
||||
t7 = np.add(t6, t4)
|
||||
t8 = np.add(t7, t3)
|
||||
t9 = np.multiply(t2, t8)
|
||||
t10 = np.add(t1, t9)
|
||||
t11 = np.greater(t10, t0)
|
||||
return t11
|
||||
|
||||
cst0 = np.random.randint(-2, 2, size=(10, 2))
|
||||
cst1 = np.random.randint(0, 2, size=(10, 1))
|
||||
|
||||
def function(x):
|
||||
t0 = 0
|
||||
t1 = 3
|
||||
t2 = 1
|
||||
t3 = 1.2921873902965313
|
||||
t4 = 1.0507009873554805
|
||||
t5 = 1
|
||||
t6 = 1.7580993408473766
|
||||
t7 = [0.15588106, -0.01305565]
|
||||
t8 = 1
|
||||
t9 = 1.3664466152828822
|
||||
t10 = [[4, -4]]
|
||||
t11 = 0
|
||||
t12 = cst0
|
||||
t13 = 0
|
||||
t14 = cst1
|
||||
t15 = x
|
||||
t16 = -2
|
||||
t17 = np.add(t15, t16)
|
||||
t18 = subgraph_18(t17)
|
||||
t19 = np.matmul(t18, t12)
|
||||
t20 = np.matmul(t18, t14)
|
||||
t21 = np.multiply(t13, t20)
|
||||
t22 = np.add(t19, t21)
|
||||
t23 = t22.astype(np.float32)
|
||||
t24 = subgraph_24(t22)
|
||||
t25 = np.add(t23, t11)
|
||||
t26 = np.subtract(t5, t24)
|
||||
t27 = np.add(t25, t10)
|
||||
t28 = np.multiply(t9, t27)
|
||||
t29 = np.add(t7, t28)
|
||||
t30 = np.multiply(t4, t29)
|
||||
t31 = np.exp(t29)
|
||||
t32 = np.multiply(t24, t30)
|
||||
t33 = np.subtract(t31, t8)
|
||||
t34 = np.multiply(t6, t33)
|
||||
t35 = np.multiply(t26, t34)
|
||||
t36 = np.add(t32, t35)
|
||||
t37 = np.true_divide(t36, t3)
|
||||
t38 = np.add(t37, t2)
|
||||
t39 = np.rint(t38)
|
||||
t40 = np.clip(t39, t0, t1)
|
||||
t41 = t40.astype(np.int64)
|
||||
return t41
|
||||
|
||||
return function
|
||||
|
||||
# pylint: enable=invalid-name,too-many-locals,too-many-statements
|
||||
|
||||
|
||||
def fusable_with_hard_to_find_lca(x):
|
||||
"""
|
||||
Fusable function that requires harder lca search.
|
||||
"""
|
||||
|
||||
a = x * 3
|
||||
b = x // 3
|
||||
c = a + b
|
||||
return ((np.sin(a) ** 2) + (np.cos(c) ** 2)).round().astype(np.int64)
|
||||
|
||||
|
||||
def fusable_with_hard_to_find_lca_used_twice(x):
|
||||
"""
|
||||
Fusable function that uses `fusable_with_hard_to_find_lca` twice.
|
||||
"""
|
||||
|
||||
a = x @ np.array([[3, 1], [4, 2]])
|
||||
b = x @ np.array([[1, 2], [3, 4]])
|
||||
|
||||
a = fusable_with_hard_to_find_lca(a)
|
||||
b = fusable_with_hard_to_find_lca(b)
|
||||
|
||||
return a + b
|
||||
|
||||
|
||||
def fusable_additional_1(x):
|
||||
"""
|
||||
Another fusable function for additional safety.
|
||||
"""
|
||||
|
||||
a = x.astype(np.float64) * 3.0
|
||||
b = x + 1
|
||||
c = a.astype(np.int64)
|
||||
return (a + b + c).astype(np.int64)
|
||||
|
||||
|
||||
def fusable_additional_2(x):
|
||||
"""
|
||||
Another fusable function for additional safety.
|
||||
"""
|
||||
|
||||
a = x.astype(np.float64) / 3.0
|
||||
b = x + 1
|
||||
c = a * a
|
||||
return (a + b + c).astype(np.int64)
|
||||
|
||||
|
||||
def deterministic_unary_function(x):
|
||||
"""
|
||||
An example deterministic unary function.
|
||||
"""
|
||||
|
||||
def per_element(element):
|
||||
result = 0
|
||||
for i in range(element):
|
||||
result += i
|
||||
return result
|
||||
|
||||
return np.vectorize(per_element)(x)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x // 3,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127]},
|
||||
},
|
||||
id="x // 3",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 127 // x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [1, 127]},
|
||||
},
|
||||
id="127 // x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: (x / 3).astype(np.int64),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127]},
|
||||
},
|
||||
id="(x / 3).astype(np.int64)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: (127 / x).astype(np.int64),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [1, 127]},
|
||||
},
|
||||
id="(127 / x).astype(np.int64)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x**2,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 11]},
|
||||
},
|
||||
id="x ** 2",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 2**x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 6]},
|
||||
},
|
||||
id="2 ** x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x % 10,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127]},
|
||||
},
|
||||
id="x % 10",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 121 % x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [1, 127]},
|
||||
},
|
||||
id="121 % x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: +x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127]},
|
||||
},
|
||||
id="+x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: abs(42 - x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 84]},
|
||||
},
|
||||
id="abs(64 - x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: ~x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="~x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x & 10,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="x & 10",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 5 & x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="5 & x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x | 6,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="x | 6",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 11 | x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="11 | x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x ^ 9,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="x ^ 9",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 13 ^ x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="13 ^ x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x << 2,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="x << 2",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 2 << x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 5]},
|
||||
},
|
||||
id="2 << x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x >> 2,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 120]},
|
||||
},
|
||||
id="x >> 2",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 120 >> x,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 16]},
|
||||
},
|
||||
id="120 >> x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x > 50,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="x > 50",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 50 > x, # pylint: disable=misplaced-comparison-constant
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="50 > x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x < 50,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="x < 50",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 50 < x, # pylint: disable=misplaced-comparison-constant
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="50 < x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x >= 50,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="x >= 50",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 50 >= x, # pylint: disable=misplaced-comparison-constant
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="50 >= x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x <= 50,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="x <= 50",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 50 <= x, # pylint: disable=misplaced-comparison-constant
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="50 <= x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x == 50,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="x == 50",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 50 == x, # pylint: disable=misplaced-comparison-constant
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="50 == x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x != 50,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="x != 50",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 50 != x, # pylint: disable=misplaced-comparison-constant
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="50 != x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.clip(5, 10),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 15]},
|
||||
},
|
||||
id="x.clip(5, 10)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: (60 * np.sin(x)).astype(np.int64) + 60,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127]},
|
||||
},
|
||||
id="(60 * np.sin(x)).astype(np.int64) + 60",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: ((np.sin(x) ** 2) + (np.cos(x) ** 2)).astype(np.int64),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127]},
|
||||
},
|
||||
id="((np.sin(x) ** 2) + (np.cos(x) ** 2)).astype(np.int64)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.maximum(x, [[10, 20], [30, 40], [50, 60]]),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 127], "shape": (3, 2)},
|
||||
},
|
||||
id="np.maximum(x, [[10, 20], [30, 40], [50, 60]])",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_with_bigger_search,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [5, 10]},
|
||||
"y": {"status": "encrypted", "range": [5, 10]},
|
||||
},
|
||||
id="fusable_with_bigger_search",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_with_bigger_search_needs_second_iteration,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [5, 10]},
|
||||
"y": {"status": "encrypted", "range": [5, 10]},
|
||||
},
|
||||
id="fusable_with_bigger_search_needs_second_iteration",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_with_one_of_the_start_nodes_is_lca_generator(),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 4], "shape": (1, 10)},
|
||||
},
|
||||
id="fusable_with_one_of_the_start_nodes_is_lca",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_with_hard_to_find_lca,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="fusable_with_hard_to_find_lca",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_with_hard_to_find_lca_used_twice,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 4], "shape": (2, 2)},
|
||||
},
|
||||
id="fusable_with_hard_to_find_lca_used_twice",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_additional_1,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="fusable_additional_1",
|
||||
),
|
||||
pytest.param(
|
||||
fusable_additional_2,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="fusable_additional_2",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + x.shape[0] + x.ndim + x.size,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 15], "shape": (3, 2)},
|
||||
},
|
||||
id="x + x.shape[0] + x.ndim + x.size",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: (50 * np.sin(x.transpose())).astype(np.int64),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 15], "shape": (3, 2)},
|
||||
},
|
||||
id="(50 * np.sin(x.transpose())).astype(np.int64)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.where(x < 5, x * 3, x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="np.where(x < 5, x * 3, x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + np.ones_like(x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="x + np.ones_like(x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + np.zeros_like(x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="x + np.zeros_like(x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.univariate(deterministic_unary_function)(x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 10]},
|
||||
},
|
||||
id="cnp.univariate(deterministic_unary_function)(x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: round(np.sqrt(x)),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100], "shape": ()},
|
||||
},
|
||||
id="round(np.sqrt(x))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sqrt(x).round().astype(np.int64),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100]},
|
||||
},
|
||||
id="np.sqrt(x).round().astype(np.int64)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: (2.5 * round(np.sqrt(x), ndigits=4)).astype(np.int64),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [0, 100], "shape": ()},
|
||||
},
|
||||
id="(2.5 * round(np.sqrt(x), decimals=4)).astype(np.int64)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: cnp.LookupTable(list(range(32)))[x + y],
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10]},
|
||||
"y": {"status": "encrypted", "range": [-10, 10]},
|
||||
},
|
||||
id="cnp.LookupTable(list(range(32)))[x + y]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=0),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=0)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=1),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=1)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=2),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=2)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=(0, 1)),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=(0, 1))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=(0, 2)),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=(0, 2))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=(1, 2)),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=(1, 2))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.expand_dims(x, axis=(0, 1, 2)),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (3, 2)},
|
||||
},
|
||||
id="np.expand_dims(x, axis=(0, 1, 2))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x**3,
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-30, 30]},
|
||||
},
|
||||
id="x ** 3",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.squeeze(x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (1, 2, 1, 3, 1, 4)},
|
||||
},
|
||||
id="np.squeeze(x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.squeeze(x, axis=2),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (1, 2, 1, 3, 1, 4)},
|
||||
},
|
||||
id="np.squeeze(x, axis=2)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.squeeze(x, axis=(0, 4)),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (1, 2, 1, 3, 1, 4)},
|
||||
},
|
||||
id="np.squeeze(x, axis=(0, 4))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.squeeze(x),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (1, 1, 1)},
|
||||
},
|
||||
id="np.squeeze(x) where x.shape == (1, 1, 1)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.squeeze(x, axis=1),
|
||||
{
|
||||
"x": {"status": "encrypted", "range": [-10, 10], "shape": (1, 1, 1)},
|
||||
},
|
||||
id="np.squeeze(x, axis=1) where x.shape == (1, 1, 1)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_others(function, parameters, helpers):
|
||||
"""
|
||||
Test others.
|
||||
"""
|
||||
|
||||
# scalar
|
||||
# ------
|
||||
|
||||
if "shape" not in parameters["x"] or parameters["x"]["shape"] == ():
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
# tensor
|
||||
# ------
|
||||
|
||||
if "shape" not in parameters["x"]:
|
||||
parameters["x"]["shape"] = (3, 2)
|
||||
|
||||
if parameters["x"]["shape"] == ():
|
||||
return
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
def test_others_bad_fusing(helpers):
|
||||
"""
|
||||
Test others with bad fusing.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# two variable inputs
|
||||
# -------------------
|
||||
|
||||
@cnp.compiler({"x": "encrypted", "y": "clear"})
|
||||
def function1(x, y):
|
||||
return (10 * (np.sin(x) ** 2) + 10 * (np.cos(y) ** 2)).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
inputset = [(i, i) for i in range(100)]
|
||||
function1.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
A subgraph within the function you are trying to compile cannot be fused because it has multiple input nodes
|
||||
|
||||
%0 = x # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is one of the input nodes
|
||||
%1 = y # ClearScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is one of the input nodes
|
||||
%2 = sin(%0) # EncryptedScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%3 = 2 # ClearScalar<uint2>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%4 = power(%2, %3) # EncryptedScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%5 = 10 # ClearScalar<uint4>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%6 = multiply(%5, %4) # EncryptedScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%7 = cos(%1) # ClearScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%8 = 2 # ClearScalar<uint2>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%9 = power(%7, %8) # ClearScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%10 = 10 # ClearScalar<uint4>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%11 = multiply(%10, %9) # ClearScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%12 = add(%6, %11) # EncryptedScalar<float64>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%13 = astype(%12, dtype=int_) # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
return %13
|
||||
|
||||
""", # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
|
||||
# intermediates with different shape
|
||||
# ----------------------------------
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function2(x):
|
||||
return np.abs(np.sin(x)).reshape((2, 3)).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
inputset = [np.random.randint(0, 2**7, size=(3, 2)) for _ in range(100)]
|
||||
function2.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
A subgraph within the function you are trying to compile cannot be fused because of a node, which is has a different shape than the input node
|
||||
|
||||
%0 = x # EncryptedTensor<uint7, shape=(3, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this input node
|
||||
%1 = sin(%0) # EncryptedTensor<float64, shape=(3, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%2 = absolute(%1) # EncryptedTensor<float64, shape=(3, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%3 = reshape(%2, newshape=(2, 3)) # EncryptedTensor<float64, shape=(2, 3)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%4 = astype(%3, dtype=int_) # EncryptedTensor<uint1, shape=(2, 3)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this node has a different shape than the input node
|
||||
return %4
|
||||
|
||||
""", # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
|
||||
# non-fusable operation
|
||||
# ---------------------
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function3(x):
|
||||
return np.abs(np.sin(x)).transpose().astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
inputset = [[[0, 1], [2, 3]]]
|
||||
function3.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
A subgraph within the function you are trying to compile cannot be fused because of a node, which is marked explicitly as non-fusable
|
||||
|
||||
%0 = x # EncryptedTensor<uint2, shape=(2, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this input node
|
||||
%1 = sin(%0) # EncryptedTensor<float64, shape=(2, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%2 = absolute(%1) # EncryptedTensor<float64, shape=(2, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
%3 = transpose(%2) # EncryptedTensor<float64, shape=(2, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this node is not fusable
|
||||
%4 = astype(%3, dtype=int_) # EncryptedTensor<uint1, shape=(2, 2)>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
return %4
|
||||
|
||||
""", # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
|
||||
# integer two variable inputs
|
||||
# ---------------------------
|
||||
|
||||
@cnp.compiler({"x": "encrypted", "y": "clear"})
|
||||
def function4(x, y):
|
||||
return np.maximum(x, y)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
inputset = [(i, i) for i in range(100)]
|
||||
function4.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(
|
||||
# pylint: disable=line-too-long
|
||||
"""
|
||||
|
||||
A subgraph within the function you are trying to compile cannot be fused because it has multiple input nodes
|
||||
|
||||
%0 = x # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is one of the input nodes
|
||||
%1 = y # ClearScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is one of the input nodes
|
||||
%2 = maximum(%0, %1) # EncryptedScalar<uint1>
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within this subgraph
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
str(excinfo.value),
|
||||
)
|
||||
|
||||
|
||||
def test_others_bad_univariate(helpers):
|
||||
"""
|
||||
Test univariate with bad function.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
def bad_univariate(x):
|
||||
return np.array([x, x, x])
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def f(x):
|
||||
return cnp.univariate(bad_univariate)(x)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
inputset = range(10)
|
||||
f.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(
|
||||
"Function bad_univariate cannot be used with cnp.univariate",
|
||||
str(excinfo.value),
|
||||
)
|
||||
170
frontends/concrete-python/tests/execution/test_reshape.py
Normal file
170
frontends/concrete-python/tests/execution/test_reshape.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Tests of execution of reshape operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"shape,newshape",
|
||||
[
|
||||
pytest.param(
|
||||
(12,),
|
||||
(12, 1),
|
||||
),
|
||||
pytest.param(
|
||||
(12,),
|
||||
(1, 12),
|
||||
),
|
||||
pytest.param(
|
||||
(12,),
|
||||
(3, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(12,),
|
||||
(3, 2, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
12,
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(12,),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(4, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(2, 2, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(2, 3, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(3, 2, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(3, 1, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(12, 1),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(-1,),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
-1,
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
(3, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
(4, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
(3, 2, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 4, 5, 6),
|
||||
(6, 4, 30),
|
||||
),
|
||||
pytest.param(
|
||||
(6, 4, 30),
|
||||
(2, 3, 4, 5, 6),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 4, 5, 6),
|
||||
(2, 60, 6),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 60, 6),
|
||||
(2, 3, 4, 5, 6),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 2, 3, 4),
|
||||
(6, 6, -1),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 2, 3, 4),
|
||||
(6, -1, 12),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 2, 3, 4),
|
||||
(-1, 18, 4),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_reshape(shape, newshape, helpers):
|
||||
"""
|
||||
Test reshape.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return np.reshape(x, newshape)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def method(x):
|
||||
return x.reshape(newshape)
|
||||
|
||||
inputset = [np.random.randint(0, 2**5, size=shape) for i in range(100)]
|
||||
|
||||
function_circuit = function.compile(inputset, configuration)
|
||||
method_circuit = method.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**5, size=shape)
|
||||
|
||||
helpers.check_execution(function_circuit, function, sample)
|
||||
helpers.check_execution(method_circuit, method, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"shape",
|
||||
[
|
||||
pytest.param(
|
||||
(12,),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 4, 5, 6),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_flatten(shape, helpers):
|
||||
"""
|
||||
Test flatten.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
return x.flatten()
|
||||
|
||||
inputset = [np.random.randint(0, 2**5, size=shape) for i in range(100)]
|
||||
circuit = function.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**5, size=shape)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
@@ -0,0 +1,282 @@
|
||||
"""
|
||||
Tests of execution of round bit pattern operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
from concrete.numpy.representation.utils import format_constant
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"sample,lsbs_to_remove,expected_output",
|
||||
[
|
||||
(0b_0000_0011, 0, 0b_0000_0011),
|
||||
(0b_0000_0100, 0, 0b_0000_0100),
|
||||
(0b_0000_0000, 3, 0b_0000_0000),
|
||||
(0b_0000_0001, 3, 0b_0000_0000),
|
||||
(0b_0000_0010, 3, 0b_0000_0000),
|
||||
(0b_0000_0011, 3, 0b_0000_0000),
|
||||
(0b_0000_0100, 3, 0b_0000_1000),
|
||||
(0b_0000_0101, 3, 0b_0000_1000),
|
||||
(0b_0000_0110, 3, 0b_0000_1000),
|
||||
(0b_0000_0111, 3, 0b_0000_1000),
|
||||
(0b_0000_1000, 3, 0b_0000_1000),
|
||||
(0b_0000_1001, 3, 0b_0000_1000),
|
||||
(0b_0000_1010, 3, 0b_0000_1000),
|
||||
(0b_0000_1011, 3, 0b_0000_1000),
|
||||
(0b_0000_1100, 3, 0b_0001_0000),
|
||||
(0b_0000_1101, 3, 0b_0001_0000),
|
||||
(0b_0000_1110, 3, 0b_0001_0000),
|
||||
(0b_0000_1111, 3, 0b_0001_0000),
|
||||
],
|
||||
)
|
||||
def test_plain_round_bit_pattern(sample, lsbs_to_remove, expected_output):
|
||||
"""
|
||||
Test round bit pattern in evaluation context.
|
||||
"""
|
||||
assert cnp.round_bit_pattern(sample, lsbs_to_remove=lsbs_to_remove) == expected_output
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"sample,lsbs_to_remove,expected_error,expected_message",
|
||||
[
|
||||
(
|
||||
np.array([3.2, 4.1]),
|
||||
3,
|
||||
TypeError,
|
||||
"Expected input elements to be integers but they are dtype[float64]",
|
||||
),
|
||||
(
|
||||
"foo",
|
||||
3,
|
||||
TypeError,
|
||||
"Expected input to be an int or a numpy array but it's str",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_bad_plain_round_bit_pattern(
|
||||
sample,
|
||||
lsbs_to_remove,
|
||||
expected_error,
|
||||
expected_message,
|
||||
):
|
||||
"""
|
||||
Test round bit pattern in evaluation context with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
cnp.round_bit_pattern(sample, lsbs_to_remove=lsbs_to_remove)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_bits,lsbs_to_remove",
|
||||
[
|
||||
(3, 1),
|
||||
(3, 2),
|
||||
(4, 1),
|
||||
(4, 2),
|
||||
(4, 3),
|
||||
(5, 1),
|
||||
(5, 2),
|
||||
(5, 3),
|
||||
(5, 4),
|
||||
],
|
||||
)
|
||||
def test_round_bit_pattern(input_bits, lsbs_to_remove, helpers):
|
||||
"""
|
||||
Test round bit pattern in evaluation context.
|
||||
"""
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function(x):
|
||||
x_rounded = cnp.round_bit_pattern(x, lsbs_to_remove=lsbs_to_remove)
|
||||
return np.abs(50 * np.sin(x_rounded)).astype(np.int64)
|
||||
|
||||
circuit = function.compile([(2**input_bits) - 1], helpers.configuration())
|
||||
helpers.check_execution(circuit, function, np.random.randint(0, 2**input_bits), simulate=True)
|
||||
|
||||
|
||||
def test_auto_rounding(helpers):
|
||||
"""
|
||||
Test round bit pattern with auto rounding.
|
||||
"""
|
||||
|
||||
# with auto adjust rounders configuration
|
||||
# ---------------------------------------
|
||||
|
||||
# y has the max value of 1999, so it's 11 bits
|
||||
# our target msb is 5 bits, which means we need to remove 6 of the least significant bits
|
||||
|
||||
rounder1 = cnp.AutoRounder(target_msbs=5)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function1(x):
|
||||
y = x + 1000
|
||||
z = cnp.round_bit_pattern(y, lsbs_to_remove=rounder1)
|
||||
return np.sqrt(z).astype(np.int64)
|
||||
|
||||
inputset1 = range(1000)
|
||||
function1.trace(inputset1, helpers.configuration(), auto_adjust_rounders=True)
|
||||
|
||||
assert rounder1.lsbs_to_remove == 6
|
||||
|
||||
# manual
|
||||
# ------
|
||||
|
||||
# y has the max value of 1999, so it's 11 bits
|
||||
# our target msb is 3 bits, which means we need to remove 8 of the least significant bits
|
||||
|
||||
rounder2 = cnp.AutoRounder(target_msbs=3)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function2(x):
|
||||
y = x + 1000
|
||||
z = cnp.round_bit_pattern(y, lsbs_to_remove=rounder2)
|
||||
return np.sqrt(z).astype(np.int64)
|
||||
|
||||
inputset2 = range(1000)
|
||||
cnp.AutoRounder.adjust(function2, inputset2)
|
||||
|
||||
assert rounder2.lsbs_to_remove == 8
|
||||
|
||||
# complicated case
|
||||
# ----------------
|
||||
|
||||
# have 2 ** 8 entries during evaluation, it won't matter after compilation
|
||||
entries3 = list(range(2**8))
|
||||
# we have 8-bit inputs for this table, and we only want to use first 5-bits
|
||||
for i in range(0, 2**8, 2**3):
|
||||
# so we set every 8th entry to a 4-bit value
|
||||
entries3[i] = np.random.randint(0, (2**4) - (2**2))
|
||||
# when this tlu is applied to an 8-bit value with 5-bit msb rounding, result will be 4-bits
|
||||
table3 = cnp.LookupTable(entries3)
|
||||
# and this is the rounder for table1, which should have lsbs_to_remove of 3
|
||||
rounder3 = cnp.AutoRounder(target_msbs=5)
|
||||
|
||||
# have 2 ** 8 entries during evaluation, it won't matter after compilation
|
||||
entries4 = list(range(2**8))
|
||||
# we have 4-bit inputs for this table, and we only want to use first 2-bits
|
||||
for i in range(0, 2**4, 2**2):
|
||||
# so we set every 4th entry to an 8-bit value
|
||||
entries4[i] = np.random.randint(2**7, 2**8)
|
||||
# when this tlu is applied to a 4-bit value with 2-bit msb rounding, result will be 8-bits
|
||||
table4 = cnp.LookupTable(entries4)
|
||||
# and this is the rounder for table2, which should have lsbs_to_remove of 2
|
||||
rounder4 = cnp.AutoRounder(target_msbs=2)
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def function3(x):
|
||||
a = cnp.round_bit_pattern(x, lsbs_to_remove=rounder3)
|
||||
b = table3[a]
|
||||
c = cnp.round_bit_pattern(b, lsbs_to_remove=rounder4)
|
||||
d = table4[c]
|
||||
return d
|
||||
|
||||
inputset3 = range((2**8) - (2**3))
|
||||
circuit3 = function3.compile(
|
||||
inputset3,
|
||||
helpers.configuration(),
|
||||
auto_adjust_rounders=True,
|
||||
)
|
||||
|
||||
assert rounder3.lsbs_to_remove == 3
|
||||
assert rounder4.lsbs_to_remove == 2
|
||||
|
||||
table3_formatted_string = format_constant(table3.table, 25)
|
||||
table4_formatted_string = format_constant(table4.table, 25)
|
||||
|
||||
helpers.check_str(
|
||||
f"""
|
||||
|
||||
%0 = x # EncryptedScalar<uint8>
|
||||
%1 = round_bit_pattern(%0, lsbs_to_remove=3) # EncryptedScalar<uint8>
|
||||
%2 = tlu(%1, table={table3_formatted_string}) # EncryptedScalar<uint4>
|
||||
%3 = round_bit_pattern(%2, lsbs_to_remove=2) # EncryptedScalar<uint4>
|
||||
%4 = tlu(%3, table={table4_formatted_string}) # EncryptedScalar<uint8>
|
||||
return %4
|
||||
|
||||
""",
|
||||
str(circuit3.graph.format(show_bounds=False)),
|
||||
)
|
||||
|
||||
|
||||
def test_auto_rounding_without_adjustment():
|
||||
"""
|
||||
Test round bit pattern with auto rounding but without adjustment.
|
||||
"""
|
||||
|
||||
rounder = cnp.AutoRounder(target_msbs=5)
|
||||
|
||||
def function(x):
|
||||
y = x + 1000
|
||||
z = cnp.round_bit_pattern(y, lsbs_to_remove=rounder)
|
||||
return np.sqrt(z).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
function(100)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"AutoRounders cannot be used before adjustment, "
|
||||
"please call AutoRounder.adjust with the function that will be compiled "
|
||||
"and provide the exact inputset that will be used for compilation"
|
||||
)
|
||||
|
||||
|
||||
def test_auto_rounding_with_empty_inputset():
|
||||
"""
|
||||
Test round bit pattern with auto rounding but with empty inputset.
|
||||
"""
|
||||
|
||||
rounder = cnp.AutoRounder(target_msbs=5)
|
||||
|
||||
def function(x):
|
||||
y = x + 1000
|
||||
z = cnp.round_bit_pattern(y, lsbs_to_remove=rounder)
|
||||
return np.sqrt(z).astype(np.int64)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
cnp.AutoRounder.adjust(function, [])
|
||||
|
||||
assert str(excinfo.value) == "AutoRounders cannot be adjusted with an empty inputset"
|
||||
|
||||
|
||||
def test_auto_rounding_recursive_adjustment():
|
||||
"""
|
||||
Test round bit pattern with auto rounding but with recursive adjustment.
|
||||
"""
|
||||
|
||||
rounder = cnp.AutoRounder(target_msbs=5)
|
||||
|
||||
def function(x):
|
||||
cnp.AutoRounder.adjust(function, range(10))
|
||||
y = x + 1000
|
||||
z = cnp.round_bit_pattern(y, lsbs_to_remove=rounder)
|
||||
return np.sqrt(z).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
cnp.AutoRounder.adjust(function, range(10))
|
||||
|
||||
assert str(excinfo.value) == "AutoRounders cannot be adjusted recursively"
|
||||
|
||||
|
||||
def test_auto_rounding_construct_in_function():
|
||||
"""
|
||||
Test round bit pattern with auto rounding but rounder is constructed within the function.
|
||||
"""
|
||||
|
||||
def function(x):
|
||||
y = x + 1000
|
||||
z = cnp.round_bit_pattern(y, lsbs_to_remove=cnp.AutoRounder(target_msbs=5))
|
||||
return np.sqrt(z).astype(np.int64)
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
cnp.AutoRounder.adjust(function, range(10))
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"AutoRounders cannot be constructed during adjustment, "
|
||||
"please construct AutoRounders outside the function and reference it"
|
||||
)
|
||||
178
frontends/concrete-python/tests/execution/test_shift.py
Normal file
178
frontends/concrete-python/tests/execution/test_shift.py
Normal file
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
Tests of execution of shift operations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x << y,
|
||||
id="x << y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 1], "status": "encrypted"},
|
||||
"y": {"range": [0, 7], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 3], "status": "encrypted"},
|
||||
"y": {"range": [0, 3], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 3], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [0, 3], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 3], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [0, 3], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_left_shift(function, parameters, helpers):
|
||||
"""
|
||||
Test left shift between encrypted integers.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x >> y,
|
||||
id="x >> y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 1 << 7], "status": "encrypted"},
|
||||
"y": {"range": [0, 7], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 1 << 4], "status": "encrypted"},
|
||||
"y": {"range": [0, 3], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 1 << 4], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [0, 3], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 1 << 4], "status": "encrypted", "shape": (2,)},
|
||||
"y": {"range": [0, 3], "status": "encrypted", "shape": (2,)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_right_shift(function, parameters, helpers):
|
||||
"""
|
||||
Test right shift between encrypted integers.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x << y,
|
||||
id="x << y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 1], "status": "encrypted"},
|
||||
"y": {"range": [0, 7], "status": "encrypted"},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_left_shift_coverage(function, parameters, helpers):
|
||||
"""
|
||||
Test left shift between encrypted integers all cases.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
for i in range(2):
|
||||
for j in range(8):
|
||||
helpers.check_execution(circuit, function, [i, j])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x >> y,
|
||||
id="x >> y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 1 << 7], "status": "encrypted"},
|
||||
"y": {"range": [0, 7], "status": "encrypted"},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_right_shift_coverage(function, parameters, helpers):
|
||||
"""
|
||||
Test right shift between encrypted integers all cases.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
helpers.check_execution(circuit, function, [0b11, 0])
|
||||
helpers.check_execution(circuit, function, [0b11, 1])
|
||||
helpers.check_execution(circuit, function, [0b110, 2])
|
||||
helpers.check_execution(circuit, function, [0b1100, 3])
|
||||
helpers.check_execution(circuit, function, [0b11000, 4])
|
||||
helpers.check_execution(circuit, function, [0b110000, 5])
|
||||
helpers.check_execution(circuit, function, [0b110000, 6])
|
||||
helpers.check_execution(circuit, function, [0b1100000, 7])
|
||||
@@ -0,0 +1,530 @@
|
||||
"""
|
||||
Tests of execution of static assignment operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def assignment_case_0():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (3,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[:] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_1():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (3,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[0] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_2():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (3,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[1] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_3():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (3,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[2] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_4():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[0:3] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_5():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[1:4] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_6():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[1:4:2] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_7():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (10,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[::2] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_8():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[2:0:-1] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_9():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[4:0:-2] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_10():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=(3,))
|
||||
|
||||
def assign(x):
|
||||
x[1:4] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_11():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5,)
|
||||
value = np.random.randint(0, 2**7, size=(3,))
|
||||
|
||||
def assign(x):
|
||||
x[4:1:-1] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_12():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (10,)
|
||||
value = np.random.randint(0, 2**7, size=(3,))
|
||||
|
||||
def assign(x):
|
||||
x[1:7:2] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_13():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (10,)
|
||||
value = np.random.randint(0, 2**7, size=(3,))
|
||||
|
||||
def assign(x):
|
||||
x[7:1:-2] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_14():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[0, 0] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_15():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[3, 1] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_16():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[0] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_17():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(4,))
|
||||
|
||||
def assign(x):
|
||||
x[0] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_18():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(5,))
|
||||
|
||||
def assign(x):
|
||||
x[:, 0] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_19():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(5,))
|
||||
|
||||
def assign(x):
|
||||
x[:, 1] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_20():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[0:3, :] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_21():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(3, 4))
|
||||
|
||||
def assign(x):
|
||||
x[0:3, :] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_22():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(4,))
|
||||
|
||||
def assign(x):
|
||||
x[0:3, :] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_23():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(3,))
|
||||
|
||||
def assign(x):
|
||||
x[0:3, 1:4] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_24():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(3, 3))
|
||||
|
||||
def assign(x):
|
||||
x[0:3, 1:4] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_25():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(3, 3))
|
||||
|
||||
def assign(x):
|
||||
x[4:1:-1, 3:0:-1] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_26():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(3,))
|
||||
|
||||
def assign(x):
|
||||
x[3:0:-1, 0] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_27():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=(2,))
|
||||
|
||||
def assign(x):
|
||||
x[0, 1:3] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
def assignment_case_28():
|
||||
"""
|
||||
Assignment test case.
|
||||
"""
|
||||
|
||||
shape = (5, 4)
|
||||
value = np.random.randint(0, 2**7, size=())
|
||||
|
||||
def assign(x):
|
||||
x[2:4, 1:3] = value
|
||||
return x
|
||||
|
||||
return shape, assign
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"shape,function",
|
||||
[
|
||||
pytest.param(*assignment_case_0()),
|
||||
pytest.param(*assignment_case_1()),
|
||||
pytest.param(*assignment_case_2()),
|
||||
pytest.param(*assignment_case_3()),
|
||||
pytest.param(*assignment_case_4()),
|
||||
pytest.param(*assignment_case_5()),
|
||||
pytest.param(*assignment_case_6()),
|
||||
pytest.param(*assignment_case_7()),
|
||||
pytest.param(*assignment_case_8()),
|
||||
pytest.param(*assignment_case_9()),
|
||||
pytest.param(*assignment_case_10()),
|
||||
pytest.param(*assignment_case_11()),
|
||||
pytest.param(*assignment_case_12()),
|
||||
pytest.param(*assignment_case_13()),
|
||||
pytest.param(*assignment_case_14()),
|
||||
pytest.param(*assignment_case_15()),
|
||||
pytest.param(*assignment_case_16()),
|
||||
pytest.param(*assignment_case_17()),
|
||||
pytest.param(*assignment_case_18()),
|
||||
pytest.param(*assignment_case_19()),
|
||||
pytest.param(*assignment_case_20()),
|
||||
pytest.param(*assignment_case_21()),
|
||||
pytest.param(*assignment_case_22()),
|
||||
pytest.param(*assignment_case_23()),
|
||||
pytest.param(*assignment_case_24()),
|
||||
pytest.param(*assignment_case_25()),
|
||||
pytest.param(*assignment_case_26()),
|
||||
pytest.param(*assignment_case_27()),
|
||||
pytest.param(*assignment_case_28()),
|
||||
],
|
||||
)
|
||||
def test_static_assignment(shape, function, helpers):
|
||||
"""
|
||||
Test static assignment.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**7, size=shape) for _ in range(100)]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**7, size=shape)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
def test_bad_static_assignment(helpers):
|
||||
"""
|
||||
Test static assingment with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# with float
|
||||
# ----------
|
||||
|
||||
def f(x):
|
||||
x[1.5] = 0
|
||||
return x
|
||||
|
||||
compiler = cnp.Compiler(f, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**3, size=(3,)) for _ in range(100)]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == "Assigning to '1.5' is not supported"
|
||||
|
||||
# with bad slice
|
||||
# --------------
|
||||
|
||||
def g(x):
|
||||
x[slice(1.5, 2.5, None)] = 0
|
||||
return x
|
||||
|
||||
compiler = cnp.Compiler(g, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**3, size=(3,)) for _ in range(100)]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == "Assigning to '1.5:2.5' is not supported"
|
||||
@@ -0,0 +1,198 @@
|
||||
"""
|
||||
Tests of execution of static indexing operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"shape,function",
|
||||
[
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[0],
|
||||
id="x[0] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[1],
|
||||
id="x[1] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[2],
|
||||
id="x[2] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[-1],
|
||||
id="x[-1] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[-2],
|
||||
id="x[-2] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[-3],
|
||||
id="x[-3] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[0:1],
|
||||
id="x[0:1] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[1:2],
|
||||
id="x[1:2] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[2:3],
|
||||
id="x[2:3] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[0:2],
|
||||
id="x[0:2] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[1:3],
|
||||
id="x[1:3] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[0:3],
|
||||
id="x[0:3] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[2:0:-1],
|
||||
id="x[2:0:-1] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[::-1],
|
||||
id="x[::-1] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[:-1],
|
||||
id="x[:-1] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3,),
|
||||
lambda x: x[-2:],
|
||||
id="x[-2:] where x.shape == (3,)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[0, 0],
|
||||
id="x[0, 0] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[0, -1],
|
||||
id="x[0, -1] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[-1, 0],
|
||||
id="x[-1, 0] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[-1, -1],
|
||||
id="x[-1, -1] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[2, 1],
|
||||
id="x[2, 1] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[0],
|
||||
id="x[0] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[:, 0],
|
||||
id="x[:, 0] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[-1],
|
||||
id="x[-1] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[:, -1],
|
||||
id="x[:, -1] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[1:3, 1:3],
|
||||
id="x[1:3, 1:3] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
lambda x: x[::-1],
|
||||
id="x[::-1] where x.shape == (3, 4)",
|
||||
),
|
||||
pytest.param(
|
||||
(10,),
|
||||
lambda x: x[slice(np.int64(8), np.int64(2), np.int64(-2))],
|
||||
id="x[8:2:-2] where x.shape == (10,)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_static_indexing(shape, function, helpers):
|
||||
"""
|
||||
Test static indexing.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**5, size=shape) for _ in range(100)]
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = np.random.randint(0, 2**5, size=shape)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
def test_bad_static_indexing(helpers):
|
||||
"""
|
||||
Test static indexing with bad parameters.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
# with float
|
||||
# ----------
|
||||
|
||||
compiler = cnp.Compiler(lambda x: x[1.5], {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**3, size=(3,)) for _ in range(100)]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == "Indexing with '1.5' is not supported"
|
||||
|
||||
# with bad slice
|
||||
# --------------
|
||||
|
||||
compiler = cnp.Compiler(lambda x: x[slice(1.5, 2.5, None)], {"x": "encrypted"})
|
||||
|
||||
inputset = [np.random.randint(0, 2**3, size=(3,)) for _ in range(100)]
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == "Indexing with '1.5:2.5' is not supported"
|
||||
159
frontends/concrete-python/tests/execution/test_sub.py
Normal file
159
frontends/concrete-python/tests/execution/test_sub.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
Tests of execution of sub operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x - 42,
|
||||
id="x - 42",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 42 - x,
|
||||
id="42 - x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x - np.array([1, 2, 3]),
|
||||
id="x - [1, 2, 3]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([1, 2, 3]) - x,
|
||||
id="[1, 2, 3] - x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x - np.array([[1, 2, 3], [4, 5, 6]]),
|
||||
id="x - [[1, 2, 3], [4, 5, 6]]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([[1, 2, 3], [4, 5, 6]]) - x,
|
||||
id="[[1, 2, 3], [4, 5, 6]] - x",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_constant_sub(function, parameters, helpers):
|
||||
"""
|
||||
Test sub where one of the operators is a constant.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: x - y,
|
||||
id="x - y",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear"},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "clear"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "clear"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear"},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted"},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "clear", "shape": (2, 1)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (2, 1)},
|
||||
"y": {"range": [0, 60], "status": "clear", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 60], "status": "encrypted", "shape": (2, 1)},
|
||||
"y": {"range": [0, 60], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_sub(function, parameters, helpers):
|
||||
"""
|
||||
Test sub where both of the operators are dynamic.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
120
frontends/concrete-python/tests/execution/test_sum.py
Normal file
120
frontends/concrete-python/tests/execution/test_sum.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Tests of execution of sum operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: np.sum(x),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=None), # type: ignore
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=0),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=1),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=-1),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=-2),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=(0, 1)),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=(-2, -1)),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=0, keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=1, keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=-1, keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=-2, keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=(0, 1), keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, axis=(-2, -1), keepdims=True),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_sum(function, parameters, helpers):
|
||||
"""
|
||||
Test sum.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
78
frontends/concrete-python/tests/execution/test_transpose.py
Normal file
78
frontends/concrete-python/tests/execution/test_transpose.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
Tests of execution of transpose operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: np.transpose(x),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.transpose(),
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.T,
|
||||
{
|
||||
"x": {"shape": (3, 2), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.transpose((1, 0, 2)),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.transpose((1, 2, 0)),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.transpose((0, 2, 1)),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.transpose((2, 0, 1)),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.transpose(x, (3, 0, 2, 1)),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4, 5), "range": [0, 10], "status": "encrypted"},
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_transpose(function, parameters, helpers):
|
||||
"""
|
||||
Test transpose.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
49
frontends/concrete-python/tests/execution/test_zeros.py
Normal file
49
frontends/concrete-python/tests/execution/test_zeros.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Tests of execution of zeros operation.
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: cnp.zero() + x,
|
||||
id="cnp.zero() + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.zeros(()) + x,
|
||||
id="cnp.zeros(()) + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.zeros(10) + x,
|
||||
id="cnp.zeros(10) + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.zeros((10,)) + x,
|
||||
id="cnp.zeros((10,)) + x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: cnp.zeros((3, 2)) + x,
|
||||
id="cnp.zeros((3, 2)) + x",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_zeros(function, helpers):
|
||||
"""
|
||||
Test zeros.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
|
||||
inputset = range(10)
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
sample = random.randint(0, 11)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
3
frontends/concrete-python/tests/extensions/__init__.py
Normal file
3
frontends/concrete-python/tests/extensions/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of extensions.
|
||||
"""
|
||||
37
frontends/concrete-python/tests/extensions/test_array.py
Normal file
37
frontends/concrete-python/tests/extensions/test_array.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Tests of 'array' extension.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters,expected_error",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: cnp.array([x, y]),
|
||||
{
|
||||
"x": {"range": [0, 10], "status": "encrypted", "shape": ()},
|
||||
"y": {"range": [0, 10], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
"Encrypted arrays can only be created from scalars",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_bad_array(function, parameters, expected_error, helpers):
|
||||
"""
|
||||
Test array with bad parameters.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
assert str(excinfo.value) == expected_error
|
||||
28
frontends/concrete-python/tests/extensions/test_table.py
Normal file
28
frontends/concrete-python/tests/extensions/test_table.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
Tests of 'LookupTable' extension.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy import LookupTable
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"table, expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
LookupTable([1, 2, 3]),
|
||||
"[1, 2, 3]",
|
||||
),
|
||||
pytest.param(
|
||||
LookupTable([LookupTable([1, 2, 3]), LookupTable([4, 5, 6])]),
|
||||
"[[1, 2, 3], [4, 5, 6]]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_lookup_table_repr(table, expected_result):
|
||||
"""
|
||||
Test `__repr__` method of `LookupTable` class.
|
||||
"""
|
||||
|
||||
assert repr(table) == expected_result
|
||||
64
frontends/concrete-python/tests/extensions/test_tag.py
Normal file
64
frontends/concrete-python/tests/extensions/test_tag.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""
|
||||
Tests of 'tag' extension.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def test_tag(helpers):
|
||||
"""
|
||||
Test tag extension.
|
||||
"""
|
||||
|
||||
def g(z):
|
||||
with cnp.tag("def"):
|
||||
a = 120 - z
|
||||
b = a // 4
|
||||
return b
|
||||
|
||||
@cnp.compiler({"x": "encrypted"})
|
||||
def f(x):
|
||||
with cnp.tag("abc"):
|
||||
x = x * 2
|
||||
with cnp.tag("foo"):
|
||||
y = x + 42
|
||||
z = np.sqrt(y).astype(np.int64)
|
||||
|
||||
return g(z + 3) * 2
|
||||
|
||||
inputset = range(10)
|
||||
circuit = f.trace(inputset, configuration=helpers.configuration())
|
||||
|
||||
helpers.check_str(
|
||||
"""
|
||||
|
||||
%0 = x # EncryptedScalar<uint4>
|
||||
%1 = 2 # ClearScalar<uint2> @ abc
|
||||
%2 = multiply(%0, %1) # EncryptedScalar<uint5> @ abc
|
||||
%3 = 42 # ClearScalar<uint6> @ abc.foo
|
||||
%4 = add(%2, %3) # EncryptedScalar<uint6> @ abc.foo
|
||||
%5 = subgraph(%4) # EncryptedScalar<uint3> @ abc
|
||||
%6 = 3 # ClearScalar<uint2>
|
||||
%7 = add(%5, %6) # EncryptedScalar<uint4>
|
||||
%8 = 120 # ClearScalar<uint7> @ def
|
||||
%9 = subtract(%8, %7) # EncryptedScalar<uint7> @ def
|
||||
%10 = 4 # ClearScalar<uint3> @ def
|
||||
%11 = floor_divide(%9, %10) # EncryptedScalar<uint5> @ def
|
||||
%12 = 2 # ClearScalar<uint2>
|
||||
%13 = multiply(%11, %12) # EncryptedScalar<uint6>
|
||||
return %13
|
||||
|
||||
Subgraphs:
|
||||
|
||||
%5 = subgraph(%4):
|
||||
|
||||
%0 = input # EncryptedScalar<uint2> @ abc.foo
|
||||
%1 = sqrt(%0) # EncryptedScalar<float64> @ abc
|
||||
%2 = astype(%1, dtype=int_) # EncryptedScalar<uint1> @ abc
|
||||
return %2
|
||||
|
||||
""".strip(),
|
||||
circuit.format(show_bounds=False),
|
||||
)
|
||||
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Tests of 'univariate' extension.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def test_bad_univariate(helpers):
|
||||
"""
|
||||
Test 'univariate' extension with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
@cnp.circuit({"x": "encrypted"}, helpers.configuration())
|
||||
def function(x: cnp.uint3):
|
||||
return cnp.univariate(lambda x: x**2)(x)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Univariate extension requires `outputs` argument for direct circuit definition "
|
||||
"(e.g., cnp.univariate(function, outputs=cnp.uint4)(x))"
|
||||
)
|
||||
3
frontends/concrete-python/tests/internal/__init__.py
Normal file
3
frontends/concrete-python/tests/internal/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.internal` namespace.
|
||||
"""
|
||||
29
frontends/concrete-python/tests/internal/test_utils.py
Normal file
29
frontends/concrete-python/tests/internal/test_utils.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Tests of utilities related to the entire project.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.internal.utils import assert_that, unreachable
|
||||
|
||||
|
||||
def test_assert_that():
|
||||
"""
|
||||
Test `assert_that` function.
|
||||
"""
|
||||
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
assert_that(2 + 2 == 3, "no")
|
||||
|
||||
assert str(excinfo.value) == "no"
|
||||
|
||||
|
||||
def test_unreachable():
|
||||
"""
|
||||
Test `unreachable` function.
|
||||
"""
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
unreachable()
|
||||
|
||||
assert str(excinfo.value) == "Entered unreachable code"
|
||||
3
frontends/concrete-python/tests/mlir/__init__.py
Normal file
3
frontends/concrete-python/tests/mlir/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.mlir` namespace.
|
||||
"""
|
||||
501
frontends/concrete-python/tests/mlir/test_graph_converter.py
Normal file
501
frontends/concrete-python/tests/mlir/test_graph_converter.py
Normal file
@@ -0,0 +1,501 @@
|
||||
"""
|
||||
Tests of `GraphConverter` class.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
import concrete.onnx as connx
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
|
||||
|
||||
def assign(x):
|
||||
"""
|
||||
Simple assignment to a vector.
|
||||
"""
|
||||
|
||||
x[0] = 0
|
||||
return x
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,encryption_statuses,inputset,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: (x - y, x + y),
|
||||
{"x": "encrypted", "y": "clear"},
|
||||
[(0, 0), (7, 7), (0, 7), (7, 0)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedScalar<uint3> ∈ [0, 7]
|
||||
%1 = y # ClearScalar<uint3> ∈ [0, 7]
|
||||
%2 = subtract(%0, %1) # EncryptedScalar<int4> ∈ [-7, 7]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only a single output is supported
|
||||
%3 = add(%0, %1) # EncryptedScalar<uint4> ∈ [0, 14]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only a single output is supported
|
||||
return (%2, %3)
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x,
|
||||
{"x": "clear"},
|
||||
range(-10, 10),
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearScalar<int5> ∈ [-10, 9]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted signed integer inputs are supported
|
||||
return %0
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x * 1.5,
|
||||
{"x": "encrypted"},
|
||||
[2.5 * x for x in range(100)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedScalar<float64> ∈ [0.0, 247.5]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer inputs are supported
|
||||
%1 = 1.5 # ClearScalar<float64> ∈ [1.5, 1.5]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer constants are supported
|
||||
%2 = multiply(%0, %1) # EncryptedScalar<float64> ∈ [0.0, 371.25]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sin(x),
|
||||
{"x": "encrypted"},
|
||||
range(100),
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedScalar<uint7> ∈ [0, 99]
|
||||
%1 = sin(%0) # EncryptedScalar<float64> ∈ [-0.99999, 0.999912]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y)),
|
||||
{"x": "encrypted", "y": "clear"},
|
||||
[
|
||||
(
|
||||
np.random.randint(0, 2**3, size=(3, 2)),
|
||||
np.random.randint(0, 2**3, size=(3, 2)),
|
||||
)
|
||||
for _ in range(100)
|
||||
],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedTensor<uint3, shape=(3, 2)> ∈ [0, 7]
|
||||
%1 = y # ClearTensor<uint3, shape=(3, 2)> ∈ [0, 7]
|
||||
%2 = concatenate((%0, %1)) # EncryptedTensor<uint3, shape=(6, 2)> ∈ [0, 7]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only all encrypted concatenate is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, w: connx.conv(x, w),
|
||||
{"x": "encrypted", "w": "encrypted"},
|
||||
[
|
||||
(
|
||||
np.random.randint(0, 2, size=(1, 1, 4)),
|
||||
np.random.randint(0, 2, size=(1, 1, 1)),
|
||||
)
|
||||
for _ in range(100)
|
||||
],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedTensor<uint1, shape=(1, 1, 4)> ∈ [0, 1]
|
||||
%1 = w # EncryptedTensor<uint1, shape=(1, 1, 1)> ∈ [0, 1]
|
||||
%2 = conv1d(%0, %1, [0], pads=(0, 0), strides=(1,), dilations=(1,), group=1) # EncryptedTensor<uint1, shape=(1, 1, 4)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only conv1d with encrypted input and clear weight is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, w: connx.conv(x, w),
|
||||
{"x": "encrypted", "w": "encrypted"},
|
||||
[
|
||||
(
|
||||
np.random.randint(0, 2, size=(1, 1, 4, 4)),
|
||||
np.random.randint(0, 2, size=(1, 1, 1, 1)),
|
||||
)
|
||||
for _ in range(100)
|
||||
],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedTensor<uint1, shape=(1, 1, 4, 4)> ∈ [0, 1]
|
||||
%1 = w # EncryptedTensor<uint1, shape=(1, 1, 1, 1)> ∈ [0, 1]
|
||||
%2 = conv2d(%0, %1, [0], pads=(0, 0, 0, 0), strides=(1, 1), dilations=(1, 1), group=1) # EncryptedTensor<uint1, shape=(1, 1, 4, 4)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only conv2d with encrypted input and clear weight is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, w: connx.conv(x, w),
|
||||
{"x": "encrypted", "w": "encrypted"},
|
||||
[
|
||||
(
|
||||
np.random.randint(0, 2, size=(1, 1, 4, 4, 4)),
|
||||
np.random.randint(0, 2, size=(1, 1, 1, 1, 1)),
|
||||
)
|
||||
for _ in range(100)
|
||||
],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedTensor<uint1, shape=(1, 1, 4, 4, 4)> ∈ [0, 1]
|
||||
%1 = w # EncryptedTensor<uint1, shape=(1, 1, 1, 1, 1)> ∈ [0, 1]
|
||||
%2 = conv3d(%0, %1, [0], pads=(0, 0, 0, 0, 0, 0), strides=(1, 1, 1), dilations=(1, 1, 1), group=1) # EncryptedTensor<uint1, shape=(1, 1, 4, 4, 4)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only conv3d with encrypted input and clear weight is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.dot(x, y),
|
||||
{"x": "encrypted", "y": "encrypted"},
|
||||
[([0], [0]), ([3], [3]), ([3], [0]), ([0], [3]), ([1], [1])],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedTensor<uint2, shape=(1,)> ∈ [0, 3]
|
||||
%1 = y # EncryptedTensor<uint2, shape=(1,)> ∈ [0, 3]
|
||||
%2 = dot(%0, %1) # EncryptedScalar<uint4> ∈ [0, 9]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only dot product between encrypted and clear is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x[0],
|
||||
{"x": "clear"},
|
||||
[[0, 1, 2, 3], [7, 6, 5, 4]],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint3, shape=(4,)> ∈ [0, 7]
|
||||
%1 = %0[0] # ClearScalar<uint3> ∈ [0, 7]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted indexing supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x @ y,
|
||||
{"x": "encrypted", "y": "encrypted"},
|
||||
[
|
||||
(
|
||||
np.random.randint(0, 2**1, size=(1, 1)),
|
||||
np.random.randint(0, 2**1, size=(1, 1)),
|
||||
)
|
||||
for _ in range(100)
|
||||
],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedTensor<uint1, shape=(1, 1)> ∈ [0, 1]
|
||||
%1 = y # EncryptedTensor<uint1, shape=(1, 1)> ∈ [0, 1]
|
||||
%2 = matmul(%0, %1) # EncryptedTensor<uint1, shape=(1, 1)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only matrix multiplication between encrypted and clear is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x * y,
|
||||
{"x": "encrypted", "y": "encrypted"},
|
||||
[(0, 0), (7, 7), (0, 7), (7, 0)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedScalar<uint3> ∈ [0, 7]
|
||||
%1 = y # EncryptedScalar<uint3> ∈ [0, 7]
|
||||
%2 = multiply(%0, %1) # EncryptedScalar<uint6> ∈ [0, 49]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only multiplication between encrypted and clear is supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: -x,
|
||||
{"x": "clear"},
|
||||
[0, 7],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearScalar<uint3> ∈ [0, 7]
|
||||
%1 = negative(%0) # ClearScalar<int4> ∈ [-7, 0]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted negation is supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.reshape((3, 2)),
|
||||
{"x": "clear"},
|
||||
[np.random.randint(0, 2**3, size=(2, 3)) for _ in range(100)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint3, shape=(2, 3)> ∈ [0, 7]
|
||||
%1 = reshape(%0, newshape=(3, 2)) # ClearTensor<uint3, shape=(3, 2)> ∈ [0, 7]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted reshape is supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x),
|
||||
{"x": "clear"},
|
||||
[np.random.randint(0, 2, size=(1,)) for _ in range(100)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint1, shape=(1,)> ∈ [0, 1]
|
||||
%1 = sum(%0) # ClearScalar<uint1> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted sum is supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.maximum(x, np.array([3])),
|
||||
{"x": "clear"},
|
||||
[[0], [1]],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint1, shape=(1,)> ∈ [0, 1]
|
||||
%1 = [3] # ClearTensor<uint2, shape=(1,)> ∈ [3, 3]
|
||||
%2 = maximum(%0, %1) # ClearTensor<uint2, shape=(1,)> ∈ [3, 3]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one of the operands must be encrypted
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.transpose(x),
|
||||
{"x": "clear"},
|
||||
[np.random.randint(0, 2, size=(3, 2)) for _ in range(10)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint1, shape=(3, 2)> ∈ [0, 1]
|
||||
%1 = transpose(%0) # ClearTensor<uint1, shape=(2, 3)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted transpose is supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.broadcast_to(x, shape=(3, 2)),
|
||||
{"x": "clear"},
|
||||
[np.random.randint(0, 2, size=(2,)) for _ in range(10)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint1, shape=(2,)> ∈ [0, 1]
|
||||
%1 = broadcast_to(%0, shape=(3, 2)) # ClearTensor<uint1, shape=(3, 2)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted broadcasting is supported
|
||||
return %1
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
assign,
|
||||
{"x": "clear"},
|
||||
[np.random.randint(0, 2, size=(3,)) for _ in range(10)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # ClearTensor<uint1, shape=(3,)> ∈ [0, 1]
|
||||
%1 = 0 # ClearScalar<uint1> ∈ [0, 0]
|
||||
%2 = (%0[0] = %1) # ClearTensor<uint1, shape=(3,)> ∈ [0, 1]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only assignment to encrypted tensors are supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.abs(10 * np.sin(x + 300)).astype(np.int64),
|
||||
{"x": "encrypted"},
|
||||
[200000],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR:
|
||||
|
||||
%0 = x # EncryptedScalar<uint18> ∈ [200000, 200000]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this input is 18-bits
|
||||
%1 = 300 # ClearScalar<uint9> ∈ [300, 300]
|
||||
%2 = add(%0, %1) # EncryptedScalar<uint18> ∈ [200300, 200300]
|
||||
%3 = subgraph(%2) # EncryptedScalar<uint4> ∈ [9, 9]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ table lookups are only supported on circuits with up to 16-bits
|
||||
return %3
|
||||
|
||||
Subgraphs:
|
||||
|
||||
%3 = subgraph(%2):
|
||||
|
||||
%0 = input # EncryptedScalar<uint2>
|
||||
%1 = sin(%0) # EncryptedScalar<float64>
|
||||
%2 = 10 # ClearScalar<uint4>
|
||||
%3 = multiply(%2, %1) # EncryptedScalar<float64>
|
||||
%4 = absolute(%3) # EncryptedScalar<float64>
|
||||
%5 = astype(%4, dtype=int_) # EncryptedScalar<uint1>
|
||||
return %5
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x << y,
|
||||
{"x": "encrypted", "y": "encrypted"},
|
||||
[(-1, 1), (-2, 3)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedScalar<int2> ∈ [-2, -1]
|
||||
%1 = y # EncryptedScalar<uint2> ∈ [1, 3]
|
||||
%2 = left_shift(%0, %1) # EncryptedScalar<int5> ∈ [-16, -2]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only unsigned bitwise operations are supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: x << y,
|
||||
{"x": "encrypted", "y": "encrypted"},
|
||||
[(1, 20), (2, 10)],
|
||||
RuntimeError,
|
||||
"""
|
||||
|
||||
Function you are trying to compile cannot be converted to MLIR
|
||||
|
||||
%0 = x # EncryptedScalar<uint2> ∈ [1, 2]
|
||||
%1 = y # EncryptedScalar<uint5> ∈ [10, 20]
|
||||
%2 = left_shift(%0, %1) # EncryptedScalar<uint21> ∈ [2048, 1048576]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only up to 4-bit shifts are supported
|
||||
return %2
|
||||
|
||||
""", # noqa: E501
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_graph_converter_bad_convert(
|
||||
function,
|
||||
encryption_statuses,
|
||||
inputset,
|
||||
expected_error,
|
||||
expected_message,
|
||||
helpers,
|
||||
):
|
||||
"""
|
||||
Test unsupported graph conversion.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
compiler = cnp.Compiler(function, encryption_statuses)
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
compiler.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(expected_message, str(excinfo.value))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,inputset,expected_mlir",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: 1 + cnp.LookupTable([4, 1, 2, 3])[x] + cnp.LookupTable([4, 1, 2, 3])[x + 1],
|
||||
range(3),
|
||||
"""
|
||||
|
||||
module {
|
||||
func.func @main(%arg0: !FHE.eint<3>) -> !FHE.eint<3> {
|
||||
%c1_i4 = arith.constant 1 : i4
|
||||
%cst = arith.constant dense<[4, 1, 2, 3, 3, 3, 3, 3]> : tensor<8xi64>
|
||||
%0 = "FHE.apply_lookup_table"(%arg0, %cst) : (!FHE.eint<3>, tensor<8xi64>) -> !FHE.eint<3>
|
||||
%1 = "FHE.add_eint_int"(%arg0, %c1_i4) : (!FHE.eint<3>, i4) -> !FHE.eint<3>
|
||||
%2 = "FHE.add_eint_int"(%0, %c1_i4) : (!FHE.eint<3>, i4) -> !FHE.eint<3>
|
||||
%3 = "FHE.apply_lookup_table"(%1, %cst) : (!FHE.eint<3>, tensor<8xi64>) -> !FHE.eint<3>
|
||||
%4 = "FHE.add_eint"(%2, %3) : (!FHE.eint<3>, !FHE.eint<3>) -> !FHE.eint<3>
|
||||
return %4 : !FHE.eint<3>
|
||||
}
|
||||
}
|
||||
|
||||
""", # noqa: E501
|
||||
# Notice that there is only a single 1 and a single table cst above
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_constant_cache(function, inputset, expected_mlir, helpers):
|
||||
"""
|
||||
Test caching MLIR constants.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
circuit = compiler.compile(inputset, configuration)
|
||||
|
||||
helpers.check_str(expected_mlir, circuit.mlir)
|
||||
|
||||
|
||||
# pylint: enable=line-too-long
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.representation` namespace.
|
||||
"""
|
||||
371
frontends/concrete-python/tests/representation/test_graph.py
Normal file
371
frontends/concrete-python/tests/representation/test_graph.py
Normal file
@@ -0,0 +1,371 @@
|
||||
"""
|
||||
Tests of `Graph` class.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
import tests
|
||||
|
||||
tests_directory = os.path.dirname(tests.__file__)
|
||||
|
||||
|
||||
def g(z):
|
||||
"""
|
||||
Example function with a tag.
|
||||
"""
|
||||
|
||||
with cnp.tag("def"):
|
||||
a = 120 - z
|
||||
b = a // 4
|
||||
return b
|
||||
|
||||
|
||||
def f(x):
|
||||
"""
|
||||
Example function with nested tags.
|
||||
"""
|
||||
|
||||
with cnp.tag("abc"):
|
||||
x = x * 2
|
||||
with cnp.tag("foo"):
|
||||
y = x + 42
|
||||
z = np.sqrt(y).astype(np.int64)
|
||||
|
||||
return g(z + 3) * 2
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,inputset,tag_filter,operation_filter,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x + 1,
|
||||
range(5),
|
||||
None,
|
||||
None,
|
||||
3,
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + 42,
|
||||
range(10),
|
||||
None,
|
||||
None,
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + 42,
|
||||
range(50),
|
||||
None,
|
||||
None,
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + 1.2,
|
||||
[1.5, 4.2],
|
||||
None,
|
||||
None,
|
||||
-1,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
None,
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"",
|
||||
None,
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"abc",
|
||||
None,
|
||||
5,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
["abc", "def"],
|
||||
None,
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
re.compile(".*b.*"),
|
||||
None,
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"input",
|
||||
4,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"constant",
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"subgraph",
|
||||
3,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"add",
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
["subgraph", "add"],
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
re.compile("sub.*"),
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"abc.foo",
|
||||
"add",
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"abc",
|
||||
"floor_divide",
|
||||
-1,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_graph_maximum_integer_bit_width(
|
||||
function,
|
||||
inputset,
|
||||
tag_filter,
|
||||
operation_filter,
|
||||
expected_result,
|
||||
helpers,
|
||||
):
|
||||
"""
|
||||
Test `maximum_integer_bit_width` method of `Graph` class.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
graph = compiler.trace(inputset, configuration)
|
||||
|
||||
assert graph.maximum_integer_bit_width(tag_filter, operation_filter) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,inputset,tag_filter,operation_filter,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x + 42,
|
||||
range(-10, 10),
|
||||
None,
|
||||
None,
|
||||
(-10, 51),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + 1.2,
|
||||
[1.5, 4.2],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
None,
|
||||
(0, 120),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"",
|
||||
None,
|
||||
(0, 54),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"abc",
|
||||
None,
|
||||
(0, 18),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
["abc", "def"],
|
||||
None,
|
||||
(0, 120),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
re.compile(".*b.*"),
|
||||
None,
|
||||
(0, 60),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"input",
|
||||
(0, 9),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"constant",
|
||||
(2, 120),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"subgraph",
|
||||
(6, 7),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
"add",
|
||||
(9, 60),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
["subgraph", "add"],
|
||||
(6, 60),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
None,
|
||||
re.compile("sub.*"),
|
||||
(6, 111),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"abc.foo",
|
||||
"add",
|
||||
(42, 60),
|
||||
),
|
||||
pytest.param(
|
||||
f,
|
||||
range(10),
|
||||
"abc",
|
||||
"floor_divide",
|
||||
None,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_graph_integer_range(
|
||||
function,
|
||||
inputset,
|
||||
tag_filter,
|
||||
operation_filter,
|
||||
expected_result,
|
||||
helpers,
|
||||
):
|
||||
"""
|
||||
Test `integer_range` method of `Graph` class.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, {"x": "encrypted"})
|
||||
graph = compiler.trace(inputset, configuration)
|
||||
|
||||
assert graph.integer_range(tag_filter, operation_filter) == expected_result
|
||||
|
||||
|
||||
def test_graph_format_show_lines(helpers):
|
||||
"""
|
||||
Test `format` method of `Graph` class with show_lines=True.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(f, {"x": "encrypted"})
|
||||
graph = compiler.trace(range(10), configuration)
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
expected = f"""
|
||||
|
||||
%0 = x # EncryptedScalar<uint4> ∈ [0, 9] {tests_directory}/representation/test_graph.py:324
|
||||
%1 = 2 # ClearScalar<uint2> ∈ [2, 2] @ abc {tests_directory}/representation/test_graph.py:34
|
||||
%2 = multiply(%0, %1) # EncryptedScalar<uint5> ∈ [0, 18] @ abc {tests_directory}/representation/test_graph.py:34
|
||||
%3 = 42 # ClearScalar<uint6> ∈ [42, 42] @ abc.foo {tests_directory}/representation/test_graph.py:36
|
||||
%4 = add(%2, %3) # EncryptedScalar<uint6> ∈ [42, 60] @ abc.foo {tests_directory}/representation/test_graph.py:36
|
||||
%5 = subgraph(%4) # EncryptedScalar<uint3> ∈ [6, 7] @ abc {tests_directory}/representation/test_graph.py:37
|
||||
%6 = 3 # ClearScalar<uint2> ∈ [3, 3] {tests_directory}/representation/test_graph.py:39
|
||||
%7 = add(%5, %6) # EncryptedScalar<uint4> ∈ [9, 10] {tests_directory}/representation/test_graph.py:39
|
||||
%8 = 120 # ClearScalar<uint7> ∈ [120, 120] @ def {tests_directory}/representation/test_graph.py:23
|
||||
%9 = subtract(%8, %7) # EncryptedScalar<uint7> ∈ [110, 111] @ def {tests_directory}/representation/test_graph.py:23
|
||||
%10 = 4 # ClearScalar<uint3> ∈ [4, 4] @ def {tests_directory}/representation/test_graph.py:24
|
||||
%11 = floor_divide(%9, %10) # EncryptedScalar<uint5> ∈ [27, 27] @ def {tests_directory}/representation/test_graph.py:24
|
||||
%12 = 2 # ClearScalar<uint2> ∈ [2, 2] {tests_directory}/representation/test_graph.py:39
|
||||
%13 = multiply(%11, %12) # EncryptedScalar<uint6> ∈ [54, 54] {tests_directory}/representation/test_graph.py:39
|
||||
return %13
|
||||
|
||||
Subgraphs:
|
||||
|
||||
%5 = subgraph(%4):
|
||||
|
||||
%0 = input # EncryptedScalar<uint2> @ abc.foo {tests_directory}/representation/test_graph.py:36
|
||||
%1 = sqrt(%0) # EncryptedScalar<float64> @ abc {tests_directory}/representation/test_graph.py:37
|
||||
%2 = astype(%1, dtype=int_) # EncryptedScalar<uint1> @ abc {tests_directory}/representation/test_graph.py:37
|
||||
return %2
|
||||
|
||||
""" # noqa: E501
|
||||
# pylint: enable=line-too-long
|
||||
|
||||
actual = graph.format(show_locations=True)
|
||||
|
||||
assert (
|
||||
actual.strip() == expected.strip()
|
||||
), f"""
|
||||
|
||||
Expected Output
|
||||
===============
|
||||
{expected}
|
||||
|
||||
Actual Output
|
||||
=============
|
||||
{actual}
|
||||
|
||||
"""
|
||||
294
frontends/concrete-python/tests/representation/test_node.py
Normal file
294
frontends/concrete-python/tests/representation/test_node.py
Normal file
@@ -0,0 +1,294 @@
|
||||
"""
|
||||
Tests of `Node` class.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import UnsignedInteger
|
||||
from concrete.numpy.representation import Node
|
||||
from concrete.numpy.values import ClearScalar, EncryptedScalar, EncryptedTensor, Value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constant,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
"abc",
|
||||
ValueError,
|
||||
"Constant 'abc' is not supported",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_node_bad_constant(constant, expected_error, expected_message):
|
||||
"""
|
||||
Test `constant` function of `Node` class with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Node.constant(constant)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"node,args,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
Node.constant(1),
|
||||
["abc"],
|
||||
ValueError,
|
||||
"Evaluation of constant '1' node using 'abc' failed "
|
||||
"because of invalid number of arguments",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="add",
|
||||
inputs=[Value.of(4), Value.of(10, is_encrypted=True)],
|
||||
output=Value.of(14),
|
||||
operation=lambda x, y: x + y,
|
||||
),
|
||||
["abc"],
|
||||
ValueError,
|
||||
"Evaluation of generic 'add' node using 'abc' failed "
|
||||
"because of invalid number of arguments",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="add",
|
||||
inputs=[Value.of(4), Value.of(10, is_encrypted=True)],
|
||||
output=Value.of(14),
|
||||
operation=lambda x, y: x + y,
|
||||
),
|
||||
["abc", "def"],
|
||||
ValueError,
|
||||
"Evaluation of generic 'add' node using 'abc', 'def' failed "
|
||||
"because argument 'abc' is not valid",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="add",
|
||||
inputs=[Value.of([3, 4]), Value.of(10, is_encrypted=True)],
|
||||
output=Value.of([13, 14]),
|
||||
operation=lambda x, y: x + y,
|
||||
),
|
||||
[[1, 2, 3, 4], 10],
|
||||
ValueError,
|
||||
"Evaluation of generic 'add' node using [1, 2, 3, 4], 10 failed "
|
||||
"because argument [1, 2, 3, 4] does not have the expected shape of (2,)",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="unknown",
|
||||
inputs=[],
|
||||
output=Value.of(10),
|
||||
operation=lambda: "abc",
|
||||
),
|
||||
[],
|
||||
ValueError,
|
||||
"Evaluation of generic 'unknown' node resulted in 'abc' of type str "
|
||||
"which is not acceptable either because of the type or because of overflow",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="unknown",
|
||||
inputs=[],
|
||||
output=Value.of(10),
|
||||
operation=lambda: np.array(["abc", "def"]),
|
||||
),
|
||||
[],
|
||||
ValueError,
|
||||
"Evaluation of generic 'unknown' node resulted in array(['abc', 'def'], dtype='<U3') "
|
||||
"of type np.ndarray and of underlying type 'dtype[str_]' "
|
||||
"which is not acceptable because of the underlying type",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="unknown",
|
||||
inputs=[],
|
||||
output=Value.of(10),
|
||||
operation=lambda: [1, (), 3],
|
||||
),
|
||||
[],
|
||||
ValueError,
|
||||
"Evaluation of generic 'unknown' node resulted in [1, (), 3] of type list "
|
||||
"which is not acceptable either because of the type or because of overflow",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="unknown",
|
||||
inputs=[],
|
||||
output=Value.of(10),
|
||||
operation=lambda: [1, 2, 3],
|
||||
),
|
||||
[],
|
||||
ValueError,
|
||||
"Evaluation of generic 'unknown' node resulted in array([1, 2, 3]) "
|
||||
"which does not have the expected shape of ()",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_node_bad_call(node, args, expected_error, expected_message):
|
||||
"""
|
||||
Test `__call__` method of `Node` class.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
node(*args)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"node,predecessors,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
Node.constant(1),
|
||||
[],
|
||||
"1",
|
||||
),
|
||||
pytest.param(
|
||||
Node.input("x", EncryptedScalar(UnsignedInteger(3))),
|
||||
[],
|
||||
"x",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="tlu",
|
||||
inputs=[
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
operation=lambda x, table: table[x],
|
||||
kwargs={"table": np.array([4, 1, 3, 2])},
|
||||
),
|
||||
["%0"],
|
||||
"tlu(%0, table=[4 1 3 2])",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="index.static",
|
||||
inputs=[EncryptedTensor(UnsignedInteger(3), shape=(3,))],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3,)),
|
||||
operation=lambda x: x[slice(None, None, -1)],
|
||||
kwargs={"index": (slice(None, None, -1),)},
|
||||
),
|
||||
["%0"],
|
||||
"%0[::-1]",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="concatenate",
|
||||
inputs=[
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3, 6)),
|
||||
operation=lambda *args, **kwargs: np.concatenate(tuple(args), **kwargs),
|
||||
kwargs={"axis": 1},
|
||||
),
|
||||
["%0", "%1", "%2"],
|
||||
"concatenate((%0, %1, %2), axis=1)",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="array",
|
||||
inputs=[
|
||||
EncryptedScalar(UnsignedInteger(3)),
|
||||
ClearScalar(UnsignedInteger(3)),
|
||||
ClearScalar(UnsignedInteger(3)),
|
||||
EncryptedScalar(UnsignedInteger(3)),
|
||||
],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(2, 2)),
|
||||
operation=lambda *args: np.array(args).reshape((2, 2)),
|
||||
),
|
||||
["%0", "%1", "%2", "%3"],
|
||||
"array([[%0, %1], [%2, %3]])",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="assign.static",
|
||||
inputs=[EncryptedTensor(UnsignedInteger(3), shape=(3, 4))],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3, 4)),
|
||||
operation=lambda *args: args,
|
||||
kwargs={"index": (1, 2)},
|
||||
),
|
||||
["%0", "%1"],
|
||||
"(%0[1, 2] = %1)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_node_format(node, predecessors, expected_result):
|
||||
"""
|
||||
Test `format` method of `Node` class.
|
||||
"""
|
||||
|
||||
assert node.format(predecessors) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"node,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
Node.constant(1),
|
||||
"1",
|
||||
),
|
||||
pytest.param(
|
||||
Node.input("x", EncryptedScalar(UnsignedInteger(3))),
|
||||
"x",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="tlu",
|
||||
inputs=[
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
operation=lambda x, table: table[x],
|
||||
kwargs={"table": np.array([4, 1, 3, 2])},
|
||||
),
|
||||
"tlu",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="concatenate",
|
||||
inputs=[
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
EncryptedTensor(UnsignedInteger(3), shape=(3, 2)),
|
||||
],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3, 6)),
|
||||
operation=lambda *args, **kwargs: np.concatenate(tuple(args), **kwargs),
|
||||
kwargs={"axis": -1},
|
||||
),
|
||||
"concatenate",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="index.static",
|
||||
inputs=[EncryptedTensor(UnsignedInteger(3), shape=(3, 4))],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=()),
|
||||
operation=lambda *args: args,
|
||||
kwargs={"index": (1, 2)},
|
||||
),
|
||||
"□[1, 2]",
|
||||
),
|
||||
pytest.param(
|
||||
Node.generic(
|
||||
name="assign.static",
|
||||
inputs=[EncryptedTensor(UnsignedInteger(3), shape=(3, 4))],
|
||||
output=EncryptedTensor(UnsignedInteger(3), shape=(3, 4)),
|
||||
operation=lambda *args: args,
|
||||
kwargs={"index": (1, 2)},
|
||||
),
|
||||
"□[1, 2] = □",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_node_label(node, expected_result):
|
||||
"""
|
||||
Test `label` method of `Node` class.
|
||||
"""
|
||||
|
||||
assert node.label() == expected_result
|
||||
56
frontends/concrete-python/tests/representation/test_utils.py
Normal file
56
frontends/concrete-python/tests/representation/test_utils.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
Tests of utilities related to representation of computation.
|
||||
"""
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.representation.utils import format_constant
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constant,maximum_length,keep_newlines,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
1,
|
||||
45,
|
||||
True,
|
||||
"1",
|
||||
),
|
||||
pytest.param(
|
||||
np.uint32,
|
||||
45,
|
||||
True,
|
||||
"uintc",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4]]),
|
||||
45,
|
||||
True,
|
||||
"[[1 2]\n [3 4]]",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4]]),
|
||||
45,
|
||||
False,
|
||||
"[[1 2] [3 4]]",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 0], [1, 2], [3, 4], [5, 6]]),
|
||||
45,
|
||||
True,
|
||||
"[[1 2]\n [3 4]\n [5 6]\n...\n[1 2]\n [3 4]\n [5 6]]",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 0], [1, 2], [3, 4], [5, 6]]),
|
||||
45,
|
||||
False,
|
||||
"[[1 2] [3 4] [5 6] [ ... ] [1 2] [3 4] [5 6]]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_format_constant(constant, maximum_length, keep_newlines, expected_result):
|
||||
"""
|
||||
Test `format_constant` function.
|
||||
"""
|
||||
|
||||
assert format_constant(constant, maximum_length, keep_newlines) == expected_result
|
||||
3
frontends/concrete-python/tests/tracing/__init__.py
Normal file
3
frontends/concrete-python/tests/tracing/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.tracing` namespace.
|
||||
"""
|
||||
157
frontends/concrete-python/tests/tracing/test_tracer.py
Normal file
157
frontends/concrete-python/tests/tracing/test_tracer.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""
|
||||
Tests of `Tracer` class.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import UnsignedInteger
|
||||
from concrete.numpy.tracing import Tracer
|
||||
from concrete.numpy.tracing.typing import uint4
|
||||
from concrete.numpy.values import EncryptedTensor
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: np.ravel(x),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
RuntimeError,
|
||||
"Function 'np.ravel' is not supported",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.sum(x, initial=42),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
RuntimeError,
|
||||
"Function 'np.sum' is not supported with kwarg 'initial'",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.absolute(x, where=False),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(1, 2, 3))},
|
||||
RuntimeError,
|
||||
"Function 'np.absolute' is not supported with kwarg 'where'",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.multiply.outer(x, [1, 2, 3]),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(4,))},
|
||||
RuntimeError,
|
||||
"Only __call__ hook is supported for numpy ufuncs",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(uint4),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(4,))},
|
||||
ValueError,
|
||||
"`astype` method must be called with a "
|
||||
"numpy type for compilation (e.g., value.astype(np.int64))",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x + 1 if x else x + x,
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=())},
|
||||
RuntimeError,
|
||||
"Branching within circuits is not possible",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_tracer_bad_trace(function, parameters, expected_error, expected_message):
|
||||
"""
|
||||
Test `trace` function of `Tracer` class with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Tracer.trace(function, parameters)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.int8),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.int16),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.int32),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.uint8),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.uint16),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.uint32),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x.astype(np.uint64),
|
||||
{"x": EncryptedTensor(UnsignedInteger(7), shape=(3, 2))},
|
||||
(
|
||||
"Warning: When using `value.astype(newtype)` "
|
||||
"with an integer newtype, "
|
||||
"only use `np.int64` as the newtype "
|
||||
"to avoid unexpected overflows "
|
||||
"during inputset evaluation"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_tracer_warning_trace(function, parameters, expected_message, capsys):
|
||||
"""
|
||||
Test `trace` function of `Tracer` class with parameters that result in a warning.
|
||||
"""
|
||||
|
||||
Tracer.trace(function, parameters)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out.strip() == expected_message
|
||||
54
frontends/concrete-python/tests/tracing/test_typing.py
Normal file
54
frontends/concrete-python/tests/tracing/test_typing.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Test type annotations.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
def test_bad_tensor():
|
||||
"""
|
||||
Test `tensor` type with bad parameters
|
||||
"""
|
||||
|
||||
# invalid dtype
|
||||
# -------------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
def case1(x: cnp.tensor[int]):
|
||||
return x
|
||||
|
||||
case1(None)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"First argument to tensor annotations should be a "
|
||||
"concrete-numpy data type (e.g., cnp.uint4) not int"
|
||||
)
|
||||
|
||||
# no shape
|
||||
# --------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
def case2(x: cnp.tensor[cnp.uint3]):
|
||||
return x
|
||||
|
||||
case2(None)
|
||||
|
||||
assert str(excinfo.value) == (
|
||||
"Tensor annotations should have a shape (e.g., cnp.tensor[cnp.uint4, 3, 2])"
|
||||
)
|
||||
|
||||
# bad shape
|
||||
# ---------
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
|
||||
def case3(x: cnp.tensor[cnp.uint3, 1.5]):
|
||||
return x
|
||||
|
||||
case3(None)
|
||||
|
||||
assert str(excinfo.value) == "Tensor annotation shape elements must be 'int'"
|
||||
3
frontends/concrete-python/tests/values/__init__.py
Normal file
3
frontends/concrete-python/tests/values/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.values` namespace.
|
||||
"""
|
||||
321
frontends/concrete-python/tests/values/test_value.py
Normal file
321
frontends/concrete-python/tests/values/test_value.py
Normal file
@@ -0,0 +1,321 @@
|
||||
"""
|
||||
Tests of `Value` class.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Float, SignedInteger, UnsignedInteger
|
||||
from concrete.numpy.values import ClearScalar, ClearTensor, EncryptedScalar, EncryptedTensor, Value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,is_encrypted,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
True,
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
True,
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
False,
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
False,
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.int32(0),
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
0.0,
|
||||
False,
|
||||
Value(dtype=Float(64), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.float64(0.0),
|
||||
True,
|
||||
Value(dtype=Float(64), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
np.float32(0.0),
|
||||
False,
|
||||
Value(dtype=Float(32), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.float16(0.0),
|
||||
True,
|
||||
Value(dtype=Float(16), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
[True, False, True],
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(3,), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
[True, False, True],
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(3,), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
[0, 3, 1, 2],
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(2), shape=(4,), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0, 3, 1, 2], dtype=np.int32),
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(2), shape=(4,), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0.2, 3.4, 1.5, 2.0], dtype=np.float64),
|
||||
False,
|
||||
Value(dtype=Float(64), shape=(4,), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0.2, 3.4, 1.5, 2.0], dtype=np.float32),
|
||||
True,
|
||||
Value(dtype=Float(32), shape=(4,), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0.2, 3.4, 1.5, 2.0], dtype=np.float16),
|
||||
False,
|
||||
Value(dtype=Float(16), shape=(4,), is_encrypted=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_of(value, is_encrypted, expected_result):
|
||||
"""
|
||||
Test `of` function of `Value` class.
|
||||
"""
|
||||
|
||||
assert Value.of(value, is_encrypted) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,is_encrypted,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
"abc",
|
||||
False,
|
||||
ValueError,
|
||||
"Value cannot represent 'abc'",
|
||||
),
|
||||
pytest.param(
|
||||
[1, (), 3],
|
||||
False,
|
||||
ValueError,
|
||||
"Value cannot represent [1, (), 3]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_bad_of(value, is_encrypted, expected_error, expected_message):
|
||||
"""
|
||||
Test `of` function of `Value` class with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Value.of(value, is_encrypted)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs,rhs,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(5)),
|
||||
Value(dtype=SignedInteger(5), shape=(), is_encrypted=False),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
ClearTensor(UnsignedInteger(5), shape=(3, 2)),
|
||||
Value(dtype=UnsignedInteger(5), shape=(3, 2), is_encrypted=False),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
Value(dtype=SignedInteger(5), shape=(), is_encrypted=True),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(UnsignedInteger(5), shape=(3, 2)),
|
||||
Value(dtype=UnsignedInteger(5), shape=(3, 2), is_encrypted=True),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(UnsignedInteger(5), shape=(3, 2)),
|
||||
ClearScalar(SignedInteger(3)),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(3)),
|
||||
"ClearScalar(SignedInteger(3))",
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"ClearScalar(SignedInteger(3))",
|
||||
ClearScalar(SignedInteger(3)),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_eq(lhs, rhs, expected_result):
|
||||
"""
|
||||
Test `__eq__` method of `Value` class.
|
||||
"""
|
||||
|
||||
assert (lhs == rhs) == expected_result
|
||||
assert (rhs == lhs) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(5)),
|
||||
"ClearScalar<int5>",
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
"EncryptedScalar<int5>",
|
||||
),
|
||||
pytest.param(
|
||||
ClearTensor(SignedInteger(5), shape=(3, 2)),
|
||||
"ClearTensor<int5, shape=(3, 2)>",
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
"EncryptedTensor<int5, shape=(3, 2)>",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_str(data_type, expected_result):
|
||||
"""
|
||||
Test `__str__` method of `Value` class.
|
||||
"""
|
||||
|
||||
assert str(data_type) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(5)),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_is_clear(value, expected_result):
|
||||
"""
|
||||
Test `is_clear` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.is_clear == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_is_scalar(value, expected_result):
|
||||
"""
|
||||
Test `is_scalar` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.is_scalar == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3,)),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
2,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(5, 3, 2)),
|
||||
3,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_ndim(value, expected_result):
|
||||
"""
|
||||
Test `ndim` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.ndim == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3,)),
|
||||
3,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(5, 3, 2)),
|
||||
30,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(1,)),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(1, 1)),
|
||||
1,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_size(value, expected_result):
|
||||
"""
|
||||
Test `size` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.size == expected_result
|
||||
Reference in New Issue
Block a user