mirror of
https://github.com/zama-ai/concrete.git
synced 2026-02-08 19:44:57 -05:00
feat: rewrite tests
This commit is contained in:
@@ -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
4
.gitignore
vendored
@@ -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
|
||||
|
||||
2
Makefile
2
Makefile
@@ -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
3
tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy` namespace.
|
||||
"""
|
||||
3
tests/compilation/__init__.py
Normal file
3
tests/compilation/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.compilation` namespace.
|
||||
"""
|
||||
61
tests/compilation/test_artifacts.py
Normal file
61
tests/compilation/test_artifacts.py
Normal 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()
|
||||
131
tests/compilation/test_circuit.py
Normal file
131
tests/compilation/test_circuit.py
Normal 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>"
|
||||
)
|
||||
122
tests/compilation/test_compiler.py
Normal file
122
tests/compilation/test_compiler.py
Normal 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"
|
||||
28
tests/compilation/test_configuration.py
Normal file
28
tests/compilation/test_configuration.py
Normal 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
|
||||
86
tests/compilation/test_decorator.py
Normal file
86
tests/compilation/test_decorator.py
Normal 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
298
tests/conftest.py
Normal 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
3
tests/dtypes/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.dtypes` namespace.
|
||||
"""
|
||||
92
tests/dtypes/test_float.py
Normal file
92
tests/dtypes/test_float.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Tests of `Float` data type.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Float
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"bit_width,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
128,
|
||||
ValueError,
|
||||
"Float(128) is not supported (bit width must be one of 16, 32 or 64)",
|
||||
),
|
||||
pytest.param(
|
||||
"abc",
|
||||
ValueError,
|
||||
"Float('abc') is not supported (bit width must be one of 16, 32 or 64)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_float_bad_init(bit_width, expected_error, expected_message):
|
||||
"""
|
||||
Test `__init__` method of `Float` data type with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Float(bit_width)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs,rhs,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
Float(32),
|
||||
Float(32),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
Float(32),
|
||||
Float(64),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
Float(32),
|
||||
"Float(32)",
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"Float(32)",
|
||||
Float(32),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_float_eq(lhs, rhs, expected_result):
|
||||
"""
|
||||
Test `__eq__` method of `Float` data type.
|
||||
"""
|
||||
|
||||
assert (lhs == rhs) == expected_result
|
||||
assert (rhs == lhs) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
Float(16),
|
||||
"float16",
|
||||
),
|
||||
pytest.param(
|
||||
Float(32),
|
||||
"float32",
|
||||
),
|
||||
pytest.param(
|
||||
Float(64),
|
||||
"float64",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_float_str(data_type, expected_result):
|
||||
"""
|
||||
Test `__str__` method of `Float` data type.
|
||||
"""
|
||||
|
||||
assert str(data_type) == expected_result
|
||||
691
tests/dtypes/test_integer.py
Normal file
691
tests/dtypes/test_integer.py
Normal file
@@ -0,0 +1,691 @@
|
||||
"""
|
||||
Tests of `Integer` data type.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Integer, SignedInteger, UnsignedInteger
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,force_signed,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
-4,
|
||||
False,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-3,
|
||||
False,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-2,
|
||||
False,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
-1,
|
||||
False,
|
||||
SignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
False,
|
||||
UnsignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
1,
|
||||
False,
|
||||
UnsignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
2,
|
||||
False,
|
||||
UnsignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
3,
|
||||
False,
|
||||
UnsignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
4,
|
||||
False,
|
||||
UnsignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-4,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-3,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
-2,
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
-1,
|
||||
True,
|
||||
SignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
True,
|
||||
SignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
1,
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
2,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
3,
|
||||
True,
|
||||
SignedInteger(3),
|
||||
),
|
||||
pytest.param(
|
||||
4,
|
||||
True,
|
||||
SignedInteger(4),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0, 1]),
|
||||
False,
|
||||
UnsignedInteger(1),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0, 1]),
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
[-1, 1],
|
||||
False,
|
||||
SignedInteger(2),
|
||||
),
|
||||
pytest.param(
|
||||
[-1, 1],
|
||||
True,
|
||||
SignedInteger(2),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_that_can_represent(value, force_signed, expected_result):
|
||||
"""
|
||||
Test `that_can_represent` function of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert Integer.that_can_represent(value, force_signed) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,force_signed,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
"abc",
|
||||
False,
|
||||
ValueError,
|
||||
"Integer cannot represent 'abc'",
|
||||
),
|
||||
pytest.param(
|
||||
"abc",
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent 'abc'",
|
||||
),
|
||||
pytest.param(
|
||||
4.2,
|
||||
False,
|
||||
ValueError,
|
||||
"Integer cannot represent 4.2",
|
||||
),
|
||||
pytest.param(
|
||||
4.2,
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent 4.2",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([2.2, 1.1]),
|
||||
False,
|
||||
ValueError,
|
||||
"Integer cannot represent array([2.2, 1.1])",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([2.2, 1.1]),
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent array([2.2, 1.1])",
|
||||
),
|
||||
pytest.param(
|
||||
[1, (), 3],
|
||||
True,
|
||||
ValueError,
|
||||
"Integer cannot represent [1, (), 3]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_bad_that_can_represent(value, force_signed, expected_error, expected_message):
|
||||
"""
|
||||
Test `that_can_represent` function of `Integer` data type with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Integer.that_can_represent(value, force_signed)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constructor,bit_width,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
SignedInteger,
|
||||
0,
|
||||
ValueError,
|
||||
"SignedInteger(0) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger,
|
||||
0,
|
||||
ValueError,
|
||||
"UnsignedInteger(0) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger,
|
||||
-1,
|
||||
ValueError,
|
||||
"SignedInteger(-1) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger,
|
||||
-1,
|
||||
ValueError,
|
||||
"UnsignedInteger(-1) is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger,
|
||||
"abc",
|
||||
ValueError,
|
||||
"SignedInteger('abc') is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger,
|
||||
"abc",
|
||||
ValueError,
|
||||
"UnsignedInteger('abc') is not supported (bit width must be a positive integer)",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_bad_init(constructor, bit_width, expected_error, expected_message):
|
||||
"""
|
||||
Test `__init__` method of `Integer` data type with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
constructor(bit_width)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs,rhs,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
SignedInteger(5),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
UnsignedInteger(5),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
SignedInteger(6),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(6),
|
||||
SignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
UnsignedInteger(6),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(6),
|
||||
UnsignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
UnsignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
SignedInteger(5),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
"SignedInteger(5)",
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"SignedInteger(5)",
|
||||
SignedInteger(5),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_eq(lhs, rhs, expected_result):
|
||||
"""
|
||||
Test `__eq__` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert (lhs == rhs) == expected_result
|
||||
assert (rhs == lhs) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(4),
|
||||
"uint4",
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(7),
|
||||
"uint7",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(4),
|
||||
"int4",
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(7),
|
||||
"int7",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_str(data_type, expected_result):
|
||||
"""
|
||||
Test `__str__` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert str(data_type) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-1,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-4,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
-16,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_min(data_type, expected_result):
|
||||
"""
|
||||
Test `min` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert data_type.min() == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
7,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(5),
|
||||
31,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
3,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(5),
|
||||
15,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_max(data_type, expected_result):
|
||||
"""
|
||||
Test `max` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert data_type.max() == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
-1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(1),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
-1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(2),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
-1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
UnsignedInteger(3),
|
||||
4,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
-1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
1,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(1),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
-1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
2,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
3,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(2),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-4,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
-1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
0,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
1,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
2,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
3,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
SignedInteger(3),
|
||||
4,
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_integer_can_represent(data_type, value, expected_result):
|
||||
"""
|
||||
Test `can_represent` method of `Integer` data type.
|
||||
"""
|
||||
|
||||
assert data_type.can_represent(value) == expected_result
|
||||
77
tests/dtypes/test_utils.py
Normal file
77
tests/dtypes/test_utils.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
Tests of utilities related to data types.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Float, SignedInteger, UnsignedInteger
|
||||
from concrete.numpy.dtypes.utils import combine_dtypes
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dtypes,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
[Float(64), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(32), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(16), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(32), Float(16)],
|
||||
Float(32),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(16), Float(16)],
|
||||
Float(16),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), Float(64)],
|
||||
Float(64),
|
||||
),
|
||||
pytest.param(
|
||||
[Float(32), SignedInteger(5)],
|
||||
Float(32),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), Float(16)],
|
||||
Float(16),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), SignedInteger(6)],
|
||||
SignedInteger(6),
|
||||
),
|
||||
pytest.param(
|
||||
[UnsignedInteger(5), UnsignedInteger(6)],
|
||||
UnsignedInteger(6),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), UnsignedInteger(6)],
|
||||
SignedInteger(7),
|
||||
),
|
||||
pytest.param(
|
||||
[SignedInteger(5), UnsignedInteger(4)],
|
||||
SignedInteger(5),
|
||||
),
|
||||
pytest.param(
|
||||
[UnsignedInteger(6), SignedInteger(5)],
|
||||
SignedInteger(7),
|
||||
),
|
||||
pytest.param(
|
||||
[UnsignedInteger(4), SignedInteger(5)],
|
||||
SignedInteger(5),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_combine_dtypes(dtypes, expected_result):
|
||||
"""
|
||||
Test `combine_dtypes` function.
|
||||
"""
|
||||
|
||||
assert combine_dtypes(dtypes) == expected_result
|
||||
3
tests/execution/__init__.py
Normal file
3
tests/execution/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of execution.
|
||||
"""
|
||||
159
tests/execution/test_add.py
Normal file
159
tests/execution/test_add.py
Normal 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)
|
||||
176
tests/execution/test_concat.py
Normal file
176
tests/execution/test_concat.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""
|
||||
Tests of execution of concatenate operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function,parameters",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y)),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=0),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-2),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=None),
|
||||
{
|
||||
"x": {"shape": (3, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z)),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
"z": {"shape": (5, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=0),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
"z": {"shape": (5, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
"z": {"shape": (2, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=-1),
|
||||
{
|
||||
"x": {"shape": (2, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
"z": {"shape": (2, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=-2),
|
||||
{
|
||||
"x": {"shape": (4, 2)},
|
||||
"y": {"shape": (3, 2)},
|
||||
"z": {"shape": (5, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y, z: np.concatenate((x, y, z), axis=None),
|
||||
{
|
||||
"x": {"shape": (3, 4)},
|
||||
"y": {"shape": (2, 3)},
|
||||
"z": {"shape": (5, 1)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y)),
|
||||
{
|
||||
"x": {"shape": (3, 4, 2)},
|
||||
"y": {"shape": (5, 4, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=0),
|
||||
{
|
||||
"x": {"shape": (3, 4, 2)},
|
||||
"y": {"shape": (5, 4, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=1),
|
||||
{
|
||||
"x": {"shape": (2, 4, 5)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=2),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-1),
|
||||
{
|
||||
"x": {"shape": (2, 3, 4)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-2),
|
||||
{
|
||||
"x": {"shape": (2, 4, 5)},
|
||||
"y": {"shape": (2, 3, 5)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=-3),
|
||||
{
|
||||
"x": {"shape": (3, 4, 2)},
|
||||
"y": {"shape": (5, 4, 2)},
|
||||
},
|
||||
),
|
||||
pytest.param(
|
||||
lambda x, y: np.concatenate((x, y), axis=None),
|
||||
{
|
||||
"x": {"shape": (3, 4, 5)},
|
||||
"y": {"shape": (5, 2, 3)},
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_concatenate(function, parameters, helpers):
|
||||
"""
|
||||
Test concatenate.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses, configuration)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
206
tests/execution/test_convolution.py
Normal file
206
tests/execution/test_convolution.py
Normal 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"
|
||||
299
tests/execution/test_direct_table_lookup.py
Normal file
299
tests/execution/test_direct_table_lookup.py
Normal 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>"
|
||||
)
|
||||
47
tests/execution/test_dot.py
Normal file
47
tests/execution/test_dot.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Tests of execution of dot operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"size",
|
||||
[1, 4, 6, 10],
|
||||
)
|
||||
def test_dot(size, helpers):
|
||||
"""
|
||||
Test dot.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
bound = int(np.floor(np.sqrt(127 / size)))
|
||||
cst = np.random.randint(0, bound, size=(size,))
|
||||
|
||||
@cnp.compiler({"x": "encrypted"}, 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)
|
||||
158
tests/execution/test_matmul.py
Normal file
158
tests/execution/test_matmul.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
Tests of execution of matmul operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs_shape,rhs_shape,bounds",
|
||||
[
|
||||
pytest.param(
|
||||
(3, 2),
|
||||
(2, 3),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(1, 2),
|
||||
(2, 1),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 3),
|
||||
(3, 3),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 1),
|
||||
(1, 2),
|
||||
(0, 7),
|
||||
),
|
||||
pytest.param(
|
||||
(2,),
|
||||
(2,),
|
||||
(0, 7),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 5),
|
||||
(5,),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(5, 5),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(5, 3),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 3),
|
||||
(3,),
|
||||
(0, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(4, 5, 3),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(4, 5, 3),
|
||||
(3,),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5,),
|
||||
(2, 4, 5, 3),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 4, 5, 3),
|
||||
(3,),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 4, 3),
|
||||
(3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(4, 3),
|
||||
(5, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 5, 4, 3),
|
||||
(3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 4, 3),
|
||||
(1, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(1, 4, 3),
|
||||
(5, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(5, 4, 3),
|
||||
(2, 1, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 1, 4, 3),
|
||||
(5, 3, 2),
|
||||
(0, 5),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_matmul(lhs_shape, rhs_shape, bounds, helpers):
|
||||
"""
|
||||
Test matmul.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
minimum, maximum = bounds
|
||||
|
||||
lhs_cst = list(np.random.randint(minimum, maximum, size=lhs_shape))
|
||||
rhs_cst = list(np.random.randint(minimum, maximum, size=rhs_shape))
|
||||
|
||||
@cnp.compiler({"x": "encrypted"}, 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)
|
||||
76
tests/execution/test_mul.py
Normal file
76
tests/execution/test_mul.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Tests of execution of mul operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"function",
|
||||
[
|
||||
pytest.param(
|
||||
lambda x: x * 3,
|
||||
id="x * 3",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: 3 * x,
|
||||
id="3 * x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.dot(x, 3),
|
||||
id="np.dot(x, 3)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.dot(3, x),
|
||||
id="np.dot(3, x)",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x * np.array([1, 2, 3]),
|
||||
id="x * [1, 2, 3]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([1, 2, 3]) * x,
|
||||
id="[1, 2, 3] * x",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: x * np.array([[1, 2, 3], [3, 1, 2]]),
|
||||
id="x * [[1, 2, 3], [3, 1, 2]]",
|
||||
),
|
||||
pytest.param(
|
||||
lambda x: np.array([[1, 2, 3], [3, 1, 2]]) * x,
|
||||
id="[[1, 2, 3], [3, 1, 2]] * x",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"parameters",
|
||||
[
|
||||
{
|
||||
"x": {"range": [0, 40], "status": "encrypted"},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 40], "status": "encrypted", "shape": (3,)},
|
||||
},
|
||||
{
|
||||
"x": {"range": [0, 40], "status": "encrypted", "shape": (2, 3)},
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_constant_mul(function, parameters, helpers):
|
||||
"""
|
||||
Test mul where one of the operators is a constant.
|
||||
"""
|
||||
|
||||
parameter_encryption_statuses = helpers.generate_encryption_statuses(parameters)
|
||||
configuration = helpers.configuration()
|
||||
|
||||
compiler = cnp.Compiler(function, parameter_encryption_statuses, configuration)
|
||||
|
||||
inputset = helpers.generate_inputset(parameters)
|
||||
circuit = compiler.compile(inputset)
|
||||
|
||||
sample = helpers.generate_sample(parameters)
|
||||
helpers.check_execution(circuit, function, sample)
|
||||
46
tests/execution/test_neg.py
Normal file
46
tests/execution/test_neg.py
Normal 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)
|
||||
589
tests/execution/test_others.py
Normal file
589
tests/execution/test_others.py
Normal 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),
|
||||
)
|
||||
170
tests/execution/test_reshape.py
Normal file
170
tests/execution/test_reshape.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Tests of execution of reshape operation.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import concrete.numpy as cnp
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"shape,newshape",
|
||||
[
|
||||
pytest.param(
|
||||
(12,),
|
||||
(12, 1),
|
||||
),
|
||||
pytest.param(
|
||||
(12,),
|
||||
(1, 12),
|
||||
),
|
||||
pytest.param(
|
||||
(12,),
|
||||
(3, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(12,),
|
||||
(3, 2, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
12,
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(12,),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(4, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(2, 2, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(2, 3, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(3, 2, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(3, 1, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(12, 1),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
(-1,),
|
||||
),
|
||||
pytest.param(
|
||||
(3, 4),
|
||||
-1,
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
(3, 4),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
(4, 3),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 2, 3),
|
||||
(3, 2, 2),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 4, 5, 6),
|
||||
(6, 4, 30),
|
||||
),
|
||||
pytest.param(
|
||||
(6, 4, 30),
|
||||
(2, 3, 4, 5, 6),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 4, 5, 6),
|
||||
(2, 60, 6),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 60, 6),
|
||||
(2, 3, 4, 5, 6),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 2, 3, 4),
|
||||
(6, 6, -1),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 2, 3, 4),
|
||||
(6, -1, 12),
|
||||
),
|
||||
pytest.param(
|
||||
(2, 3, 2, 3, 4),
|
||||
(-1, 18, 4),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_reshape(shape, newshape, helpers):
|
||||
"""
|
||||
Test reshape.
|
||||
"""
|
||||
|
||||
configuration = helpers.configuration()
|
||||
|
||||
@cnp.compiler({"x": "encrypted"}, 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)
|
||||
193
tests/execution/test_static_indexing.py
Normal file
193
tests/execution/test_static_indexing.py
Normal 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"
|
||||
56
tests/execution/test_sub.py
Normal file
56
tests/execution/test_sub.py
Normal 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
114
tests/execution/test_sum.py
Normal 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)
|
||||
3
tests/internal/__init__.py
Normal file
3
tests/internal/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.internal` namespace.
|
||||
"""
|
||||
29
tests/internal/test_utils.py
Normal file
29
tests/internal/test_utils.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Tests of utilities related to the entire project.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.internal.utils import assert_that, unreachable
|
||||
|
||||
|
||||
def test_assert_that():
|
||||
"""
|
||||
Test `assert_that` function.
|
||||
"""
|
||||
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
assert_that(2 + 2 == 3, "no")
|
||||
|
||||
assert str(excinfo.value) == "no"
|
||||
|
||||
|
||||
def test_unreachable():
|
||||
"""
|
||||
Test `unreachable` function.
|
||||
"""
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
unreachable()
|
||||
|
||||
assert str(excinfo.value) == "Entered unreachable code"
|
||||
3
tests/mlir/__init__.py
Normal file
3
tests/mlir/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.mlir` namespace.
|
||||
"""
|
||||
337
tests/mlir/test_graph_converter.py
Normal file
337
tests/mlir/test_graph_converter.py
Normal 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
|
||||
3
tests/representation/__init__.py
Normal file
3
tests/representation/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.representation` namespace.
|
||||
"""
|
||||
47
tests/representation/test_graph.py
Normal file
47
tests/representation/test_graph.py
Normal 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
|
||||
237
tests/representation/test_node.py
Normal file
237
tests/representation/test_node.py
Normal 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
|
||||
56
tests/representation/test_utils.py
Normal file
56
tests/representation/test_utils.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
Tests of utilities related to representation of computation.
|
||||
"""
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.representation.utils import format_constant
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"constant,maximum_length,keep_newlines,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
1,
|
||||
45,
|
||||
True,
|
||||
"1",
|
||||
),
|
||||
pytest.param(
|
||||
np.uint32,
|
||||
45,
|
||||
True,
|
||||
"uintc",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4]]),
|
||||
45,
|
||||
True,
|
||||
"[[1 2]\n [3 4]]",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4]]),
|
||||
45,
|
||||
False,
|
||||
"[[1 2] [3 4]]",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 0], [1, 2], [3, 4], [5, 6]]),
|
||||
45,
|
||||
True,
|
||||
"[[1 2]\n [3 4]\n [5 6]\n...\n[1 2]\n [3 4]\n [5 6]]",
|
||||
),
|
||||
pytest.param(
|
||||
np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 0], [1, 2], [3, 4], [5, 6]]),
|
||||
45,
|
||||
False,
|
||||
"[[1 2] [3 4] [5 6] [ ... ] [1 2] [3 4] [5 6]]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_format_constant(constant, maximum_length, keep_newlines, expected_result):
|
||||
"""
|
||||
Test `format_constant` function.
|
||||
"""
|
||||
|
||||
assert format_constant(constant, maximum_length, keep_newlines) == expected_result
|
||||
3
tests/tracing/__init__.py
Normal file
3
tests/tracing/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.tracing` namespace.
|
||||
"""
|
||||
44
tests/tracing/test_tracer.py
Normal file
44
tests/tracing/test_tracer.py
Normal 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
3
tests/values/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Tests of `concrete.numpy.values` namespace.
|
||||
"""
|
||||
321
tests/values/test_value.py
Normal file
321
tests/values/test_value.py
Normal file
@@ -0,0 +1,321 @@
|
||||
"""
|
||||
Tests of `Value` class.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from concrete.numpy.dtypes import Float, SignedInteger, UnsignedInteger
|
||||
from concrete.numpy.values import ClearScalar, ClearTensor, EncryptedScalar, EncryptedTensor, Value
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,is_encrypted,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
True,
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
True,
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
False,
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
False,
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
0,
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.int32(0),
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
0.0,
|
||||
False,
|
||||
Value(dtype=Float(64), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.float64(0.0),
|
||||
True,
|
||||
Value(dtype=Float(64), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
np.float32(0.0),
|
||||
False,
|
||||
Value(dtype=Float(32), shape=(), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.float16(0.0),
|
||||
True,
|
||||
Value(dtype=Float(16), shape=(), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
[True, False, True],
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(1), shape=(3,), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
[True, False, True],
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(1), shape=(3,), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
[0, 3, 1, 2],
|
||||
False,
|
||||
Value(dtype=UnsignedInteger(2), shape=(4,), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0, 3, 1, 2], dtype=np.int32),
|
||||
True,
|
||||
Value(dtype=UnsignedInteger(2), shape=(4,), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0.2, 3.4, 1.5, 2.0], dtype=np.float64),
|
||||
False,
|
||||
Value(dtype=Float(64), shape=(4,), is_encrypted=False),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0.2, 3.4, 1.5, 2.0], dtype=np.float32),
|
||||
True,
|
||||
Value(dtype=Float(32), shape=(4,), is_encrypted=True),
|
||||
),
|
||||
pytest.param(
|
||||
np.array([0.2, 3.4, 1.5, 2.0], dtype=np.float16),
|
||||
False,
|
||||
Value(dtype=Float(16), shape=(4,), is_encrypted=False),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_of(value, is_encrypted, expected_result):
|
||||
"""
|
||||
Test `of` function of `Value` class.
|
||||
"""
|
||||
|
||||
assert Value.of(value, is_encrypted) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,is_encrypted,expected_error,expected_message",
|
||||
[
|
||||
pytest.param(
|
||||
"abc",
|
||||
False,
|
||||
ValueError,
|
||||
"Value cannot represent 'abc'",
|
||||
),
|
||||
pytest.param(
|
||||
[1, (), 3],
|
||||
False,
|
||||
ValueError,
|
||||
"Value cannot represent [1, (), 3]",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_bad_of(value, is_encrypted, expected_error, expected_message):
|
||||
"""
|
||||
Test `of` function of `Value` class with bad parameters.
|
||||
"""
|
||||
|
||||
with pytest.raises(expected_error) as excinfo:
|
||||
Value.of(value, is_encrypted)
|
||||
|
||||
assert str(excinfo.value) == expected_message
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"lhs,rhs,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(5)),
|
||||
Value(dtype=SignedInteger(5), shape=(), is_encrypted=False),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
ClearTensor(UnsignedInteger(5), shape=(3, 2)),
|
||||
Value(dtype=UnsignedInteger(5), shape=(3, 2), is_encrypted=False),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
Value(dtype=SignedInteger(5), shape=(), is_encrypted=True),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(UnsignedInteger(5), shape=(3, 2)),
|
||||
Value(dtype=UnsignedInteger(5), shape=(3, 2), is_encrypted=True),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(UnsignedInteger(5), shape=(3, 2)),
|
||||
ClearScalar(SignedInteger(3)),
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(3)),
|
||||
"ClearScalar(SignedInteger(3))",
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"ClearScalar(SignedInteger(3))",
|
||||
ClearScalar(SignedInteger(3)),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_eq(lhs, rhs, expected_result):
|
||||
"""
|
||||
Test `__eq__` method of `Value` class.
|
||||
"""
|
||||
|
||||
assert (lhs == rhs) == expected_result
|
||||
assert (rhs == lhs) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"data_type,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(5)),
|
||||
"ClearScalar<int5>",
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
"EncryptedScalar<int5>",
|
||||
),
|
||||
pytest.param(
|
||||
ClearTensor(SignedInteger(5), shape=(3, 2)),
|
||||
"ClearTensor<int5, shape=(3, 2)>",
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
"EncryptedTensor<int5, shape=(3, 2)>",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_str(data_type, expected_result):
|
||||
"""
|
||||
Test `__str__` method of `Value` class.
|
||||
"""
|
||||
|
||||
assert str(data_type) == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
ClearScalar(SignedInteger(5)),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_is_clear(value, expected_result):
|
||||
"""
|
||||
Test `is_clear` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.is_clear == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_is_scalar(value, expected_result):
|
||||
"""
|
||||
Test `is_scalar` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.is_scalar == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
0,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3,)),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
2,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(5, 3, 2)),
|
||||
3,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_ndim(value, expected_result):
|
||||
"""
|
||||
Test `ndim` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.ndim == expected_result
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected_result",
|
||||
[
|
||||
pytest.param(
|
||||
EncryptedScalar(SignedInteger(5)),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3,)),
|
||||
3,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(3, 2)),
|
||||
6,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(5, 3, 2)),
|
||||
30,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(1,)),
|
||||
1,
|
||||
),
|
||||
pytest.param(
|
||||
EncryptedTensor(SignedInteger(5), shape=(1, 1)),
|
||||
1,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_value_size(value, expected_result):
|
||||
"""
|
||||
Test `size` property of `Value` class.
|
||||
"""
|
||||
|
||||
assert value.size == expected_result
|
||||
Reference in New Issue
Block a user