From 95c672ab021d53fb69979de4432ff1f7445cb4de Mon Sep 17 00:00:00 2001 From: Jose Urruticoechea <77821769+jurruti@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:06:41 +0000 Subject: [PATCH 1/3] Support the deprecated_parameter decorator for asynchronous functions. (#6247) * Support the deprecated_parameter decorator for asynchronous functions. * Add assert iscoroutine to async_deprecated_parameter test --------- Co-authored-by: Jose Urruticoechea --- cirq-core/cirq/_compat.py | 34 +++++++++++++++++++++---------- cirq-core/cirq/_compat_test.py | 37 +++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/cirq-core/cirq/_compat.py b/cirq-core/cirq/_compat.py index 0faf0b2662b..566ec1c2a34 100644 --- a/cirq-core/cirq/_compat.py +++ b/cirq-core/cirq/_compat.py @@ -400,23 +400,36 @@ def deprecated_parameter( _validate_deadline(deadline) def decorator(func: Callable) -> Callable: + def deprecation_warning(): + qualname = func.__qualname__ if func_name is None else func_name + _warn_or_error( + f'The {parameter_desc} parameter of {qualname} was ' + f'used but is deprecated.\n' + f'It will be removed in cirq {deadline}.\n' + f'{fix}\n' + ) + @functools.wraps(func) def decorated_func(*args, **kwargs) -> Any: if match(args, kwargs): if rewrite is not None: args, kwargs = rewrite(args, kwargs) + deprecation_warning() + return func(*args, **kwargs) - qualname = func.__qualname__ if func_name is None else func_name - _warn_or_error( - f'The {parameter_desc} parameter of {qualname} was ' - f'used but is deprecated.\n' - f'It will be removed in cirq {deadline}.\n' - f'{fix}\n' - ) + @functools.wraps(func) + async def async_decorated_func(*args, **kwargs) -> Any: + if match(args, kwargs): + if rewrite is not None: + args, kwargs = rewrite(args, kwargs) + deprecation_warning() - return func(*args, **kwargs) + return await func(*args, **kwargs) - return decorated_func + if inspect.iscoroutinefunction(func): + return async_decorated_func + else: + return decorated_func return decorator @@ -436,13 +449,12 @@ def deprecate_attributes(module_name: str, deprecated_attributes: Dict[str, Tupl will cause a warning for these deprecated attributes. """ - for (deadline, _) in deprecated_attributes.values(): + for deadline, _ in deprecated_attributes.values(): _validate_deadline(deadline) module = sys.modules[module_name] class Wrapped(ModuleType): - __dict__ = module.__dict__ # Workaround for: https://github.com/python/mypy/issues/8083 diff --git a/cirq-core/cirq/_compat_test.py b/cirq-core/cirq/_compat_test.py index 122affd6537..c5dba50c975 100644 --- a/cirq-core/cirq/_compat_test.py +++ b/cirq-core/cirq/_compat_test.py @@ -14,6 +14,7 @@ import collections import dataclasses import importlib.metadata +import inspect import logging import multiprocessing import os @@ -26,7 +27,7 @@ from importlib.machinery import ModuleSpec from unittest import mock - +import duet import numpy as np import pandas as pd import pytest @@ -263,6 +264,40 @@ def f_with_badly_deprecated_param(new_count): # pragma: no cover # pylint: enable=unused-variable +@duet.sync +async def test_deprecated_parameter_async_function(): + @deprecated_parameter( + deadline='v1.2', + fix='Double it yourself.', + func_name='test_func', + parameter_desc='double_count', + match=lambda args, kwargs: 'double_count' in kwargs, + rewrite=lambda args, kwargs: (args, {'new_count': kwargs['double_count'] * 2}), + ) + async def f(new_count): + return new_count + + assert inspect.iscoroutinefunction(f) + + # Does not warn on usual use. + with cirq.testing.assert_logs(count=0): + assert await f(1) == 1 + assert await f(new_count=1) == 1 + + with cirq.testing.assert_deprecated( + '_compat_test.py:', + 'double_count parameter of test_func was used', + 'will be removed in cirq v1.2', + 'Double it yourself.', + deadline='v1.2', + ): + # pylint: disable=unexpected-keyword-arg + # pylint: disable=no-value-for-parameter + assert await f(double_count=1) == 2 + # pylint: enable=no-value-for-parameter + # pylint: enable=unexpected-keyword-arg + + def test_wrap_module(): my_module = types.ModuleType('my_module', 'my doc string') my_module.foo = 'foo' From 30fa6f596107ad11c91622cf89c63143cfa9727a Mon Sep 17 00:00:00 2001 From: Tanuj Khattar Date: Thu, 17 Aug 2023 13:40:08 -0700 Subject: [PATCH 2/3] Fix bug in cirq-ft due to which T-complexity fails for gates with an empty decomposition / no-op (#6252) --- cirq-ft/cirq_ft/algos/select_swap_qrom.py | 2 +- cirq-ft/cirq_ft/algos/select_swap_qrom_test.py | 6 ++++++ cirq-ft/cirq_ft/infra/decompose_protocol.py | 2 +- cirq-ft/cirq_ft/infra/decompose_protocol_test.py | 15 ++++++++++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cirq-ft/cirq_ft/algos/select_swap_qrom.py b/cirq-ft/cirq_ft/algos/select_swap_qrom.py index 5773a617c4b..4cde2b1f172 100644 --- a/cirq-ft/cirq_ft/algos/select_swap_qrom.py +++ b/cirq-ft/cirq_ft/algos/select_swap_qrom.py @@ -124,7 +124,7 @@ def __init__( assert len(target_bitsizes) == len(data) assert all(t >= max(d).bit_length() for t, d in zip(target_bitsizes, data)) self._num_sequences = len(data) - self._target_bitsizes = target_bitsizes + self._target_bitsizes = tuple(target_bitsizes) self._iteration_length = len(data[0]) if block_size is None: # Figure out optimal value of block_size diff --git a/cirq-ft/cirq_ft/algos/select_swap_qrom_test.py b/cirq-ft/cirq_ft/algos/select_swap_qrom_test.py index b3ab1c3d86a..f040f312bbf 100644 --- a/cirq-ft/cirq_ft/algos/select_swap_qrom_test.py +++ b/cirq-ft/cirq_ft/algos/select_swap_qrom_test.py @@ -101,3 +101,9 @@ def test_qroam_diagram(): def test_qroam_raises(): with pytest.raises(ValueError, match="must be of equal length"): _ = cirq_ft.SelectSwapQROM([1, 2], [1, 2, 3]) + + +def test_qroam_hashable(): + qrom = cirq_ft.SelectSwapQROM([1, 2, 5, 6, 7, 8]) + assert hash(qrom) is not None + assert cirq_ft.t_complexity(qrom) == cirq_ft.TComplexity(32, 160, 0) diff --git a/cirq-ft/cirq_ft/infra/decompose_protocol.py b/cirq-ft/cirq_ft/infra/decompose_protocol.py index 73a2f4db3b3..0a88de2de18 100644 --- a/cirq-ft/cirq_ft/infra/decompose_protocol.py +++ b/cirq-ft/cirq_ft/infra/decompose_protocol.py @@ -98,4 +98,4 @@ def _decompose_once_considering_known_decomposition(val: Any) -> DecomposeResult else: decomposed = cirq.decompose_once(val, context=context, flatten=False, default=None) - return [*cirq.flatten_to_ops(decomposed)] if decomposed else None + return [*cirq.flatten_to_ops(decomposed)] if decomposed is not None else None diff --git a/cirq-ft/cirq_ft/infra/decompose_protocol_test.py b/cirq-ft/cirq_ft/infra/decompose_protocol_test.py index 3fb53af9ff3..ac753faf107 100644 --- a/cirq-ft/cirq_ft/infra/decompose_protocol_test.py +++ b/cirq-ft/cirq_ft/infra/decompose_protocol_test.py @@ -15,7 +15,11 @@ import cirq import numpy as np import pytest -from cirq_ft.infra.decompose_protocol import _fredkin, _try_decompose_from_known_decompositions +from cirq_ft.infra.decompose_protocol import ( + _fredkin, + _try_decompose_from_known_decompositions, + _decompose_once_considering_known_decomposition, +) def test_fredkin_unitary(): @@ -43,3 +47,12 @@ def test_decompose_fredkin(gate): for o in cirq.flatten_op_tree(_fredkin((c, t1, t2), context)) ) assert want == _try_decompose_from_known_decompositions(op, context) + + +def test_known_decomposition_empty_unitary(): + class DecomposeEmptyList(cirq.testing.SingleQubitGate): + def _decompose_(self, _): + return [] + + gate = DecomposeEmptyList() + assert _decompose_once_considering_known_decomposition(gate) == [] From 95cb43f55d127df02532af493c6c46024514b104 Mon Sep 17 00:00:00 2001 From: Yisu Peng Date: Fri, 18 Aug 2023 03:23:41 -0400 Subject: [PATCH 3/3] Remove temporary directories after test done (#6236) * remove temporary isolated venv after isolated notebook/packaging test done * remove temporary venv after cloned env test done * remove temporary directories for figures after heatmap testings Fixes #6036 --- cirq-core/cirq/vis/heatmap_test.py | 2 ++ dev_tools/cloned_env_test.py | 2 ++ dev_tools/notebooks/isolated_notebook_test.py | 2 ++ dev_tools/packaging/isolated_packages_test.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/cirq-core/cirq/vis/heatmap_test.py b/cirq-core/cirq/vis/heatmap_test.py index 252afd0edf4..20a0b4eea39 100644 --- a/cirq-core/cirq/vis/heatmap_test.py +++ b/cirq-core/cirq/vis/heatmap_test.py @@ -14,6 +14,7 @@ """Tests for Heatmap.""" import pathlib +import shutil import string from tempfile import mkdtemp @@ -309,6 +310,7 @@ def test_colorbar(ax, position, size, pad): plt.close(fig1) plt.close(fig2) + shutil.rmtree(tmp_dir) @pytest.mark.usefixtures('closefigures') diff --git a/dev_tools/cloned_env_test.py b/dev_tools/cloned_env_test.py index 883737e7cb0..5284d05ee16 100644 --- a/dev_tools/cloned_env_test.py +++ b/dev_tools/cloned_env_test.py @@ -15,6 +15,7 @@ """Tests the cloned_env fixture in conftest.py""" import json import os +import shutil import subprocess from unittest import mock @@ -39,3 +40,4 @@ def test_isolated_env_cloning(cloned_env, param): packages = json.loads(result.stdout) assert {"name": "flynt", "version": "0.64"} in packages assert {"astor", "flynt", "pip", "setuptools", "wheel"} == set(p['name'] for p in packages) + shutil.rmtree(env) diff --git a/dev_tools/notebooks/isolated_notebook_test.py b/dev_tools/notebooks/isolated_notebook_test.py index 03e03a9e15b..a1f1e7f9912 100644 --- a/dev_tools/notebooks/isolated_notebook_test.py +++ b/dev_tools/notebooks/isolated_notebook_test.py @@ -27,6 +27,7 @@ import os import re import subprocess +import shutil import warnings from typing import Set, List @@ -188,6 +189,7 @@ def _rewrite_and_run_notebook(notebook_path, cloned_env): f"dev_tools/notebooks/isolated_notebook_test.py." ) os.remove(rewritten_notebook_path) + shutil.rmtree(notebook_env) @pytest.mark.slow diff --git a/dev_tools/packaging/isolated_packages_test.py b/dev_tools/packaging/isolated_packages_test.py index c3c92e7a61b..3f03d2408fe 100644 --- a/dev_tools/packaging/isolated_packages_test.py +++ b/dev_tools/packaging/isolated_packages_test.py @@ -13,6 +13,7 @@ # limitations under the License. import os +import shutil import subprocess from unittest import mock @@ -48,3 +49,4 @@ def test_isolated_packages(cloned_env, module): check=False, ) assert result.returncode == 0, f"Failed isolated tests for {module.name}:\n{result.stdout}" + shutil.rmtree(env)