From 54d792c7bf61868611d56d88b37d0f834ccd6a4f Mon Sep 17 00:00:00 2001 From: Umut Date: Wed, 22 Nov 2023 09:11:45 +0100 Subject: [PATCH] feat(frontend-python): add truncate bit pattern extension --- docs/SUMMARY.md | 1 + docs/_static/truncating/identity.png | Bin 0 -> 14045 bytes docs/_static/truncating/lsbs_to_remove.png | Bin 0 -> 87918 bytes docs/_static/truncating/msbs_to_keep.png | Bin 0 -> 87100 bytes docs/tutorial/truncating.md | 281 +++++++++++ .../concrete-python/concrete/fhe/__init__.py | 2 + .../concrete/fhe/compilation/compiler.py | 5 +- .../concrete/fhe/compilation/configuration.py | 4 + .../concrete/fhe/extensions/__init__.py | 1 + .../fhe/extensions/truncate_bit_pattern.py | 257 ++++++++++ .../concrete/fhe/mlir/context.py | 39 ++ .../concrete/fhe/mlir/converter.py | 18 + .../fhe/mlir/processors/assign_bit_widths.py | 4 + .../concrete/fhe/mlir/utils.py | 17 +- frontends/concrete-python/requirements.txt | 1 + .../execution/test_truncate_bit_pattern.py | 467 ++++++++++++++++++ .../extensions/test_truncate_bit_pattern.py | 37 ++ .../tests/mlir/test_converter.py | 17 + 18 files changed, 1149 insertions(+), 2 deletions(-) create mode 100644 docs/_static/truncating/identity.png create mode 100644 docs/_static/truncating/lsbs_to_remove.png create mode 100644 docs/_static/truncating/msbs_to_keep.png create mode 100644 docs/tutorial/truncating.md create mode 100644 frontends/concrete-python/concrete/fhe/extensions/truncate_bit_pattern.py create mode 100644 frontends/concrete-python/tests/execution/test_truncate_bit_pattern.py create mode 100644 frontends/concrete-python/tests/extensions/test_truncate_bit_pattern.py diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index fe8c09b22..ae8d714c7 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -23,6 +23,7 @@ * [Min/Max Operations](tutorial/minmax.md) * [Bitwise Operations](tutorial/bitwise.md) * [Table Lookups](tutorial/table\_lookups.md) +* [Truncating](tutorial/truncating.md) * [Rounding](tutorial/rounding.md) * [Floating Points](tutorial/floating\_points.md) * [Multi Precision](tutorial/multi\_precision.md) diff --git a/docs/_static/truncating/identity.png b/docs/_static/truncating/identity.png new file mode 100644 index 0000000000000000000000000000000000000000..7a85a7bf939b53f04b3730cef19ee326bbd3a52a GIT binary patch literal 14045 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

