From 03c577b7b953b2ad0e7b3b7818028ff0bf16db5b Mon Sep 17 00:00:00 2001 From: Gao Jianjian Date: Thu, 3 Apr 2025 09:42:20 +0800 Subject: [PATCH 1/3] feat(utils): add ArrayLike type for array-like type checking Introduce a new class `ArrayLike` in the `typing` module to provide a union of all array-like types, including lists, tuples, and numpy arrays. Also includes support for PyTorch tensors if available. Includes instance and subclass checks for array-like objects. --- cqlib/utils/typing.py | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 cqlib/utils/typing.py diff --git a/cqlib/utils/typing.py b/cqlib/utils/typing.py new file mode 100644 index 0000000..e452c57 --- /dev/null +++ b/cqlib/utils/typing.py @@ -0,0 +1,71 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Typing utilities. + +This module contains the following classes: +* :class:`ArrayLike`: Returns a ``Union`` of all array-like types, which + includes any scalar or sequence that can be interpreted as a numpy array, + including lists and tuples. +""" +import contextlib +import sys + +import numpy as np + + +class ArrayLikeMETA(type): + """ + ArrayLike metaclass. + """ + _normal_types = list, tuple, np.ndarray + + def __instancecheck__(cls, other): + """ Check if an object is a `ArrayLike` instance. """ + return isinstance(other, cls._normal_types) or _is_torch(other) + + def __subclasscheck__(cls, other): + """ Checks if a class is a subclass of ``ArrayLike``.""" + return issubclass(other, cls._normal_types) or _is_torch(other, subclass=True) + + +# pylint: disable=too-few-public-methods +class ArrayLike(metaclass=ArrayLikeMETA): + """ + Returns a ``Union`` of all array-like types, which includes any scalar or sequence + that can be interpreted as a numpy array, including lists and tuples. + + **Examples** + + >>> from cqlib.utils.typing import ArrayLike + >>> isinstance([2, 6, 8], ArrayLike) + True + >>> isinstance(torch.tensor([1, 2, 3]), ArrayLike) + True + >>> issubclass(list, ArrayLike) + True + >>> isinstance(5, ArrayLike) + False + """ + + +def _is_torch(other, subclass=False): + """ Check whether it is PyTorch tensor type. """ + if "torch" in sys.modules: + with contextlib.suppress(ImportError): + # pylint: disable=import-outside-toplevel + from torch import Tensor as torchTensor + check = issubclass if subclass else isinstance + return check(other, torchTensor) + return False -- Gitee From 247cefa172fff1828661a0a2b9ed55f54fc2fca2 Mon Sep 17 00:00:00 2001 From: Gao Jianjian Date: Thu, 3 Apr 2025 10:34:25 +0800 Subject: [PATCH 2/3] test(utils): add typing test cases for ArrayLike Added a new test file `test_typing.py` to validate the `ArrayLike` type in the typing module. The test cases cover various data types to ensure correct type checking behavior. --- tests/utils/test_typing.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/utils/test_typing.py diff --git a/tests/utils/test_typing.py b/tests/utils/test_typing.py new file mode 100644 index 0000000..bf05bbc --- /dev/null +++ b/tests/utils/test_typing.py @@ -0,0 +1,30 @@ +# This code is part of cqlib. +# +# Copyright (C) 2025 China Telecom Quantum Group, QuantumCTek Co., Ltd., +# Center for Excellence in Quantum Information and Quantum Physics. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Typing testcase. """ +import numpy as np + +from cqlib.utils.typing import ArrayLike + + +def test_typing(): + """ + Test the typing module. + """ + assert isinstance([2, 6, 8], ArrayLike) + assert isinstance((1, 2), ArrayLike) + assert isinstance(np.array([1, 2, 3]), ArrayLike) + assert issubclass(list, ArrayLike) + assert not isinstance(5, ArrayLike) + assert not isinstance({"1": 1}, ArrayLike) + assert not isinstance({1, 2, 3}, ArrayLike) -- Gitee From 13167b75fad60299d52e29fcc347eb3a2e7c0fef Mon Sep 17 00:00:00 2001 From: Gao Jianjian Date: Thu, 3 Apr 2025 10:35:21 +0800 Subject: [PATCH 3/3] refactor(circuit): improve parameter handling and introduce ArrayLike type Refactored the parameter handling logic in the circuit module to use the new ArrayLike type for better compatibility with array-like objects. Updated the typing module to include length and iterator methods for ArrayLike objects. Removed unnecessary numpy import and improved error messages for mismatched parameter lengths. Enhanced code readability and maintainability. --- cqlib/circuits/circuit.py | 19 ++++++++----------- cqlib/utils/typing.py | 9 +++++++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cqlib/circuits/circuit.py b/cqlib/circuits/circuit.py index 2b02212..eed3fe6 100644 --- a/cqlib/circuits/circuit.py +++ b/cqlib/circuits/circuit.py @@ -21,8 +21,6 @@ from copy import deepcopy from math import pi from typing import Union, Sequence, Optional -import numpy as np - from cqlib.circuits import gates from cqlib.circuits.barrier import Barrier from cqlib.circuits.instruction import Instruction @@ -30,6 +28,7 @@ from cqlib.circuits.instruction_data import InstructionData from cqlib.circuits.measure import Measure from cqlib.circuits.parameter import Parameter from cqlib.circuits.qubit import Qubit +from cqlib.utils.typing import ArrayLike # Type Alias Definition Qubits = Union[Qubit, int, Sequence[Union[Qubit, int]]] @@ -693,19 +692,17 @@ class Circuit: if values is None: values = {} _t = type(values) - if isinstance(values, (Sequence, np.ndarray)) or \ - (hasattr(_t, "__module__") and _t.__module__ == 'torch' - and _t.__name__ == 'Tensor'): - # list like type, list/tuple/np.ndarray/torch.Tensor - if len(values) != len(target.parameters_value): - raise ValueError(f"Length of values {len(values)} does not match " - f"the number of parameters {len(self._parameters)}.") - values = dict(zip(self._parameters, values)) + if isinstance(values, ArrayLike): + # Array like type, list/tuple/np.ndarray/torch.Tensor + if lv := len(values) != len(target.parameters_value): + raise ValueError(f"Length of values {lv} does not match " + f"the number of parameters {len(target.parameters_value)}.") + values = dict(zip(target.parameters_value, values)) values.update(kwargs) for param, value in values.items(): if isinstance(value, str): param = Parameter(param) - if param not in self._parameters: + if param not in target.parameters_value: raise KeyError(f"Parameter {param} not found.") # pylint: disable=protected-access diff --git a/cqlib/utils/typing.py b/cqlib/utils/typing.py index e452c57..7bdf0b4 100644 --- a/cqlib/utils/typing.py +++ b/cqlib/utils/typing.py @@ -21,6 +21,7 @@ This module contains the following classes: """ import contextlib import sys +from typing import Iterator import numpy as np @@ -59,6 +60,14 @@ class ArrayLike(metaclass=ArrayLikeMETA): False """ + def __len__(self) -> int: + """Length of the array-like object.""" + return len(self) + + def __iter__(self) -> Iterator: + """Iterate over array elements.""" + return iter(self) + def _is_torch(other, subclass=False): """ Check whether it is PyTorch tensor type. """ -- Gitee