feat: rewrite tests

This commit is contained in:
Umut
2022-04-04 13:32:24 +02:00
parent f5431be602
commit c45b9ffa43
41 changed files with 4977 additions and 4 deletions

View File

@@ -319,7 +319,7 @@ jobs:
id: coverage
if: ${{ always() && fromJSON(env.IS_REF_BUILD) && steps.pytest.outcome != 'skipped' && !cancelled() }}
run: |
./script/actions_utils/coverage.sh global-coverage-infos.json
./script/actions_utils/coverage.sh .global-coverage.json
- name: Comment with coverage
uses: marocchino/sticky-pull-request-comment@39c5b5dc7717447d0cba270cd115037d32d28443

4
.gitignore vendored
View File

@@ -52,7 +52,7 @@ diff-coverage.txt
*.py,cover
.hypothesis/
.pytest_cache/
global-coverage-infos.json
.global-coverage.json
# Translations
*.mo
@@ -133,5 +133,5 @@ dmypy.json
# Pyre type checker
.pyre/
# concrete compilation artifacts
# Compilation Artifacts
.artifacts

View File

@@ -85,7 +85,7 @@ pcc_internal: $(PCC_DEPS)
.PHONY: pytest # Run pytest
pytest:
poetry run pytest -svv \
--global-coverage-infos-json=global-coverage-infos.json \
--global-coverage=.global-coverage.json \
-n $$(./script/make_utils/ncpus.sh) \
--cov=$(SRC_DIR) --cov-fail-under=100 \
--randomly-dont-reorganize \

