From 7f809a9ff8545bf0dc7e22f0c0baed042e2105ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:04:22 +0100 Subject: [PATCH] Remove leftover algorithm/opflow utils (#11324) * Remove utils * Update reno * Fix test, reno * Update reno --- qiskit/utils/__init__.py | 16 -- qiskit/utils/arithmetic.py | 152 ------------------ qiskit/utils/circuit_utils.py | 69 -------- qiskit/utils/entangler_map.py | 111 ------------- qiskit/utils/name_unnamed_args.py | 73 --------- ...move-algorithm-utils-707648b69af439dc.yaml | 23 ++- test/python/test_util.py | 15 +- 7 files changed, 19 insertions(+), 440 deletions(-) delete mode 100644 qiskit/utils/arithmetic.py delete mode 100644 qiskit/utils/circuit_utils.py delete mode 100644 qiskit/utils/entangler_map.py delete mode 100644 qiskit/utils/name_unnamed_args.py diff --git a/qiskit/utils/__init__.py b/qiskit/utils/__init__.py index e12d30b5653b..a9b73b85f95d 100644 --- a/qiskit/utils/__init__.py +++ b/qiskit/utils/__init__.py @@ -29,14 +29,6 @@ .. autofunction:: detach_prefix .. autofunction:: wrap_method -Algorithm Utilities -=================== - -.. autofunction:: summarize_circuits -.. autofunction:: get_entangler_map -.. autofunction:: validate_entangler_map -.. autofunction:: name_args - Optional Dependency Checkers (:mod:`qiskit.utils.optionals`) ============================================================ @@ -59,18 +51,10 @@ from . import optionals -from .circuit_utils import summarize_circuits -from .entangler_map import get_entangler_map, validate_entangler_map -from .name_unnamed_args import name_args - __all__ = [ "LazyDependencyManager", "LazyImportTester", "LazySubprocessTester", - "summarize_circuits", - "get_entangler_map", - "validate_entangler_map", - "name_args", "add_deprecation_to_docstring", "deprecate_arg", "deprecate_arguments", diff --git a/qiskit/utils/arithmetic.py b/qiskit/utils/arithmetic.py deleted file mode 100644 index 23a838721f9e..000000000000 --- a/qiskit/utils/arithmetic.py +++ /dev/null @@ -1,152 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2019, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt 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. - -""" -Arithmetic Utilities -""" - -from typing import List, Tuple -import numpy as np - - -def normalize_vector(vector): - """ - Normalize the input state vector. - """ - return vector / np.linalg.norm(vector) - - -def is_power_of_2(num): - """ - Check if the input number is a power of 2. - """ - return num != 0 and ((num & (num - 1)) == 0) - - -def log2(num): - """ - Compute the log2 of the input number. Use bit operation if the input is a power of 2. - """ - if is_power_of_2(num): - ret = 0 - while True: - if num >> ret == 1: - return ret - else: - ret += 1 - else: - return np.log2(num) - - -def is_power(num, return_decomposition=False): - """ - Check if num is a perfect power in O(n^3) time, n=ceil(logN) - """ - b = 2 - while (2**b) <= num: - a = 1 - c = num - while (c - a) >= 2: - m = int((a + c) / 2) - - if (m**b) < (num + 1): - p = int(m**b) - else: - p = int(num + 1) - - if int(p) == int(num): - if return_decomposition: - return True, int(m), int(b) - else: - return True - - if p < num: - a = int(m) - else: - c = int(m) - b = b + 1 - if return_decomposition: - return False, num, 1 - else: - return False - - -def next_power_of_2_base(n): - """ - Return the base of the smallest power of 2 no less than the input number - """ - base = 0 - if n and not (n & (n - 1)): # pylint: disable=superfluous-parens - return log2(n) - - while n != 0: - n >>= 1 - base += 1 - - return base - - -def transpositions(permutation: List[int]) -> List[Tuple[int, int]]: - """Return a sequence of transpositions, corresponding to the permutation. - - Args: - permutation: The ``List[int]`` defining the permutation. An element at index ``j`` should be - permuted to index ``permutation[j]``. - - Returns: - List of transpositions, corresponding to the permutation. For permutation = [3, 0, 2, 1], - returns [(0,1), (0,3)] - """ - unchecked = [True] * len(permutation) - cyclic_form = [] - for i in range(len(permutation)): - if unchecked[i]: - cycle = [i] - unchecked[i] = False - j = i - while unchecked[permutation[j]]: - j = permutation[j] - cycle.append(j) - unchecked[j] = False - if len(cycle) > 1: - cyclic_form.append(cycle) - cyclic_form.sort() - res = [] - for x in cyclic_form: - len_x = len(x) - if len_x == 2: - res.append((x[0], x[1])) - elif len_x > 2: - first = x[0] - for y in x[len_x - 1 : 0 : -1]: - res.append((first, y)) - return res - - -def triu_to_dense(triu: np.ndarray) -> np.ndarray: - """Converts upper triangular part of matrix to dense matrix. - - Args: - triu: array in the form [[A, B, C], [D, E], [F]] - - Returns: - Array [[A, B, C], [B, D, E], [C, E, F]] - """ - dim = len(triu) - matrix = np.empty((dim, dim), dtype=complex) - for i in range(dim): - for j in range(dim - i): - matrix[i, i + j] = triu[i][j] - if j != 0: - matrix[i + j, i] = triu[i][j] - - return matrix diff --git a/qiskit/utils/circuit_utils.py b/qiskit/utils/circuit_utils.py deleted file mode 100644 index 2fe140d3780d..000000000000 --- a/qiskit/utils/circuit_utils.py +++ /dev/null @@ -1,69 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt 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. - -"""Circuit utility functions""" - -import numpy as np - - -def summarize_circuits(circuits): - """Summarize circuits based on QuantumCircuit, and five metrics are summarized. - - Number of qubits - - Number of classical bits - - Number of operations - - Depth of circuits - - Counts of different gate operations - - The average statistic of the first four is provided if multiple circuits are provided. - - Args: - circuits (QuantumCircuit or [QuantumCircuit]): the to-be-summarized circuits - - Returns: - str: a formatted string records the summary - """ - if not isinstance(circuits, list): - circuits = [circuits] - ret = "" - ret += f"Submitting {len(circuits)} circuits.\n" - ret += "============================================================================\n" - stats = np.zeros(4) - for i, circuit in enumerate(circuits): - depth = circuit.depth() - size = circuit.size() - num_qubits = sum(reg.size for reg in circuit.qregs) - num_clbits = sum(reg.size for reg in circuit.cregs) - op_counts = circuit.count_ops() - stats[0] += num_qubits - stats[1] += num_clbits - stats[2] += size - stats[3] += depth - ret = "".join( - [ - ret, - "{}-th circuit: {} qubits, {} classical bits and {} " - "operations with depth {}\nop_counts: {}\n".format( - i, num_qubits, num_clbits, size, depth, op_counts - ), - ] - ) - if len(circuits) > 1: - stats /= len(circuits) - ret = "".join( - [ - ret, - "Average: {:.2f} qubits, {:.2f} classical bits and {:.2f} " - "operations with depth {:.2f}\n".format(stats[0], stats[1], stats[2], stats[3]), - ] - ) - ret += "============================================================================\n" - return ret diff --git a/qiskit/utils/entangler_map.py b/qiskit/utils/entangler_map.py deleted file mode 100644 index 1cd750398ccb..000000000000 --- a/qiskit/utils/entangler_map.py +++ /dev/null @@ -1,111 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt 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. - -""" -This module contains the definition of creating and validating entangler map -based on the number of qubits. -""" - - -def get_entangler_map(map_type, num_qubits, offset=0): - """Utility method to get an entangler map among qubits. - - Args: - map_type (str): 'full' entangles each qubit with all the subsequent ones - 'linear' entangles each qubit with the next - 'sca' (shifted circular alternating entanglement) is a - circular entanglement where the 'long' entanglement is - shifted by one position every block and every block the - role or control/target qubits alternate - num_qubits (int): Number of qubits for which the map is needed - offset (int): Some map_types (e.g. 'sca') can shift the gates in - the entangler map by the specified integer offset. - - Returns: - list: A map of qubit index to an array of indexes to which this should be entangled - - Raises: - ValueError: if map_type is not valid. - """ - ret = [] - - if num_qubits > 1: - if map_type == "full": - ret = [[i, j] for i in range(num_qubits) for j in range(i + 1, num_qubits)] - elif map_type == "linear": - ret = [[i, i + 1] for i in range(num_qubits - 1)] - elif map_type == "sca": - offset_idx = offset % num_qubits - if offset_idx % 2 == 0: # even block numbers - for i in reversed(range(offset_idx)): - ret += [[i, i + 1]] - - ret += [[num_qubits - 1, 0]] - - for i in reversed(range(offset_idx + 1, num_qubits)): - ret += [[i - 1, i]] - - else: # odd block numbers - for i in range(num_qubits - offset_idx - 1, num_qubits - 1): - ret += [[i + 1, i]] - - ret += [[0, num_qubits - 1]] - - for i in range(num_qubits - offset_idx - 1): - ret += [[i + 1, i]] - else: - raise ValueError("map_type only supports 'full', 'linear' or 'sca' type.") - return ret - - -def validate_entangler_map(entangler_map, num_qubits, allow_double_entanglement=False): - """Validate a user supplied entangler map and converts entries to ints. - - Args: - entangler_map (list[list]) : An entangler map, keys are source qubit index (int), - value is array - of target qubit index(es) (int) - num_qubits (int) : Number of qubits - allow_double_entanglement (bool): If we allow in two qubits can be entangled each other - - Returns: - list: Validated/converted map - - Raises: - TypeError: entangler map is not list type or list of list - ValueError: the index of entangler map is out of range - ValueError: the qubits are cross-entangled. - - """ - - if isinstance(entangler_map, dict): - raise TypeError("The type of entangler map is changed to list of list.") - - if not isinstance(entangler_map, list): - raise TypeError("Entangler map type 'list' expected") - - for src_to_targ in entangler_map: - if not isinstance(src_to_targ, list): - raise TypeError(f"Entangle index list expected but got {type(src_to_targ)}") - - ret_map = [] - ret_map = [[int(src), int(targ)] for src, targ in entangler_map] - - for src, targ in ret_map: - if src < 0 or src >= num_qubits: - raise ValueError(f"Qubit entangle source value {src} invalid for {num_qubits} qubits") - if targ < 0 or targ >= num_qubits: - raise ValueError(f"Qubit entangle target value {targ} invalid for {num_qubits} qubits") - if not allow_double_entanglement and [targ, src] in ret_map: - raise ValueError(f"Qubit {src} and {targ} cross-entangled.") - - return ret_map diff --git a/qiskit/utils/name_unnamed_args.py b/qiskit/utils/name_unnamed_args.py deleted file mode 100644 index 4e153dcfefd2..000000000000 --- a/qiskit/utils/name_unnamed_args.py +++ /dev/null @@ -1,73 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2020. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt 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. - -"""Tool to name unnamed arguments.""" - -import functools - - -def name_args(mapping, skip=0): - """Decorator to convert unnamed arguments to named ones. - - Can be used to deprecate old signatures of a function, e.g. - - .. code-block:: - - old_f(a: TypeA, b: TypeB, c: TypeC) - new_f(a: TypeA, d: TypeD, b: TypeB=None, c: TypeC=None) - - Then, to support the old signature this decorator can be used as - - .. code-block:: - - @name_args([ - ('a'), # stays the same - ('d', {TypeB: 'b'}), # if arg is of type TypeB, call if 'b' else 'd' - ('b', {TypeC: 'c'}) - ]) - def new_f(a: TypeA, d: TypeD, b: TypeB=None, c: TypeC=None): - if b is not None: - # raise warning, this is deprecated! - if c is not None: - # raise warning, this is deprecated! - - """ - - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - # turn args into kwargs - for arg, replacement in zip(args[skip:], mapping): - default_name = replacement[0] - if len(replacement) == 1: # just renaming, no special cases - if default_name in kwargs: - raise ValueError(f"Name collapse on {default_name}") - kwargs[default_name] = arg - else: - # check if we find a special name - name = None - for special_type, special_name in replacement[1].items(): - if isinstance(arg, special_type): - name = special_name - break - if name is None: - name = default_name - - if name in kwargs: - raise ValueError(f"Name collapse on {default_name}") - kwargs[name] = arg - - return func(*args[:skip], **kwargs) - - return wrapper - - return decorator diff --git a/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml b/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml index 3e0dd0863026..67c551dad70f 100644 --- a/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml +++ b/releasenotes/notes/remove-algorithm-utils-707648b69af439dc.yaml @@ -1,14 +1,27 @@ --- upgrade: - | - The following algorithm utilities in :mod:`qiskit.utils` have been removed - from the codebase: + The following tools in :mod:`qiskit.utils` have been removed from the codebase: + * Utils in ``qiskit.utils.arithmetic`` + * Utils in ``qiskit.utils.circuit_utils`` + * Utils in ``qiskit.utils.entangler_map`` + * Utils in ``qiskit.utils.name_unnamed_args`` + + These functions were used exclusively in the context of ``qiskit.algorithms`` and + ``qiskit.opflow``, and were deprecated in Qiskit 0.46. ``qiskit.algorithms`` and + ``qiskit.opflow`` have been deprecated since Qiskit 0.45 and Qiskit Terra 0.24 + respectively. + + The following utilities have also been removed: + + * Utils in ``qiskit.utils.validation`` * ``algorithm_globals`` - * ``qiskit.utils.validation`` - They were deprecated in Qiskit 0.45 as a consequence of the migration + These were deprecated in Qiskit 0.45 as a consequence of the migration of ``qiskit.algorithms`` to a standalone `package `_, where - these utils have also been migrated. The can be found in the new package + these utils have also been migrated. They can be found in the new package under ``qiskit_algorithms.utils``. + + diff --git a/test/python/test_util.py b/test/python/test_util.py index b574ff8390b0..d60aad284c18 100644 --- a/test/python/test_util.py +++ b/test/python/test_util.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2018. +# (C) Copyright IBM 2017, 2023. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -13,11 +13,9 @@ """Tests for qiskit/utils""" from unittest import mock -import numpy as np from qiskit.utils.multiprocessing import local_hardware_info from qiskit.test import QiskitTestCase -from qiskit.utils.arithmetic import triu_to_dense class TestUtil(QiskitTestCase): @@ -31,14 +29,3 @@ def test_local_hardware_none_cpu_count(self, cpu_count_mock, vmem_mock, platform del cpu_count_mock, vmem_mock, platform_mock # unused result = local_hardware_info() self.assertEqual(1, result["cpus"]) - - def test_triu_to_dense(self): - """Test conversion of upper triangular matrix to dense matrix.""" - np.random.seed(50) - n = np.random.randint(5, 15) - m = np.random.randint(-100, 100, size=(n, n)) - symm = (m + m.T) / 2 - - triu = [[symm[i, j] for i in range(j, n)] for j in range(n)] - - self.assertTrue(np.array_equal(symm, triu_to_dense(triu)))