Gi_H;U7z?8J{AKKu?6NK@23{PuDuDOYwbo;9`l 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 literal 0 HcmV?d00001 diff --git a/docs/_static/truncating/msbs_to_keep.png b/docs/_static/truncating/msbs_to_keep.png new file mode 100644 index 0000000000000000000000000000000000000000..4616018c219e1ac0c7e28726f6bab56e27b1b0f2 GIT binary patch literal 87100 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 aLb)l=&oo83jONlLFkJWDw+Z^zKm8Zv8xQON literal 0 HcmV?d00001 diff --git a/docs/tutorial/truncating.md b/docs/tutorial/truncating.md new file mode 100644 index 000000000..a812d88dc --- /dev/null +++ b/docs/tutorial/truncating.md @@ -0,0 +1,281 @@ +# Truncating + +Table lookups have a strict constraint on the number of bits they support. This can be limiting, especially if you don't need exact precision. As well as this, using larger bit-widths leads to slower table lookups. + +To overcome these issues, truncated table lookups are introduced. This operation provides a way to zero the least significant bits of a large integer and then apply the table lookup on the resulting (smaller) value. + +Imagine you have a 5-bit value, you can use `fhe.truncate_bit_pattern(value, lsbs_to_remove=2)` to truncate it (here the last 2 bits are discarded). Once truncated, value will remain in 5-bits (e.g., 22 = 0b10110 would be truncated to 20 = 0b10100), and the last 2 bits of it would be zero. Concrete uses this to optimize table lookups on the truncated value, the 5-bit table lookup gets optimized to a 3-bit table lookup, which is much faster! + +Let's see how truncation works in practice: + +```python +import matplotlib.pyplot as plt +import numpy as np +from concrete import fhe + +original_bit_width = 5 +lsbs_to_remove = 2 + +assert 0 < lsbs_to_remove < original_bit_width + +original_values = list(range(2**original_bit_width)) +truncated_values = [ + fhe.truncate_bit_pattern(value, lsbs_to_remove) + for value in original_values +] + +previous_truncated = truncated_values[0] +for original, truncated in zip(original_values, truncated_values): + if truncated != previous_truncated: + previous_truncated = truncated + print() + + original_binary = np.binary_repr(original, width=(original_bit_width + 1)) + truncated_binary = np.binary_repr(truncated, width=(original_bit_width + 1)) + + print( + f"{original:2} = 0b_{original_binary[:-lsbs_to_remove]}[{original_binary[-lsbs_to_remove:]}] " + f"=> " + f"0b_{truncated_binary[:-lsbs_to_remove]}[{truncated_binary[-lsbs_to_remove:]}] = {truncated}" + ) + +fig = plt.figure() +ax = fig.add_subplot() + +plt.plot(original_values, original_values, label="original", color="black") +plt.plot(original_values, truncated_values, label="truncated", color="green") +plt.legend() + +ax.set_aspect("equal", adjustable="box") +plt.show() +``` + +prints: + +``` + 0 = 0b_0000[00] => 0b_0000[00] = 0 + 1 = 0b_0000[01] => 0b_0000[00] = 0 + 2 = 0b_0000[10] => 0b_0000[00] = 0 + 3 = 0b_0000[11] => 0b_0000[00] = 0 + + 4 = 0b_0001[00] => 0b_0001[00] = 4 + 5 = 0b_0001[01] => 0b_0001[00] = 4 + 6 = 0b_0001[10] => 0b_0001[00] = 4 + 7 = 0b_0001[11] => 0b_0001[00] = 4 + + 8 = 0b_0010[00] => 0b_0010[00] = 8 + 9 = 0b_0010[01] => 0b_0010[00] = 8 +10 = 0b_0010[10] => 0b_0010[00] = 8 +11 = 0b_0010[11] => 0b_0010[00] = 8 + +12 = 0b_0011[00] => 0b_0011[00] = 12 +13 = 0b_0011[01] => 0b_0011[00] = 12 +14 = 0b_0011[10] => 0b_0011[00] = 12 +15 = 0b_0011[11] => 0b_0011[00] = 12 + +16 = 0b_0100[00] => 0b_0100[00] = 16 +17 = 0b_0100[01] => 0b_0100[00] = 16 +18 = 0b_0100[10] => 0b_0100[00] = 16 +19 = 0b_0100[11] => 0b_0100[00] = 16 + +20 = 0b_0101[00] => 0b_0101[00] = 20 +21 = 0b_0101[01] => 0b_0101[00] = 20 +22 = 0b_0101[10] => 0b_0101[00] = 20 +23 = 0b_0101[11] => 0b_0101[00] = 20 + +24 = 0b_0110[00] => 0b_0110[00] = 24 +25 = 0b_0110[01] => 0b_0110[00] = 24 +26 = 0b_0110[10] => 0b_0110[00] = 24 +27 = 0b_0110[11] => 0b_0110[00] = 24 + +28 = 0b_0111[00] => 0b_0111[00] = 28 +29 = 0b_0111[01] => 0b_0111[00] = 28 +30 = 0b_0111[10] => 0b_0111[00] = 28 +31 = 0b_0111[11] => 0b_0111[00] = 28 +``` + +and displays: + +![](../\_static/truncating/identity.png) + +Now, let's see how truncating can be used in FHE. + +```python +import itertools +import time + +import matplotlib.pyplot as plt +import numpy as np +from concrete import fhe + +configuration = fhe.Configuration( + enable_unsafe_features=True, + use_insecure_key_cache=True, + insecure_key_cache_location=".keys", +) + +input_bit_width = 6 +input_range = np.array(range(2**input_bit_width)) + +timings = {} +results = {} + +for lsbs_to_remove in range(input_bit_width): + @fhe.compiler({"x": "encrypted"}) + def f(x): + return fhe.truncate_bit_pattern(x, lsbs_to_remove) ** 2 + + circuit = f.compile(inputset=[input_range], configuration=configuration) + circuit.keygen() + + encrypted_sample = circuit.encrypt(input_range) + start = time.time() + encrypted_result = circuit.run(encrypted_sample) + end = time.time() + result = circuit.decrypt(encrypted_result) + + took = end - start + + timings[lsbs_to_remove] = took + results[lsbs_to_remove] = result + +number_of_figures = len(results) + +columns = 1 +for i in range(2, number_of_figures): + if number_of_figures % i == 0: + columns = i +rows = number_of_figures // columns + +fig, axs = plt.subplots(rows, columns) +axs = axs.flatten() + +baseline = timings[0] +for lsbs_to_remove in range(input_bit_width): + timing = timings[lsbs_to_remove] + speedup = baseline / timing + print(f"lsbs_to_remove={lsbs_to_remove} => {speedup:.2f}x speedup") + + axs[lsbs_to_remove].set_title(f"lsbs_to_remove={lsbs_to_remove}") + axs[lsbs_to_remove].plot(input_range, results[lsbs_to_remove]) + +plt.show() +``` + +prints: + +``` +lsbs_to_remove=0 => 1.00x speedup +lsbs_to_remove=1 => 1.69x speedup +lsbs_to_remove=2 => 3.48x speedup +lsbs_to_remove=3 => 3.06x speedup +lsbs_to_remove=4 => 3.46x speedup +lsbs_to_remove=5 => 3.14x speedup +``` + +{% hint style="info" %} +These speed-ups can vary from system to system. +{% endhint %} + +{% hint style="info" %} +The reason why the speed-up is not increasing with `lsbs_to_remove` is because the truncating operation itself has a cost: each bit removal is a PBS. Therefore, if a lot of bits are removed, truncation itself could take longer than the bigger TLU which is evaluated afterwards. +{% endhint %} + +and displays: + +![](../\_static/truncating/lsbs_to_remove.png) + +## Auto Truncators + +Truncating is very useful but, in some cases, you don't know how many bits your input contains, so it's not reliable to specify `lsbs_to_remove` manually. For this reason, the `AutoTruncator` class is introduced. + +`AutoTruncator` allows you to set how many of the most significant bits to keep, but they need to be adjusted using an inputset to determine how many of the least significant bits to remove. This can be done manually using `fhe.AutoTruncator.adjust(function, inputset)`, or by setting `auto_adjust_truncators` configuration to `True` during compilation. + +Here is how auto truncators can be used in FHE: + +```python +import itertools +import time + +import matplotlib.pyplot as plt +import numpy as np +from concrete import fhe + +configuration = fhe.Configuration( + enable_unsafe_features=True, + use_insecure_key_cache=True, + insecure_key_cache_location=".keys", + single_precision=False, + parameter_selection_strategy=fhe.ParameterSelectionStrategy.MULTI, +) + +input_bit_width = 6 +input_range = np.array(range(2**input_bit_width)) + +timings = {} +results = {} + +for target_msbs in reversed(range(1, input_bit_width + 1)): + truncator = fhe.AutoTruncator(target_msbs) + + @fhe.compiler({"x": "encrypted"}) + def f(x): + return fhe.truncate_bit_pattern(x, lsbs_to_remove=truncator) ** 2 + + fhe.AutoTruncator.adjust(f, inputset=[input_range]) + + circuit = f.compile(inputset=[input_range], configuration=configuration) + circuit.keygen() + + encrypted_sample = circuit.encrypt(input_range) + start = time.time() + encrypted_result = circuit.run(encrypted_sample) + end = time.time() + result = circuit.decrypt(encrypted_result) + + took = end - start + + timings[target_msbs] = took + results[target_msbs] = result + +number_of_figures = len(results) + +columns = 1 +for i in range(2, number_of_figures): + if number_of_figures % i == 0: + columns = i +rows = number_of_figures // columns + +fig, axs = plt.subplots(rows, columns) +axs = axs.flatten() + +baseline = timings[input_bit_width] +for i, target_msbs in enumerate(reversed(range(1, input_bit_width + 1))): + timing = timings[target_msbs] + speedup = baseline / timing + print(f"target_msbs={target_msbs} => {speedup:.2f}x speedup") + + axs[i].set_title(f"target_msbs={target_msbs}") + axs[i].plot(input_range, results[target_msbs]) + +plt.show() +``` + +prints: + +``` +target_msbs=6 => 1.00x speedup +target_msbs=5 => 1.80x speedup +target_msbs=4 => 3.47x speedup +target_msbs=3 => 3.02x speedup +target_msbs=2 => 3.38x speedup +target_msbs=1 => 3.37x speedup +``` + +and displays: + +![](../\_static/truncating/msbs_to_keep.png) + +{% hint style="warning" %} +`AutoTruncator`s should be defined outside the function that is being compiled. They are used to store the result of the adjustment process, so they shouldn't be created each time the function is called. Furthermore, each `AutoTruncator` should be used with exactly one `truncate_bit_pattern` call. +{% endhint %} diff --git a/frontends/concrete-python/concrete/fhe/__init__.py b/frontends/concrete-python/concrete/fhe/__init__.py index 60c909207..50ca292fb 100644 --- a/frontends/concrete-python/concrete/fhe/__init__.py +++ b/frontends/concrete-python/concrete/fhe/__init__.py @@ -28,6 +28,7 @@ from .compilation import ( from .compilation.decorators import circuit, compiler from .extensions import ( AutoRounder, + AutoTruncator, LookupTable, array, conv, @@ -39,6 +40,7 @@ from .extensions import ( ones_like, round_bit_pattern, tag, + truncate_bit_pattern, univariate, zero, zeros, diff --git a/frontends/concrete-python/concrete/fhe/compilation/compiler.py b/frontends/concrete-python/concrete/fhe/compilation/compiler.py index 524aee2f8..e5bcb2d36 100644 --- a/frontends/concrete-python/concrete/fhe/compilation/compiler.py +++ b/frontends/concrete-python/concrete/fhe/compilation/compiler.py @@ -14,7 +14,7 @@ from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union import numpy as np from concrete.compiler import CompilationContext -from ..extensions import AutoRounder +from ..extensions import AutoRounder, AutoTruncator from ..mlir import GraphConverter from ..representation import Graph from ..tracing import Tracer @@ -279,6 +279,9 @@ class Compiler: if self.configuration.auto_adjust_rounders: AutoRounder.adjust(self.function, self.inputset) + if self.configuration.auto_adjust_truncators: + AutoTruncator.adjust(self.function, self.inputset) + if self.graph is None: try: first_sample = next(iter(self.inputset)) diff --git a/frontends/concrete-python/concrete/fhe/compilation/configuration.py b/frontends/concrete-python/concrete/fhe/compilation/configuration.py index 541560883..cc2008b1f 100644 --- a/frontends/concrete-python/concrete/fhe/compilation/configuration.py +++ b/frontends/concrete-python/concrete/fhe/compilation/configuration.py @@ -880,6 +880,7 @@ class Configuration: global_p_error: Optional[float] insecure_key_cache_location: Optional[str] auto_adjust_rounders: bool + auto_adjust_truncators: bool single_precision: bool parameter_selection_strategy: ParameterSelectionStrategy show_progress: bool @@ -913,6 +914,7 @@ class Configuration: p_error: Optional[float] = None, global_p_error: Optional[float] = None, auto_adjust_rounders: bool = False, + auto_adjust_truncators: bool = False, single_precision: bool = False, parameter_selection_strategy: Union[ ParameterSelectionStrategy, str @@ -959,6 +961,7 @@ class Configuration: self.p_error = p_error self.global_p_error = global_p_error self.auto_adjust_rounders = auto_adjust_rounders + self.auto_adjust_truncators = auto_adjust_truncators self.single_precision = single_precision self.parameter_selection_strategy = ParameterSelectionStrategy.parse( parameter_selection_strategy @@ -1035,6 +1038,7 @@ class Configuration: p_error: Union[Keep, Optional[float]] = KEEP, global_p_error: Union[Keep, Optional[float]] = KEEP, auto_adjust_rounders: Union[Keep, bool] = KEEP, + auto_adjust_truncators: Union[Keep, bool] = KEEP, single_precision: Union[Keep, bool] = KEEP, parameter_selection_strategy: Union[Keep, Union[ParameterSelectionStrategy, str]] = KEEP, show_progress: Union[Keep, bool] = KEEP, diff --git a/frontends/concrete-python/concrete/fhe/extensions/__init__.py b/frontends/concrete-python/concrete/fhe/extensions/__init__.py index 8c9df830f..80d55971e 100644 --- a/frontends/concrete-python/concrete/fhe/extensions/__init__.py +++ b/frontends/concrete-python/concrete/fhe/extensions/__init__.py @@ -11,5 +11,6 @@ from .ones import one, ones, ones_like from .round_bit_pattern import AutoRounder, round_bit_pattern from .table import LookupTable from .tag import tag +from .truncate_bit_pattern import AutoTruncator, truncate_bit_pattern from .univariate import univariate from .zeros import zero, zeros, zeros_like diff --git a/frontends/concrete-python/concrete/fhe/extensions/truncate_bit_pattern.py b/frontends/concrete-python/concrete/fhe/extensions/truncate_bit_pattern.py new file mode 100644 index 000000000..100909e92 --- /dev/null +++ b/frontends/concrete-python/concrete/fhe/extensions/truncate_bit_pattern.py @@ -0,0 +1,257 @@ +""" +Declaration of `truncate_bit_pattern` extension. +""" + +import threading +from copy import deepcopy +from typing import Any, Callable, Dict, Iterable, List, Tuple, Union + +import numpy as np + +from ..dtypes import Integer +from ..mlir.utils import MAXIMUM_TLU_BIT_WIDTH +from ..representation import Node +from ..tracing import Tracer +from ..values import ValueDescription + +local = threading.local() + +# pylint: disable=protected-access +local._is_adjusting = False +# pylint: enable=protected-access + + +class Adjusting(BaseException): + """ + Adjusting class, to be used as early stop signal during adjustment. + """ + + truncator: "AutoTruncator" + input_min: int + input_max: int + + def __init__(self, truncator: "AutoTruncator", input_min: int, input_max: int): + super().__init__() + self.truncator = truncator + self.input_min = input_min + self.input_max = input_max + + +class AutoTruncator: + """ + AutoTruncator class, to optimize for the number of msbs to keep during truncate operation. + """ + + target_msbs: int + + is_adjusted: bool + input_min: int + input_max: int + input_bit_width: int + lsbs_to_remove: int + + def __init__(self, target_msbs: int = MAXIMUM_TLU_BIT_WIDTH): + # pylint: disable=protected-access + if local._is_adjusting: + message = ( + "AutoTruncators cannot be constructed during adjustment, " + "please construct AutoTruncators outside the function and reference it" + ) + raise RuntimeError(message) + # pylint: enable=protected-access + + self.target_msbs = target_msbs + + self.is_adjusted = False + self.input_min = 0 + self.input_max = 0 + self.input_bit_width = 0 + self.lsbs_to_remove = 0 + + @staticmethod + def adjust(function: Callable, inputset: Union[Iterable[Any], Iterable[Tuple[Any, ...]]]): + """ + Adjust AutoTruncators in a function using an inputset. + """ + + # pylint: disable=protected-access,too-many-branches + + try: # extract underlying function for decorators + function = function.function # type: ignore + assert callable(function) + except AttributeError: + pass + + if local._is_adjusting: + message = "AutoTruncators cannot be adjusted recursively" + raise RuntimeError(message) + + try: + local._is_adjusting = True + + # adjust the truncator using the inputset + # this loop continues until the return is reached in the loop body + # which only happens when ALL truncators are adjusted + # this condition is met if the function can be executed fully + # without `Adjusting` exception is raised + + while True: + truncator = None + + for sample in inputset: + if not isinstance(sample, tuple): + sample = (sample,) + + try: + function(*sample) + except Adjusting as adjuster: + truncator = adjuster.truncator + + truncator.input_min = min(truncator.input_min, adjuster.input_min) + truncator.input_max = max(truncator.input_max, adjuster.input_max) + + input_value = ValueDescription.of( + [truncator.input_min, truncator.input_max] + ) + assert isinstance(input_value.dtype, Integer) + truncator.input_bit_width = input_value.dtype.bit_width + + if ( + truncator.input_bit_width - truncator.lsbs_to_remove + > truncator.target_msbs + ): + truncator.lsbs_to_remove = ( + truncator.input_bit_width - truncator.target_msbs + ) + else: + # this branch will be executed if there were no exceptions in the try block + return + + if truncator is None: + message = "AutoTruncators cannot be adjusted with an empty inputset" + raise ValueError(message) + + truncator.is_adjusted = True + + finally: + local._is_adjusting = False + + # pylint: enable=protected-access,too-many-branches + + def dump_dict(self) -> Dict: + """ + Dump properties of the truncator to a dict. + """ + + return { + "target_msbs": self.target_msbs, + "is_adjusted": self.is_adjusted, + "input_min": self.input_min, + "input_max": self.input_max, + "input_bit_width": self.input_bit_width, + "lsbs_to_remove": self.lsbs_to_remove, + } + + @classmethod + def load_dict(cls, properties: Dict) -> "AutoTruncator": + """ + Load previously dumped truncator. + """ + + result = AutoTruncator(target_msbs=properties["target_msbs"]) + + result.is_adjusted = properties["is_adjusted"] + result.input_min = properties["input_min"] + result.input_max = properties["input_max"] + result.lsbs_to_remove = properties["lsbs_to_remove"] + result.input_bit_width = properties["input_bit_width"] + + return result + + +def truncate_bit_pattern( + x: Union[int, np.integer, List, np.ndarray, Tracer], + lsbs_to_remove: Union[int, AutoTruncator], +) -> Union[int, np.integer, List, np.ndarray, Tracer]: + """ + Round the bit pattern of an integer. + + If `lsbs_to_remove` is an `AutoTruncator`: + corresponding integer value will be determined by adjustment process. + + x = 0b_0000 , lsbs_to_remove = 2 => 0b_0000 + x = 0b_0001 , lsbs_to_remove = 2 => 0b_0000 + x = 0b_0010 , lsbs_to_remove = 2 => 0b_0000 + x = 0b_0100 , lsbs_to_remove = 2 => 0b_0100 + x = 0b_0110 , lsbs_to_remove = 2 => 0b_0100 + x = 0b_1100 , lsbs_to_remove = 2 => 0b_1100 + x = 0b_abcd , lsbs_to_remove = 2 => 0b_ab00 + + Args: + x (Union[int, np.integer, np.ndarray, Tracer]): + input to truncate + + lsbs_to_remove (Union[int, AutoTruncator]): + number of the least significant bits to clear + or an auto truncator object which will be used to determine the integer value + + Returns: + Union[int, np.integer, np.ndarray, Tracer]: + Tracer that represents the operation during tracing + truncated value(s) otherwise + """ + + # pylint: disable=protected-access,too-many-branches + + if isinstance(lsbs_to_remove, AutoTruncator): + if local._is_adjusting: + if not lsbs_to_remove.is_adjusted: + raise Adjusting(lsbs_to_remove, int(np.min(x)), int(np.max(x))) # type: ignore + + elif not lsbs_to_remove.is_adjusted: + message = ( + "AutoTruncators cannot be used before adjustment, " + "please call AutoTruncator.adjust with the function that will be compiled " + "and provide the exact inputset that will be used for compilation" + ) + raise RuntimeError(message) + + lsbs_to_remove = lsbs_to_remove.lsbs_to_remove + + assert isinstance(lsbs_to_remove, int) + + def evaluator( + x: Union[int, np.integer, np.ndarray], + lsbs_to_remove: int, + ) -> Union[int, np.integer, np.ndarray]: + return (x >> lsbs_to_remove) << lsbs_to_remove + + if isinstance(x, Tracer): + computation = Node.generic( + "truncate_bit_pattern", + [deepcopy(x.output)], + deepcopy(x.output), + evaluator, + kwargs={"lsbs_to_remove": lsbs_to_remove}, + ) + return Tracer(computation, [x]) + + if isinstance(x, list): # pragma: no cover + try: + x = np.array(x) + except Exception: # pylint: disable=broad-except + pass + + if isinstance(x, np.ndarray): + if not np.issubdtype(x.dtype, np.integer): + message = ( + f"Expected input elements to be integers but they are {type(x.dtype).__name__}" + ) + raise TypeError(message) + elif not isinstance(x, (int, np.integer)): + message = f"Expected input to be an int or a numpy array but it's {type(x).__name__}" + raise TypeError(message) + + return evaluator(x, lsbs_to_remove) + + # pylint: enable=protected-access,too-many-branches diff --git a/frontends/concrete-python/concrete/fhe/mlir/context.py b/frontends/concrete-python/concrete/fhe/mlir/context.py index ebd8cce13..57d1aa259 100644 --- a/frontends/concrete-python/concrete/fhe/mlir/context.py +++ b/frontends/concrete-python/concrete/fhe/mlir/context.py @@ -3218,6 +3218,45 @@ class Context: return xs[0] + def truncate_bit_pattern(self, x: Conversion, lsbs_to_remove: int) -> Conversion: + if x.is_clear: + highlights = { + x.origin: "operand is clear", + self.converting: "but clear truncate bit pattern is not supported", + } + self.error(highlights) + + assert x.bit_width > lsbs_to_remove + + resulting_bit_width = x.bit_width + for i in range(lsbs_to_remove): + lsb = self.lsb(x.type, x) + cleared = self.sub(x.type, x, lsb) + + new_bit_width = (x.bit_width - 1) if i != (lsbs_to_remove - 1) else resulting_bit_width + x = self.reinterpret(cleared, bit_width=new_bit_width) + + return x + + def lsb(self, resulting_type: ConversionType, x: Conversion) -> Conversion: + assert resulting_type.shape == x.shape + assert resulting_type.is_signed == x.is_signed + assert resulting_type.is_encrypted and x.is_encrypted + + operation = fhe.LsbEintOp if x.is_scalar else fhelinalg.LsbEintOp + return self.operation(operation, resulting_type, x.result) + + def reinterpret(self, x: Conversion, *, bit_width: int) -> Conversion: + assert x.is_encrypted + + resulting_element_type = (self.eint if x.is_unsigned else self.esint)(bit_width) + resulting_type = self.tensor(resulting_element_type, shape=x.shape) + + operation = ( + fhe.ReinterpretPrecisionEintOp if x.is_scalar else fhelinalg.ReinterpretPrecisionEintOp + ) + return self.operation(operation, resulting_type, x.result) + def zeros(self, resulting_type: ConversionType) -> Conversion: assert resulting_type.is_encrypted diff --git a/frontends/concrete-python/concrete/fhe/mlir/converter.py b/frontends/concrete-python/concrete/fhe/mlir/converter.py index be43f306c..ecef7cbe9 100644 --- a/frontends/concrete-python/concrete/fhe/mlir/converter.py +++ b/frontends/concrete-python/concrete/fhe/mlir/converter.py @@ -618,6 +618,20 @@ class Converter: variable_input_index = variable_input_indices[0] variable_input = preds[variable_input_index] + if variable_input.origin.properties.get("name") == "truncate_bit_pattern": + original_bit_width = variable_input.origin.properties["original_bit_width"] + lsbs_to_remove = variable_input.origin.properties["kwargs"]["lsbs_to_remove"] + truncated_bit_width = original_bit_width - lsbs_to_remove + + if variable_input.bit_width > original_bit_width: + bit_width_difference = variable_input.bit_width - original_bit_width + shifter = ctx.constant( + ctx.i(variable_input.bit_width + 1), 2**bit_width_difference + ) + variable_input = ctx.mul(variable_input.type, variable_input, shifter) + + variable_input = ctx.reinterpret(variable_input, bit_width=truncated_bit_width) + if len(tables) == 1: return ctx.tlu(ctx.typeof(node), on=variable_input, table=lut_values.tolist()) @@ -637,6 +651,10 @@ class Converter: axes=node.properties["kwargs"].get("axes", []), ) + def truncate_bit_pattern(self, ctx: Context, node: Node, preds: List[Conversion]) -> Conversion: + assert len(preds) == 1 + return ctx.truncate_bit_pattern(preds[0], node.properties["kwargs"]["lsbs_to_remove"]) + def zeros(self, ctx: Context, node: Node, preds: List[Conversion]) -> Conversion: assert len(preds) == 0 return ctx.zeros(ctx.typeof(node)) diff --git a/frontends/concrete-python/concrete/fhe/mlir/processors/assign_bit_widths.py b/frontends/concrete-python/concrete/fhe/mlir/processors/assign_bit_widths.py index 0f8c56267..675c99429 100644 --- a/frontends/concrete-python/concrete/fhe/mlir/processors/assign_bit_widths.py +++ b/frontends/concrete-python/concrete/fhe/mlir/processors/assign_bit_widths.py @@ -562,3 +562,7 @@ class AdditionalConstraints: transpose = { inputs_and_output_share_precision, } + + truncate_bit_pattern = { + inputs_and_output_share_precision, + } diff --git a/frontends/concrete-python/concrete/fhe/mlir/utils.py b/frontends/concrete-python/concrete/fhe/mlir/utils.py index fc2d2508d..8122a265a 100644 --- a/frontends/concrete-python/concrete/fhe/mlir/utils.py +++ b/frontends/concrete-python/concrete/fhe/mlir/utils.py @@ -162,7 +162,9 @@ def construct_table(node: Node, preds: List[Node]) -> List[Any]: if pred.operation != Operation.Constant: variable_input_index = index break + assert_that(variable_input_index != -1) + variable_input = preds[variable_input_index] variable_input_dtype = node.inputs[variable_input_index].dtype variable_input_shape = node.inputs[variable_input_index].shape @@ -170,7 +172,6 @@ def construct_table(node: Node, preds: List[Node]) -> List[Any]: assert_that(isinstance(variable_input_dtype, Integer)) variable_input_dtype = deepcopy(cast(Integer, variable_input_dtype)) - variable_input = preds[variable_input_index] if ( variable_input.operation == Operation.Generic and variable_input.properties["name"] == "round_bit_pattern" @@ -186,6 +187,20 @@ def construct_table(node: Node, preds: List[Node]) -> List[Any]: variable_input_dtype.bit_width += 1 step = (2**variable_input_dtype.bit_width) // expected_number_of_elements + + elif ( + variable_input.operation == Operation.Generic + and variable_input.properties["name"] == "truncate_bit_pattern" + ): + original_bit_width = variable_input.properties["original_bit_width"] + lsbs_to_remove = variable_input.properties["kwargs"]["lsbs_to_remove"] + + resulting_bit_width = original_bit_width - lsbs_to_remove + expected_number_of_elements = 2**resulting_bit_width + + variable_input_dtype.bit_width = original_bit_width + step = (2**original_bit_width) // expected_number_of_elements + else: step = 1 diff --git a/frontends/concrete-python/requirements.txt b/frontends/concrete-python/requirements.txt index d0e06d576..364f7d192 100644 --- a/frontends/concrete-python/requirements.txt +++ b/frontends/concrete-python/requirements.txt @@ -1,3 +1,4 @@ +importlib-resources>=6.1 networkx>=2.6 numpy>=1.23 scipy>=1.10 diff --git a/frontends/concrete-python/tests/execution/test_truncate_bit_pattern.py b/frontends/concrete-python/tests/execution/test_truncate_bit_pattern.py new file mode 100644 index 000000000..74e226b7a --- /dev/null +++ b/frontends/concrete-python/tests/execution/test_truncate_bit_pattern.py @@ -0,0 +1,467 @@ +""" +Tests of execution of truncate bit pattern operation. +""" + +import numpy as np +import pytest + +from concrete import fhe +from concrete.fhe.representation.utils import format_constant + + +@pytest.mark.parametrize( + "sample,lsbs_to_remove,expected_output", + [ + (0b_0000_0011, 0, 0b_0000_0011), + (0b_0000_0100, 0, 0b_0000_0100), + (0b_0000_0000, 3, 0b_0000_0000), + (0b_0000_0001, 3, 0b_0000_0000), + (0b_0000_0010, 3, 0b_0000_0000), + (0b_0000_0011, 3, 0b_0000_0000), + (0b_0000_0100, 3, 0b_0000_0000), + (0b_0000_0101, 3, 0b_0000_0000), + (0b_0000_0110, 3, 0b_0000_0000), + (0b_0000_0111, 3, 0b_0000_0000), + (0b_0000_1000, 3, 0b_0000_1000), + (0b_0000_1001, 3, 0b_0000_1000), + (0b_0000_1010, 3, 0b_0000_1000), + (0b_0000_1011, 3, 0b_0000_1000), + (0b_0000_1100, 3, 0b_0000_1000), + (0b_0000_1101, 3, 0b_0000_1000), + (0b_0000_1110, 3, 0b_0000_1000), + (0b_0000_1111, 3, 0b_0000_1000), + ], +) +def test_plain_truncate_bit_pattern(sample, lsbs_to_remove, expected_output): + """ + Test truncate bit pattern in evaluation context. + """ + assert fhe.truncate_bit_pattern(sample, lsbs_to_remove=lsbs_to_remove) == expected_output + + +@pytest.mark.parametrize( + "sample,lsbs_to_remove,expected_error,expected_message", + [ + ( + np.array([3.2, 4.1]), + 3, + TypeError, + f"Expected input elements to be integers but they are {type(np.array([3.2, 4.1]).dtype).__name__}", # noqa: E501 + ), + ( + "foo", + 3, + TypeError, + "Expected input to be an int or a numpy array but it's str", + ), + ], +) +def test_bad_plain_truncate_bit_pattern( + sample, + lsbs_to_remove, + expected_error, + expected_message, +): + """ + Test truncate bit pattern in evaluation context with bad parameters. + """ + + with pytest.raises(expected_error) as excinfo: + fhe.truncate_bit_pattern(sample, lsbs_to_remove=lsbs_to_remove) + + assert str(excinfo.value) == expected_message + + +@pytest.mark.parametrize( + "input_bits,lsbs_to_remove", + [ + (3, 1), + (3, 2), + (4, 1), + (4, 2), + (4, 3), + (5, 1), + (5, 2), + (5, 3), + (5, 4), + ], +) +@pytest.mark.parametrize( + "mapper", + [ + pytest.param( + lambda x: x, + id="x", + ), + pytest.param( + lambda x: x + 10, + id="x + 10", + ), + pytest.param( + lambda x: x**2, + id="x ** 2", + ), + pytest.param( + lambda x: fhe.univariate(lambda x: x if x >= 0 else 0)(x), + id="relu", + ), + ], +) +def test_truncate_bit_pattern(input_bits, lsbs_to_remove, mapper, helpers): + """ + Test truncate bit pattern. + """ + + @fhe.compiler({"x": "encrypted"}) + def function(x): + x_truncated = fhe.truncate_bit_pattern(x, lsbs_to_remove=lsbs_to_remove) + return mapper(x_truncated) + + upper_bound = 2**input_bits + inputset = [0, upper_bound - 1] + + circuit = function.compile(inputset, helpers.configuration()) + helpers.check_execution(circuit, function, np.random.randint(0, upper_bound), retries=3) + + for value in inputset: + helpers.check_execution(circuit, function, value, retries=3) + + +@pytest.mark.parametrize( + "input_bits,lsbs_to_remove", + [ + (3, 1), + (3, 2), + (4, 1), + (4, 2), + (4, 3), + ], +) +def test_truncate_bit_pattern_unsigned_range(input_bits, lsbs_to_remove, helpers): + """ + Test truncate bit pattern in unsigned range. + """ + + @fhe.compiler({"x": "encrypted"}) + def function(x): + return fhe.truncate_bit_pattern(x, lsbs_to_remove=lsbs_to_remove) + + inputset = range(0, 2**input_bits) + circuit = function.compile(inputset, helpers.configuration()) + + for value in inputset: + helpers.check_execution(circuit, function, value, retries=3) + + +@pytest.mark.parametrize( + "input_bits,lsbs_to_remove", + [ + (3, 1), + (3, 2), + (4, 1), + (4, 2), + (4, 3), + ], +) +def test_truncate_bit_pattern_signed_range(input_bits, lsbs_to_remove, helpers): + """ + Test truncate bit pattern in signed range. + """ + + @fhe.compiler({"x": "encrypted"}) + def function(x): + return fhe.truncate_bit_pattern(x, lsbs_to_remove=lsbs_to_remove) + + inputset = range(-(2 ** (input_bits - 1)), 2 ** (input_bits - 1)) + circuit = function.compile(inputset, helpers.configuration()) + + for value in inputset: + helpers.check_execution(circuit, function, value, retries=3) + + +@pytest.mark.parametrize( + "input_bits,lsbs_to_remove", + [ + (3, 1), + (3, 2), + (4, 1), + (4, 2), + (4, 3), + ], +) +def test_truncate_bit_pattern_unsigned_range_assigned(input_bits, lsbs_to_remove, helpers): + """ + Test truncate bit pattern in unsigned range with a big bit-width assigned. + """ + + @fhe.compiler({"x": "encrypted"}) + def function(x): + truncated = fhe.truncate_bit_pattern(x, lsbs_to_remove=lsbs_to_remove) + return (truncated**2) + (63 - x) + + inputset = range(0, 2**input_bits) + circuit = function.compile(inputset, helpers.configuration()) + + for value in inputset: + helpers.check_execution(circuit, function, value, retries=3) + + +@pytest.mark.parametrize( + "input_bits,lsbs_to_remove", + [ + (3, 1), + (3, 2), + (4, 1), + (4, 2), + (4, 3), + ], +) +def test_truncate_bit_pattern_signed_range_assigned(input_bits, lsbs_to_remove, helpers): + """ + Test truncate bit pattern in signed range with a big bit-width assigned. + """ + + @fhe.compiler({"x": "encrypted"}) + def function(x): + truncated = fhe.truncate_bit_pattern(x, lsbs_to_remove=lsbs_to_remove) + return (truncated**2) + (63 - x) + + inputset = range(-(2 ** (input_bits - 1)), 2 ** (input_bits - 1)) + circuit = function.compile(inputset, helpers.configuration()) + + for value in inputset: + helpers.check_execution(circuit, function, value, retries=3) + + +def test_truncate_bit_pattern_identity(helpers, pytestconfig): + """ + Test truncate bit pattern used multiple times outside TLUs. + """ + + @fhe.compiler({"x": "encrypted"}) + def function(x): + truncated = fhe.truncate_bit_pattern(x, lsbs_to_remove=2) + return truncated + truncated + + inputset = range(-20, 20) + circuit = function.compile(inputset, helpers.configuration()) + + expected_mlir = ( + """ + +module { + func.func @main(%arg0: !FHE.esint<7>) -> !FHE.esint<7> { + %0 = "FHE.lsb"(%arg0) : (!FHE.esint<7>) -> !FHE.esint<7> + %1 = "FHE.sub_eint"(%arg0, %0) : (!FHE.esint<7>, !FHE.esint<7>) -> !FHE.esint<7> + %2 = "FHE.reinterpret_precision"(%1) : (!FHE.esint<7>) -> !FHE.esint<6> + %3 = "FHE.lsb"(%2) : (!FHE.esint<6>) -> !FHE.esint<6> + %4 = "FHE.sub_eint"(%2, %3) : (!FHE.esint<6>, !FHE.esint<6>) -> !FHE.esint<6> + %5 = "FHE.reinterpret_precision"(%4) : (!FHE.esint<6>) -> !FHE.esint<7> + %6 = "FHE.add_eint"(%5, %5) : (!FHE.esint<7>, !FHE.esint<7>) -> !FHE.esint<7> + return %6 : !FHE.esint<7> + } +} + + """ # noqa: E501 + if pytestconfig.getoption("precision") == "multi" + else """ + +module { + func.func @main(%arg0: !FHE.esint<7>) -> !FHE.esint<7> { + %0 = "FHE.lsb"(%arg0) : (!FHE.esint<7>) -> !FHE.esint<7> + %1 = "FHE.sub_eint"(%arg0, %0) : (!FHE.esint<7>, !FHE.esint<7>) -> !FHE.esint<7> + %2 = "FHE.reinterpret_precision"(%1) : (!FHE.esint<7>) -> !FHE.esint<6> + %3 = "FHE.lsb"(%2) : (!FHE.esint<6>) -> !FHE.esint<6> + %4 = "FHE.sub_eint"(%2, %3) : (!FHE.esint<6>, !FHE.esint<6>) -> !FHE.esint<6> + %5 = "FHE.reinterpret_precision"(%4) : (!FHE.esint<6>) -> !FHE.esint<7> + %6 = "FHE.add_eint"(%5, %5) : (!FHE.esint<7>, !FHE.esint<7>) -> !FHE.esint<7> + return %6 : !FHE.esint<7> + } +} + + """ # noqa: E501 + ) + + helpers.check_str(expected_mlir, circuit.mlir) + + +def test_auto_truncating(helpers): + """ + Test truncate bit pattern with auto truncating. + """ + + # with auto adjust truncators configuration + # --------------------------------------- + + # y has the max value of 1999, so it's 11 bits + # our target msb is 5 bits, which means we need to remove 6 of the least significant bits + + truncator1 = fhe.AutoTruncator(target_msbs=5) + + @fhe.compiler({"x": "encrypted"}) + def function1(x): + y = x + 1000 + z = fhe.truncate_bit_pattern(y, lsbs_to_remove=truncator1) + return np.sqrt(z).astype(np.int64) + + inputset1 = range(1000) + function1.trace(inputset1, helpers.configuration(), auto_adjust_truncators=True) + + assert truncator1.lsbs_to_remove == 6 + + # manual + # ------ + + # y has the max value of 1999, so it's 11 bits + # our target msb is 3 bits, which means we need to remove 8 of the least significant bits + + truncator2 = fhe.AutoTruncator(target_msbs=3) + + @fhe.compiler({"x": "encrypted"}) + def function2(x): + y = x + 1000 + z = fhe.truncate_bit_pattern(y, lsbs_to_remove=truncator2) + return np.sqrt(z).astype(np.int64) + + inputset2 = range(1000) + fhe.AutoTruncator.adjust(function2, inputset2) + + assert truncator2.lsbs_to_remove == 8 + + # complicated case + # ---------------- + + # have 2 ** 8 entries during evaluation, it won't matter after compilation + entries3 = list(range(2**8)) + # we have 8-bit inputs for this table, and we only want to use first 5-bits + for i in range(0, 2**8, 2**3): + # so we set every 8th entry to a 4-bit value + entries3[i] = np.random.randint(0, (2**4) - (2**2)) + # when this tlu is applied to an 8-bit value with 5-bit msb truncating, result will be 4-bits + table3 = fhe.LookupTable(entries3) + # and this is the truncator for table1, which should have lsbs_to_remove of 3 + truncator3 = fhe.AutoTruncator(target_msbs=5) + + # have 2 ** 8 entries during evaluation, it won't matter after compilation + entries4 = list(range(2**8)) + # we have 4-bit inputs for this table, and we only want to use first 2-bits + for i in range(0, 2**4, 2**2): + # so we set every 4th entry to an 8-bit value + entries4[i] = np.random.randint(2**7, 2**8) + # when this tlu is applied to a 4-bit value with 2-bit msb truncating, result will be 8-bits + table4 = fhe.LookupTable(entries4) + # and this is the truncator for table2, which should have lsbs_to_remove of 2 + truncator4 = fhe.AutoTruncator(target_msbs=2) + + @fhe.compiler({"x": "encrypted"}) + def function3(x): + a = fhe.truncate_bit_pattern(x, lsbs_to_remove=truncator3) + b = table3[a] + c = fhe.truncate_bit_pattern(b, lsbs_to_remove=truncator4) + d = table4[c] + return d + + inputset3 = range((2**8) - (2**3)) + circuit3 = function3.compile( + inputset3, + helpers.configuration(), + auto_adjust_truncators=True, + ) + + assert truncator3.lsbs_to_remove == 3 + assert truncator4.lsbs_to_remove == 2 + + table3_formatted_string = format_constant(table3.table, 25) + table4_formatted_string = format_constant(table4.table, 25) + + helpers.check_str( + f""" + +%0 = x # EncryptedScalar +%1 = truncate_bit_pattern(%0, lsbs_to_remove=3) # EncryptedScalar +%2 = tlu(%1, table={table3_formatted_string}) # EncryptedScalar +%3 = truncate_bit_pattern(%2, lsbs_to_remove=2) # EncryptedScalar +%4 = tlu(%3, table={table4_formatted_string}) # EncryptedScalar +return %4 + + """, + str(circuit3.graph.format(show_bounds=False)), + ) + + +def test_auto_truncating_without_adjustment(): + """ + Test truncate bit pattern with auto truncating but without adjustment. + """ + + truncator = fhe.AutoTruncator(target_msbs=5) + + def function(x): + y = x + 1000 + z = fhe.truncate_bit_pattern(y, lsbs_to_remove=truncator) + return np.sqrt(z).astype(np.int64) + + with pytest.raises(RuntimeError) as excinfo: + function(100) + + assert str(excinfo.value) == ( + "AutoTruncators cannot be used before adjustment, " + "please call AutoTruncator.adjust with the function that will be compiled " + "and provide the exact inputset that will be used for compilation" + ) + + +def test_auto_truncating_with_empty_inputset(): + """ + Test truncate bit pattern with auto truncating but with empty inputset. + """ + + truncator = fhe.AutoTruncator(target_msbs=5) + + def function(x): + y = x + 1000 + z = fhe.truncate_bit_pattern(y, lsbs_to_remove=truncator) + return np.sqrt(z).astype(np.int64) + + with pytest.raises(ValueError) as excinfo: + fhe.AutoTruncator.adjust(function, []) + + assert str(excinfo.value) == "AutoTruncators cannot be adjusted with an empty inputset" + + +def test_auto_truncating_recursive_adjustment(): + """ + Test truncate bit pattern with auto truncating but with recursive adjustment. + """ + + truncator = fhe.AutoTruncator(target_msbs=5) + + def function(x): + fhe.AutoTruncator.adjust(function, range(10)) + y = x + 1000 + z = fhe.truncate_bit_pattern(y, lsbs_to_remove=truncator) + return np.sqrt(z).astype(np.int64) + + with pytest.raises(RuntimeError) as excinfo: + fhe.AutoTruncator.adjust(function, range(10)) + + assert str(excinfo.value) == "AutoTruncators cannot be adjusted recursively" + + +def test_auto_truncating_construct_in_function(): + """ + Test truncate bit pattern with auto truncating but truncator is constructed within the function. + """ + + def function(x): + y = x + 1000 + z = fhe.truncate_bit_pattern(y, lsbs_to_remove=fhe.AutoTruncator(target_msbs=5)) + return np.sqrt(z).astype(np.int64) + + with pytest.raises(RuntimeError) as excinfo: + fhe.AutoTruncator.adjust(function, range(10)) + + assert str(excinfo.value) == ( + "AutoTruncators cannot be constructed during adjustment, " + "please construct AutoTruncators outside the function and reference it" + ) diff --git a/frontends/concrete-python/tests/extensions/test_truncate_bit_pattern.py b/frontends/concrete-python/tests/extensions/test_truncate_bit_pattern.py new file mode 100644 index 000000000..526c7d03f --- /dev/null +++ b/frontends/concrete-python/tests/extensions/test_truncate_bit_pattern.py @@ -0,0 +1,37 @@ +""" +Tests of 'truncate_bit_pattern' extension. +""" + +from concrete import fhe + + +def test_dump_load_auto_truncator(): + """ + Test 'dump_dict' and 'load_dict' methods of AutoTruncator. + """ + + truncator = fhe.AutoTruncator(target_msbs=3) + truncator.is_adjusted = True + truncator.input_min = 10 + truncator.input_max = 20 + truncator.input_bit_width = 5 + truncator.lsbs_to_remove = 2 + + dumped = truncator.dump_dict() + assert dumped == { + "target_msbs": 3, + "is_adjusted": True, + "input_min": 10, + "input_max": 20, + "input_bit_width": 5, + "lsbs_to_remove": 2, + } + + loaded = fhe.AutoTruncator.load_dict(dumped) + + assert loaded.target_msbs == 3 + assert loaded.is_adjusted + assert loaded.input_min == 10 + assert loaded.input_max == 20 + assert loaded.input_bit_width == 5 + assert loaded.lsbs_to_remove == 2 diff --git a/frontends/concrete-python/tests/mlir/test_converter.py b/frontends/concrete-python/tests/mlir/test_converter.py index 67125d0d1..f647fc293 100644 --- a/frontends/concrete-python/tests/mlir/test_converter.py +++ b/frontends/concrete-python/tests/mlir/test_converter.py @@ -988,6 +988,23 @@ Function you are trying to compile cannot be compiled ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ but only up to 16-bit maximum operation is supported return %2 + """, # noqa: E501 + ), + pytest.param( + lambda x: fhe.truncate_bit_pattern(x, lsbs_to_remove=2), + {"x": "clear"}, + [10, 20, 30], + RuntimeError, + """ + +Function you are trying to compile cannot be compiled + +%0 = x # ClearScalar ∈ [10, 30] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ operand is clear +%1 = truncate_bit_pattern(%0, lsbs_to_remove=2) # ClearScalar ∈ [8, 28] +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ but clear truncate bit pattern is not supported +return %1 + """, # noqa: E501 ), ],