3
tests/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy` namespace.
"""

View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.compilation` namespace.
"""

View File

@@ -0,0 +1,61 @@
"""
Tests of `CompilationArtifacts` class.
"""
import tempfile
from pathlib import Path
from concrete.numpy.compilation import CompilationArtifacts, compiler
def test_artifacts_export(helpers):
"""
Test `export` method of `Compilation` class.
"""
with tempfile.TemporaryDirectory() as path:
tmpdir = Path(path)
configuration = helpers.configuration()
artifacts = CompilationArtifacts(tmpdir)
@compiler({"x": "encrypted"}, configuration=configuration, artifacts=artifacts)
def f(x):
return x + 10
inputset = range(100)
f.compile(inputset)
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 / "1.initial.graph.png").exists()
assert (tmpdir / "2.final.graph.txt").exists()
assert (tmpdir / "2.final.graph.png").exists()
assert (tmpdir / "bounds.txt").exists()
assert (tmpdir / "mlir.txt").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 / "1.initial.graph.png").exists()
assert (tmpdir / "2.final.graph.txt").exists()
assert (tmpdir / "2.final.graph.png").exists()
assert (tmpdir / "bounds.txt").exists()
assert (tmpdir / "mlir.txt").exists()

View File

@@ -0,0 +1,131 @@
"""
Tests of `Circuit` class.
"""
import tempfile
from pathlib import Path
import numpy as np
import pytest
from concrete.numpy.compilation import compiler
def test_circuit_str(helpers):
"""
Test `__str__` method of `Circuit` class.
"""
configuration = helpers.configuration()
@compiler({"x": "encrypted", "y": "encrypted"}, configuration=configuration)
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)
assert str(circuit) == (
"""
%0 = x # EncryptedScalar<uint4>
%1 = y # EncryptedScalar<uint5>
%2 = add(%0, %1) # EncryptedScalar<uint6>
return %2
""".strip()
)
def test_circuit_draw(helpers):
"""
Test `draw` method of `Circuit` class.
"""
configuration = helpers.configuration()
@compiler({"x": "encrypted", "y": "encrypted"}, configuration=configuration)
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)
with tempfile.TemporaryDirectory() as path:
tmpdir = Path(path)
png = tmpdir / "drawing.png"
circuit.draw(save_to=png)
assert png.exists()
def test_circuit_bad_run(helpers):
"""
Test `run` method of `Circuit` class with bad parameters.
"""
configuration = helpers.configuration()
@compiler({"x": "encrypted", "y": "encrypted"}, configuration=configuration)
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)
# with 1 argument
# ---------------
with pytest.raises(ValueError) as excinfo:
circuit.run(1)
assert str(excinfo.value) == "Expected 2 inputs but got 1"
# with 3 arguments
# ----------------
with pytest.raises(ValueError) as excinfo:
circuit.run(1, 2, 3)
assert str(excinfo.value) == "Expected 2 inputs but got 3"
# with negative argument 0
# ------------------------
with pytest.raises(ValueError) as excinfo:
circuit.run(-1, 11)
assert str(excinfo.value) == (
"Expected argument 0 to be EncryptedScalar<uint4> but it's EncryptedScalar<int1>"
)
# with negative argument 1
# ------------------------
with pytest.raises(ValueError) as excinfo:
circuit.run(1, -11)
assert str(excinfo.value) == (
"Expected argument 1 to be EncryptedScalar<uint5> but it's EncryptedScalar<int5>"
)
# with large argument 0
# ---------------------
with pytest.raises(ValueError) as excinfo:
circuit.run(100, 10)
assert str(excinfo.value) == (
"Expected argument 0 to be EncryptedScalar<uint4> but it's EncryptedScalar<uint7>"
)
# with large argument 1
# ---------------------
with pytest.raises(ValueError) as excinfo:
circuit.run(1, 100)
assert str(excinfo.value) == (
"Expected argument 1 to be EncryptedScalar<uint5> but it's EncryptedScalar<uint7>"
)

View File

@@ -0,0 +1,122 @@
"""
Tests of `Compiler` class.
"""
import pytest
from concrete.numpy.compilation import Compiler
def test_compiler_bad_init(helpers):
"""
Test `__init__` method of `Compiler` class with bad parameters.
"""
configuration = helpers.configuration()
def f(x, y, z):
return x + y + z
# missing all
# -----------
with pytest.raises(ValueError) as excinfo:
Compiler(f, {}, configuration=configuration)
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"}, configuration=configuration)
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"}, configuration=configuration)
assert str(excinfo.value) == (
"Encryption status of parameter 'x' of function 'f' is not provided"
)
# additional p
# ------------
# this is fine and `p` is just ignored
Compiler(
f,
{"x": "encrypted", "y": "encrypted", "z": "clear", "p": "clear"},
configuration=configuration,
)
def test_compiler_bad_call(helpers):
"""
Test `__call__` method of `Compiler` class with bad parameters.
"""
configuration = helpers.configuration()
def f(x, y, z):
return x + y + z
with pytest.raises(RuntimeError) as excinfo:
compiler = Compiler(
f,
{"x": "encrypted", "y": "encrypted", "z": "clear"},
configuration=configuration,
)
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()
def f(x, y, z):
return x + y + z
with pytest.raises(RuntimeError) as excinfo:
compiler = Compiler(
f,
{"x": "encrypted", "y": "encrypted", "z": "clear"},
configuration=configuration,
)
compiler.trace()
assert str(excinfo.value) == "Tracing function 'f' without an inputset 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
with pytest.raises(RuntimeError) as excinfo:
compiler = Compiler(
f,
{"x": "encrypted", "y": "encrypted", "z": "clear"},
configuration=configuration,
)
compiler.compile()
assert str(excinfo.value) == "Compiling function 'f' without an inputset is not supported"

View File

@@ -0,0 +1,28 @@
"""
Tests of `CompilationConfiguration` class.
"""
import pytest
from concrete.numpy.compilation import CompilationConfiguration
@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",
),
],
)
def test_configuration_bad_init(kwargs, expected_error, expected_message):
"""
Test `__init__` method of `CompilationConfiguration` class with bad parameters.
"""
with pytest.raises(expected_error) as excinfo:
CompilationConfiguration(**kwargs)
assert str(excinfo.value) == expected_message

View File

@@ -0,0 +1,86 @@
"""
Tests of `compiler` decorator.
"""
from concrete.numpy.compilation import CompilationArtifacts, compiler
def test_call_compile(helpers):
"""
Test `__call__` and `compile` methods of `compiler` decorator back to back.
"""
configuration = helpers.configuration()
@compiler({"x": "encrypted"}, configuration=configuration)
def function(x):
return x + 42
for i in range(10):
function(i)
circuit = function.compile()
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 = CompilationArtifacts()
@compiler({"x": "encrypted"}, configuration=configuration, artifacts=artifacts)
def function(x):
return x + 42
inputset = range(10)
function.trace(inputset, show_graph=True)
captured = capsys.readouterr()
assert captured.out.strip() == (
f"""
Computation Graph
------------------------------------------------
{str(list(artifacts.textual_representations_of_graphs.values())[-1])}
------------------------------------------------
""".strip()
)
def test_compiler_verbose_compile(helpers, capsys):
"""
Test `compile` method of `compiler` decorator with verbose flag.
"""
configuration = helpers.configuration()
artifacts = CompilationArtifacts()
@compiler({"x": "encrypted"}, configuration=configuration, artifacts=artifacts)
def function(x):
return x + 42
inputset = range(10)
function.compile(inputset, show_graph=True, show_mlir=True)
captured = capsys.readouterr()
assert captured.out.strip() == (
f"""
Computation Graph
--------------------------------------------------------------------------------
{list(artifacts.textual_representations_of_graphs.values())[-1]}
--------------------------------------------------------------------------------
MLIR
--------------------------------------------------------------------------------
{artifacts.mlir_to_compile}
--------------------------------------------------------------------------------
""".strip()
)

298
tests/conftest.py Normal file
View File

@@ -0,0 +1,298 @@
"""
Configuration of `pytest`.
"""
import json
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
from concrete.numpy.compilation import configuration as configuration_
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.
"""
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)}")
# pylint: disable=protected-access
configuration_._INSECURE_KEY_CACHE_LOCATION = str(key_cache_location)
# pylint: enable=protected-access
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.CompilationConfiguration:
"""
Get the test configuration to use during testing.
Returns:
cnp.CompilationConfiguration:
test configuration
"""
return cnp.CompilationConfiguration(
dump_artifacts_on_unexpected_failures=False,
enable_unsafe_features=True,
use_insecure_key_cache=True,
)
@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, 127])
assert minimum >= 0
assert maximum <= 127
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,
):
"""
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):
number of times to retry (for probabilistic execution)
"""
if not isinstance(sample, list):
sample = [sample]
for i in range(retries):
expected = function(*sample)
actual = circuit.run(*sample)
if not isinstance(expected, tuple):
expected = (expected,)
if not isinstance(actual, tuple):
actual = (actual,)
if all(np.array_equal(e, a) for e, a in zip(expected, actual)):
break
if i == retries - 1:
raise AssertionError(
f"""
Expected Output
===============
{expected}
Actual Output
=============
{actual}
"""
)
@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
"""
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
tests/dtypes/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.dtypes` namespace.
"""

View 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

View 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

View 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

View File

@@ -0,0 +1,3 @@
"""
Tests of execution.
"""

159
tests/execution/test_add.py Normal file
View File

@@ -0,0 +1,159 @@
"""
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)},
},
],
)
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, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
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_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, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample)

View 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, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample)

View File

@@ -0,0 +1,206 @@
"""
Tests of execution of convolution operation.
"""
import numpy as np
import pytest
import concrete.numpy as cnp
@pytest.mark.parametrize(
"input_shape,weight_shape",
[
pytest.param(
(1, 1, 4, 4),
(1, 1, 2, 2),
),
pytest.param(
(4, 3, 4, 4),
(2, 3, 2, 2),
),
],
)
@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, 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"}, configuration=configuration)
def function(x):
return cnp.conv2d(x, weight, bias, strides=strides, dilations=dilations)
inputset = [np.random.randint(0, 4, size=input_shape) for i in range(100)]
circuit = function.compile(inputset)
sample = np.random.randint(0, 4, size=input_shape, dtype=np.uint8)
helpers.check_execution(circuit, function, sample)
@pytest.mark.parametrize(
"input_shape,weight_shape,bias_shape,pads,strides,dilations,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),
"VALID",
ValueError,
"Auto pad should be in {'NOTSET'} but it's 'VALID'",
),
pytest.param(
(1, 1, 4, 4),
(1, 1, 2, 2),
(1,),
(),
(1, 1),
(1, 1),
"NOTSET",
ValueError,
"Pads should be of form "
"(height_begin_pad, width_begin_pad, height_end_pad, width_end_pad) "
"but it's ()",
),
pytest.param(
(1, 1, 4, 4),
(1, 1, 2, 2),
(1,),
(0, 0, 0, 0),
(),
(1, 1),
"NOTSET",
ValueError,
"Strides should be of form (height_stride, width_stride) but it's ()",
),
pytest.param(
(1, 1, 4, 4),
(1, 1, 2, 2),
(1,),
(0, 0, 0, 0),
(1, 1),
(),
"NOTSET",
ValueError,
"Dilations should be of form (height_dilation, width_dilation) but it's ()",
),
pytest.param(
(),
(1, 1, 2, 2),
(1,),
(0, 0, 0, 0),
(1, 1),
(1, 1),
"NOTSET",
ValueError,
"Input should be of shape (N, C, H, W) but it's of shape ()",
),
pytest.param(
(1, 1, 4, 4),
(),
(1,),
(0, 0, 0, 0),
(1, 1),
(1, 1),
"NOTSET",
ValueError,
"Weight should be of shape (F, C, H, W) but it's of shape ()",
),
pytest.param(
(1, 1, 4, 4),
(1, 1, 2, 2),
(),
(0, 0, 0, 0),
(1, 1),
(1, 1),
"NOTSET",
ValueError,
"Bias should be of shape (F,) but it's of shape ()",
),
],
)
def test_bad_conv2d_tracing(
input_shape,
weight_shape,
bias_shape,
pads,
strides,
dilations,
auto_pad,
expected_error,
expected_message,
helpers,
):
"""
Test conv2d 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"}, configuration=configuration)
def function(x):
return cnp.conv2d(x, weight, bias, pads, strides, dilations, 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)
assert str(excinfo.value) == expected_message
def test_bad_conv2d_evaluation():
"""
Test conv2d evaluation with bad parameters.
"""
x = np.random.randint(0, 4, size=(1, 1, 4, 4))
with pytest.raises(ValueError) as excinfo:
cnp.conv2d(x, "abc")
assert str(excinfo.value) == "Weight should be of type np.ndarray for evaluation"
weight = np.random.randint(0, 4, size=(1, 1, 2, 2))
with pytest.raises(ValueError) as excinfo:
cnp.conv2d(x, weight, "abc")
assert str(excinfo.value) == "Bias should be of type np.ndarray for evaluation"

View File

@@ -0,0 +1,299 @@
"""
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"}, configuration)
inputset = range(2 ** bits)
circuit = compiler.compile(inputset)
sample = int(np.random.randint(0, 2 ** bits))
helpers.check_execution(circuit, function, sample, retries=10)
# tensor
# ------
compiler = cnp.Compiler(function, {"x": "encrypted"}, configuration)
inputset = [np.random.randint(0, 2 ** bits, size=(3, 2), dtype=np.uint8) for _ in range(100)]
circuit = compiler.compile(inputset)
sample = np.random.randint(0, 2 ** bits, size=(3, 2), dtype=np.uint8)
helpers.check_execution(circuit, function, sample, retries=10)
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"}, configuration)
inputset = [np.random.randint(0, 2 ** 2, size=(3, 2), dtype=np.uint8) for _ in range(100)]
circuit = compiler.compile(inputset)
sample = np.random.randint(0, 2 ** 2, size=(3, 2), dtype=np.uint8)
helpers.check_execution(circuit, function, sample, retries=10)
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"}, configuration)
inputset = [1.5]
with pytest.raises(ValueError) as excinfo:
compiler.compile(inputset)
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"}, configuration)
inputset = [10, 5, 6, 2]
with pytest.raises(ValueError) as excinfo:
compiler.compile(inputset)
assert str(excinfo.value) == (
"LookupTable of shape (3, 2) cannot be looked up with EncryptedScalar<uint4>"
)

View 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"}, configuration=configuration)
def left_function(x):
return np.dot(x, cst)
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
def right_function(x):
return np.dot(cst, x)
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
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)
right_function_circuit = right_function.compile(inputset)
method_circuit = method.compile(inputset)
sample = np.random.randint(0, bound, size=(size,), dtype=np.uint8)
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)

View 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"}, configuration=configuration)
def lhs_operator(x):
return x @ rhs_cst
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
def rhs_operator(x):
return lhs_cst @ x
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
def lhs_function(x):
return np.matmul(x, rhs_cst)
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
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)
rhs_operator_circuit = rhs_operator.compile(rhs_inputset)
lhs_function_circuit = lhs_function.compile(lhs_inputset)
rhs_function_circuit = rhs_function.compile(rhs_inputset)
lhs_sample = np.random.randint(minimum, maximum, size=lhs_shape, dtype=np.uint8)
rhs_sample = np.random.randint(minimum, maximum, size=rhs_shape, dtype=np.uint8)
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)

View 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, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample)

View File

@@ -0,0 +1,46 @@
"""
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)},
},
],
)
def test_neg(parameters, helpers):
"""
Test neg.
"""
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
configuration = helpers.configuration()
@cnp.compiler(parameter_encryption_statuses, configuration=configuration)
def operator(x):
return -x
@cnp.compiler(parameter_encryption_statuses, configuration=configuration)
def function(x):
return np.negative(x)
inputset = helpers.generate_inputset(parameters)
operator_circuit = operator.compile(inputset)
function_circuit = function.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(operator_circuit, operator, sample)
helpers.check_execution(function_circuit, function, sample)

View File

@@ -0,0 +1,589 @@
"""
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.int32)
x_1 = x_1 + 1.5
x_2 = x.astype(np.int32)
x_2 = x_2 + 3.4
add = x_1 + x_2
add_int = add.astype(np.int32)
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.int32)
x_1 = x_1 + 1.5
x_p = x + 1
x_p2 = x_p + 1
x_2 = (x_p + x_p2).astype(np.int32)
x_2 = x_2 + 3.4
add = x_1 + x_2
add_int = add.astype(np.int32)
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.int32)
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.int32)
return t41
return function
# pylint: enable=invalid-name,too-many-locals,too-many-statements
@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.uint8),
{
"x": {"status": "encrypted", "range": [0, 127]},
},
id="(x / 3).astype(np.uint8)",
),
pytest.param(
lambda x: (127 / x).astype(np.uint8),
{
"x": {"status": "encrypted", "range": [1, 127]},
},
id="(127 / x).astype(np.uint8)",
),
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.int8) + 60,
{
"x": {"status": "encrypted", "range": [0, 127]},
},
id="(60 * np.sin(x)).astype(np.int8) + 60",
),
pytest.param(
lambda x: ((np.sin(x) ** 2) + (np.cos(x) ** 2)).astype(np.uint8),
{
"x": {"status": "encrypted", "range": [0, 127]},
},
id="((np.sin(x) ** 2) + (np.cos(x) ** 2)).astype(np.uint8)",
),
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(
lambda x: x + x.shape[0] + x.ndim + x.size,
{
"x": {"status": "encrypted", "range": [0, 15], "shape": (3, 2)},
},
id="x + shape[0] + x.ndim + x.size",
),
],
)
def test_others(function, parameters, helpers):
"""
Test others.
"""
# scalar
# ------
if "shape" not in parameters["x"]:
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
configuration = helpers.configuration()
compiler = cnp.Compiler(function, parameter_encryption_statuses, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample, retries=10)
# tensor
# ------
if "shape" not in parameters["x"]:
parameters["x"]["shape"] = (3, 2)
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
configuration = helpers.configuration()
compiler = cnp.Compiler(function, parameter_encryption_statuses, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample, retries=10)
def test_others_bad_fusing(helpers):
"""
Test others with bad fusing.
"""
configuration = helpers.configuration()
# two variable inputs
# -------------------
@cnp.compiler({"x": "encrypted", "y": "clear"}, configuration=configuration)
def function1(x, y):
return (10 * (np.sin(x) ** 2) + 10 * (np.cos(y) ** 2)).astype(np.uint8)
with pytest.raises(RuntimeError) as excinfo:
inputset = [(i, i) for i in range(100)]
function1.compile(inputset)
helpers.check_str(
# pylint: disable=line-too-long
"""
Function you are trying to compile cannot be converted to MLIR
%0 = 10 # ClearScalar<uint4>
%1 = 10 # ClearScalar<uint4>
%2 = 2 # ClearScalar<uint2>
%3 = 2 # ClearScalar<uint2>
%4 = x # EncryptedScalar<uint7>
%5 = y # ClearScalar<uint7>
%6 = sin(%4) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%7 = cos(%5) # ClearScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%8 = power(%6, %2) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%9 = power(%7, %3) # ClearScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%10 = multiply(%0, %8) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%11 = multiply(%1, %9) # ClearScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%12 = add(%10, %11) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%13 = astype(%12, dtype=ubyte) # EncryptedScalar<uint4>
return %13
""", # noqa: E501
# pylint: enable=line-too-long
str(excinfo.value),
)
# big intermediate constants
# --------------------------
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
def function2(x):
return (np.sin(x) * [[1, 2], [3, 4]]).astype(np.int8)
with pytest.raises(RuntimeError) as excinfo:
inputset = range(100)
function2.compile(inputset)
helpers.check_str(
# pylint: disable=line-too-long
"""
Function you are trying to compile cannot be converted to MLIR
%0 = [[1 2] [3 4]] # ClearTensor<uint3, shape=(2, 2)>
%1 = x # EncryptedScalar<uint7>
%2 = sin(%1) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%3 = multiply(%2, %0) # EncryptedTensor<float64, shape=(2, 2)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%4 = astype(%3, dtype=byte) # EncryptedTensor<int3, shape=(2, 2)>
return %4
""", # noqa: E501
# pylint: enable=line-too-long
str(excinfo.value),
)
# intermediates with different shape
# ----------------------------------
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
def function3(x):
return np.abs(np.sin(x)).reshape((2, 3)).astype(np.uint8)
with pytest.raises(RuntimeError) as excinfo:
inputset = [np.random.randint(0, 2 ** 7, size=(3, 2)) for _ in range(100)]
function3.compile(inputset)
helpers.check_str(
# pylint: disable=line-too-long
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # EncryptedTensor<uint7, shape=(3, 2)>
%1 = sin(%0) # EncryptedTensor<float64, shape=(3, 2)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%2 = absolute(%1) # EncryptedTensor<float64, shape=(3, 2)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%3 = reshape(%2, newshape=(2, 3)) # EncryptedTensor<float64, shape=(2, 3)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer operations are supported
%4 = astype(%3, dtype=ubyte) # EncryptedTensor<uint1, shape=(2, 3)>
return %4
""", # noqa: E501
# pylint: enable=line-too-long
str(excinfo.value),
)

View 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"}, configuration=configuration)
def function(x):
return np.reshape(x, newshape)
@cnp.compiler({"x": "encrypted"}, configuration=configuration)
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)
method_circuit = method.compile(inputset)
sample = np.random.randint(0, 2 ** 5, size=shape, dtype=np.uint8)
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"}, configuration=configuration)
def function(x):
return x.flatten()
inputset = [np.random.randint(0, 2 ** 5, size=shape) for i in range(100)]
circuit = function.compile(inputset)
sample = np.random.randint(0, 2 ** 5, size=shape, dtype=np.uint8)
helpers.check_execution(circuit, function, sample)

View File

@@ -0,0 +1,193 @@
"""
Tests of executiwhere x.shape == 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)",
),
],
)
def test_static_indexing(shape, function, helpers):
"""
Test static indexing.
"""
configuration = helpers.configuration()
compiler = cnp.Compiler(function, {"x": "encrypted"}, configuration)
inputset = [np.random.randint(0, 2 ** 5, size=shape) for _ in range(100)]
circuit = compiler.compile(inputset)
sample = np.random.randint(0, 2 ** 5, size=shape, dtype=np.uint8)
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"}, configuration)
inputset = [np.random.randint(0, 2 ** 3, size=(3,)) for _ in range(100)]
with pytest.raises(ValueError) as excinfo:
compiler.compile(inputset)
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"}, configuration)
inputset = [np.random.randint(0, 2 ** 3, size=(3,)) for _ in range(100)]
with pytest.raises(ValueError) as excinfo:
compiler.compile(inputset)
assert str(excinfo.value) == "Indexing with '1.5:2.5' is not supported"

View File

@@ -0,0 +1,56 @@
"""
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: 42 - x,
id="42 - x",
),
pytest.param(
lambda x: np.array([1, 2, 3]) - x,
id="[1, 2, 3] - x",
),
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, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample)

114
tests/execution/test_sum.py Normal file
View File

@@ -0,0 +1,114 @@
"""
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=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, configuration)
inputset = helpers.generate_inputset(parameters)
circuit = compiler.compile(inputset)
sample = helpers.generate_sample(parameters)
helpers.check_execution(circuit, function, sample)

View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.internal` namespace.
"""

View 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
tests/mlir/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.mlir` namespace.
"""

View File

@@ -0,0 +1,337 @@
"""
Tests of `GraphConverter` class.
"""
import numpy as np
import pytest
import concrete.numpy as cnp
# pylint: disable=line-too-long
@pytest.mark.parametrize(
"function,encryption_statuses,inputset,expected_error,expected_message",
[
pytest.param(
lambda x, y: (x - y, x + y),
{"x": "encrypted", "y": "clear"},
[(np.random.randint(0, 2 ** 3), np.random.randint(0, 2 ** 3)) for _ in range(100)],
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # EncryptedScalar<uint3>
%1 = y # ClearScalar<uint3>
%2 = subtract(%0, %1) # EncryptedScalar<int4>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only a single output is supported
%3 = add(%0, %1) # EncryptedScalar<uint4>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only a single output is supported
return (%2, %3)
""", # 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>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only unsigned integer inputs are supported
%1 = 1.5 # ClearScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only integer constants are supported
%2 = multiply(%0, %1) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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>
%1 = sin(%0) # EncryptedScalar<float64>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)>
%1 = y # ClearTensor<uint3, shape=(3, 2)>
%2 = concatenate((%0, %1)) # EncryptedTensor<uint3, shape=(6, 2)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only all encrypted concatenate is supported
return %2
""", # noqa: E501
),
pytest.param(
lambda x, w: cnp.conv2d(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)>
%1 = w # EncryptedTensor<uint1, shape=(1, 1, 1, 1)>
%2 = conv2d(%0, %1, [0], pads=(0, 0, 0, 0), strides=(1, 1), dilations=(1, 1)) # EncryptedTensor<uint1, shape=(1, 1, 4, 4)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only conv2d 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"},
[
(
np.random.randint(0, 2 ** 2, size=(1,)),
np.random.randint(0, 2 ** 2, size=(1,)),
)
for _ in range(100)
],
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # EncryptedTensor<uint2, shape=(1,)>
%1 = y # EncryptedTensor<uint2, shape=(1,)>
%2 = dot(%0, %1) # EncryptedScalar<uint4>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only dot product between encrypted and clear is supported
return %2
""", # noqa: E501
),
pytest.param(
lambda x: x[0],
{"x": "clear"},
[np.random.randint(0, 2 ** 3, size=(4,)) for _ in range(100)],
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # ClearTensor<uint3, shape=(4,)>
%1 = %0[0] # ClearScalar<uint3>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted indexing supported
return %1
""", # noqa: E501
),
pytest.param(
lambda x, y: x @ y,
{"x": "encrypted", "y": "encrypted"},
[
(
np.random.randint(0, 2 ** 2, size=(1, 1)),
np.random.randint(0, 2 ** 2, size=(1, 1)),
)
for _ in range(100)
],
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # EncryptedTensor<uint2, shape=(1, 1)>
%1 = y # EncryptedTensor<uint2, shape=(1, 1)>
%2 = matmul(%0, %1) # EncryptedTensor<uint4, shape=(1, 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"},
[(np.random.randint(0, 2 ** 3), np.random.randint(0, 2 ** 3)) for _ in range(100)],
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # EncryptedScalar<uint3>
%1 = y # EncryptedScalar<uint3>
%2 = multiply(%0, %1) # EncryptedScalar<uint6>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only multiplication between encrypted and clear is supported
return %2
""", # noqa: E501
),
pytest.param(
lambda x: -x,
{"x": "clear"},
[np.random.randint(0, 2 ** 3) for _ in range(100)],
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR
%0 = x # ClearScalar<uint3>
%1 = negative(%0) # ClearScalar<int4>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)>
%1 = reshape(%0, newshape=(3, 2)) # ClearTensor<uint3, shape=(3, 2)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted reshape is supported
return %1
""", # noqa: E501
),
pytest.param(
lambda x: x - 1,
{"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)>
%1 = 1 # ClearScalar<uint1>
%2 = subtract(%0, %1) # ClearTensor<int4, shape=(2, 3)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only subtraction of encrypted from clear is supported
return %2
""", # 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,)>
%1 = sum(%0) # ClearScalar<uint1>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only encrypted sum is supported
return %1
""", # noqa: E501
),
pytest.param(
lambda x, y: np.maximum(x, y),
{"x": "encrypted", "y": "clear"},
[
(np.random.randint(0, 2, size=(1,)), 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 # EncryptedTensor<uint1, shape=(1,)>
%1 = y # ClearTensor<uint1, shape=(1,)>
%2 = maximum(%0, %1) # EncryptedTensor<uint1, shape=(1,)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only single input table lookups are supported
return %2
""", # noqa: E501
),
pytest.param(
lambda x: np.maximum(x, np.array([3])),
{"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,)>
%1 = [3] # ClearTensor<uint2, shape=(1,)>
%2 = maximum(%0, %1) # ClearTensor<uint2, shape=(1,)>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one of the operands must be encrypted
return %2
""", # noqa: E501
),
pytest.param(
lambda x: x + 200,
{"x": "encrypted"},
range(200),
RuntimeError,
"""
Function you are trying to compile cannot be converted to MLIR:
%0 = x # EncryptedScalar<uint8>
%1 = 200 # ClearScalar<uint8>
%2 = add(%0, %1) # EncryptedScalar<uint9>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only up to 8-bit integers 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, configuration)
with pytest.raises(expected_error) as excinfo:
compiler.compile(inputset)
helpers.check_str(expected_message, str(excinfo.value))
# pylint: enable=line-too-long

View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.representation` namespace.
"""

View File

@@ -0,0 +1,47 @@
"""
Tests of `Graph` class.
"""
import pytest
import concrete.numpy as cnp
@pytest.mark.parametrize(
"function,inputset,expected_result",
[
pytest.param(
lambda x: x + 1,
range(5),
3,
),
pytest.param(
lambda x: x + 42,
range(10),
6,
),
pytest.param(
lambda x: x + 42,
range(50),
7,
),
pytest.param(
lambda x: x + 1.2,
[1.5, 4.2],
-1,
),
],
)
def test_graph_maximum_integer_bit_width(function, inputset, expected_result, helpers):
"""
Test `maximum_integer_bit_width` method of `Graph` class.
"""
configuration = helpers.configuration()
compiler = cnp.Compiler(function, {"x": "encrypted"}, configuration=configuration)
graph = compiler.trace(inputset)
print(graph.format())
assert graph.maximum_integer_bit_width() == expected_result

View File

@@ -0,0 +1,237 @@
"""
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 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="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)",
),
],
)
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",
),
],
)
def test_node_label(node, expected_result):
"""
Test `label` method of `Node` class.
"""
assert node.label() == expected_result

View 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

View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.tracing` namespace.
"""

View File

@@ -0,0 +1,44 @@
"""
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.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.multiply.outer(x, [1, 2, 3]),
{"x": EncryptedTensor(UnsignedInteger(7), shape=(4,))},
RuntimeError,
"Only __call__ hook is supported for numpy ufuncs",
),
],
)
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

3
tests/values/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
"""
Tests of `concrete.numpy.values` namespace.
"""

321
tests/values/test_value.py Normal file
View 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