diff --git a/docs/legacy_release_notes.rst b/docs/legacy_release_notes.rst index 486d952fe91a..b8a64228c0f9 100644 --- a/docs/legacy_release_notes.rst +++ b/docs/legacy_release_notes.rst @@ -3619,7 +3619,7 @@ Bug Fixes .. releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @ b'4152009ee6d1bae8704f1e7b9ccc5727a53933bd' - The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now handle multiple and nested - definitions of :class:`.UnitaryGate`. See + definitions of :class:`~.library.UnitaryGate`. See `#4623 `__, `#6712 `__, `#7772 `__, and @@ -5123,7 +5123,7 @@ New Features library gate classes have been updated to return more specific gate objects that result in a less lossy and more efficient output. For example, running :meth:`~.IGate.power` now returns an :class:`~.IGate` - instance instead of :class:`~.UnitaryGate` as was done previously. + instance instead of :class:`~.library.UnitaryGate` as was done previously. The full list of output types that have been improved are: @@ -9148,7 +9148,7 @@ Bug Fixes .. releasenotes/notes/0.22/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml @ b'618770367f7a5a3a22fd43ea9fcfb7f17393eb6a' - The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now correctly - define the qubit parameters for :class:`.UnitaryGate` operations that do + define the qubit parameters for :class:`~.library.UnitaryGate` operations that do not affect all the qubits they are defined over. Fixed `#8224 `__. @@ -9372,7 +9372,7 @@ Known Issues .. releasenotes/notes/0.11/non-x86_ibm_cpu-493e51313ba222a6.yaml @ b'b7c4a322f8409fc2809b57b0701d1da6717c7efd' - When running on Linux s390x platforms (or other big endian platforms) - running circuits that contain :class:`~.UnitaryGate` operations will not + running circuits that contain :class:`~.library.UnitaryGate` operations will not work because of an endianess bug. See `#1506 `__ for more details. @@ -9692,7 +9692,7 @@ Bug Fixes .. releasenotes/notes/fix-evolvedop-to-instruction-c90c4f1aa6b4232a.yaml @ b'664747a66e2199a4b20abb9b7180cccb12c61a3f' - Fix :meth:`~.EvolvedOp.to_instruction` which previously tried to create a - :class:`~.UnitaryGate` without exponentiating the operator to evolve. + :class:`~.library.UnitaryGate` without exponentiating the operator to evolve. Since this operator is generally not unitary, this raised an error (and if the operator would have been unitary by chance, it would not have been the expected result). @@ -10076,7 +10076,7 @@ New Features Shannon Decomposition of arbitrary unitaries. This functionality replaces the previous isometry-based approach in the default unitary synthesis transpiler pass as well as when adding unitaries to a circuit using a - :class:`.UnitaryGate`. + :class:`~.library.UnitaryGate`. The Quantum Shannon Decomposition uses about half the cnot gates as the isometry implementation when decomposing unitary matrices of greater than @@ -10732,7 +10732,7 @@ Bug Fixes .. releasenotes/notes/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml @ b'd7f932c8b242f69c5577afd5593bf36f839657f7' - The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now correctly - define the qubit parameters for :class:`.UnitaryGate` operations that do + define the qubit parameters for :class:`~.library.UnitaryGate` operations that do not affect all the qubits they are defined over. Fixed `#8224 `__. @@ -10914,10 +10914,10 @@ Known Issues .. releasenotes/notes/ucr-gates-qpy-b8f6fb1e34fae258.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - QPY deserialization with the :func:`.qpy.load` function of a directly - instantiated :class:`~.UCPauliRotGate` object in a circuit will fail + instantiated :class:`~.library.UCPauliRotGate` object in a circuit will fail because the rotation axis argument to the class isn't stored in a standard place. To workaround this you can instead use the subclasses: - :class:`~.UCRXGate`, :class:`~.UCRYGate`, or :class:`~.UCRZGate` (based on + :class:`~.library.UCRXGate`, :class:`~.library.UCRYGate`, or :class:`~.library.UCRZGate` (based on whether you're using a rotation axis of ``"X"``, ``"Y"``, or ``"Z"`` respectively) which embeds the rotation axis in the class constructor and will work correctly in QPY. @@ -11016,7 +11016,7 @@ Bug Fixes .. releasenotes/notes/ucr-gates-qpy-b8f6fb1e34fae258.yaml @ b'625b202a4dd0c223579dca44eec530b8a0813d76' - Fixed an issue with QPY deserialization via the :func:`.qpy.load` function - of the :class:`~.UCRXGate`, :class:`~.UCRYGate`, and :class:`~.UCRZGate` + of the :class:`~.library.UCRXGate`, :class:`~.library.UCRYGate`, and :class:`~.library.UCRZGate` classes. Previously, a QPY file that contained any of these gates would error when trying to load the file. diff --git a/docs/migration_guides/opflow_migration.rst b/docs/migration_guides/opflow_migration.rst index 34554934a9cd..d84638d4efc0 100644 --- a/docs/migration_guides/opflow_migration.rst +++ b/docs/migration_guides/opflow_migration.rst @@ -1004,8 +1004,8 @@ delayed synthesis of the gates or efficient transpilation of the circuits, so th (for example, :class:`~qiskit.algorithms.time_evolvers.trotterization.TrotterQRTE`\). In a similar manner, the :class:`qiskit.opflow.evolutions.MatrixEvolution` class performs evolution by classical matrix exponentiation, - constructing a circuit with :class:`.UnitaryGate`\s or :class:`.HamiltonianGate`\s containing the exponentiation of the operator. - This class is no longer necessary, as the :class:`.HamiltonianGate`\s can be directly handled by the algorithms. + constructing a circuit with :class:`~.library.UnitaryGate`\s or :class:`~.library.HamiltonianGate`\s containing the exponentiation of the operator. + This class is no longer necessary, as the :class:`~.library.HamiltonianGate`\s can be directly handled by the algorithms. Trotterizations --------------- @@ -1046,7 +1046,7 @@ Other Evolution Classes - No direct replacement. The workflow no longer requires a specific operator for evolutions. * - :class:`~qiskit.opflow.evolutions.MatrixEvolution` - - :class:`.HamiltonianGate` + - :class:`~.library.HamiltonianGate` * - :class:`~qiskit.opflow.evolutions.PauliTrotterEvolution` - :class:`.PauliEvolutionGate` diff --git a/qiskit/__init__.py b/qiskit/__init__.py index a3282beff075..20f2e3c80851 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -66,9 +66,6 @@ # user config from qiskit import user_config as _user_config -# The qiskit.extensions.x imports needs to be placed here due to the -# mechanism for adding gates dynamically. -import qiskit.extensions import qiskit.circuit.measure import qiskit.circuit.reset diff --git a/qiskit/assembler/disassemble.py b/qiskit/assembler/disassemble.py index bd685b14996b..0db860f12e73 100644 --- a/qiskit/assembler/disassemble.py +++ b/qiskit/assembler/disassemble.py @@ -36,6 +36,20 @@ # and a header dictionary. PulseModule = NewType("PulseModule", Tuple[List[pulse.Schedule], Dict[str, Any], Dict[str, Any]]) +# Prevent the disassembler from accessing deprecated circuit methods. This can happen for +# gates where the name of the gate matches a circuit method (e.g. Isometry.name is "isometry") +# and the circuit attribute is also QuantumCircuit.isometry +_DEPRECATED_CIRCUIT_METHODS = { + "isometry", + "snapshot", + "ucrx", + "ucry", + "ucrz", + "squ", + "diagonal", + "hamiltonian", +} + def disassemble(qobj) -> Union[CircuitModule, PulseModule]: """Disassemble a qobj and return the circuits or pulse schedules, run_config, and user header. @@ -165,16 +179,12 @@ def _experiments_to_circuits(qobj): clbits.append(creg_dict[clbit_label[0]][clbit_label[1]]) except Exception: # pylint: disable=broad-except pass - if hasattr(circuit, name): + # TODO remove the check that name is not in the deprecated circuit methods + # once the methods have been removed + if hasattr(circuit, name) and name not in _DEPRECATED_CIRCUIT_METHODS: instr_method = getattr(circuit, name) - if i.name in ["snapshot"]: - _inst = instr_method( - i.label, snapshot_type=i.snapshot_type, qubits=qubits, params=params - ) - elif i.name == "initialize": + if i.name == "initialize": _inst = instr_method(params, qubits) - elif i.name == "isometry": - _inst = instr_method(*params, qubits, clbits) elif i.name in ["mcx", "mcu1", "mcp"]: _inst = instr_method(*params, qubits[:-1], qubits[-1], *clbits) else: diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index 46fe2de0c68a..ca4fbd8ce9fb 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -14,7 +14,7 @@ from __future__ import annotations from qiskit.circuit.exceptions import CircuitError -from qiskit.extensions import UnitaryGate +from qiskit.circuit.library import UnitaryGate from . import ControlledGate, Gate, QuantumRegister, QuantumCircuit from ._utils import _ctrl_state_to_int diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index b9b9c0063bd7..0680fe0d6e91 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -68,13 +68,15 @@ def power(self, exponent: float): exponent (float): Gate^exponent Returns: - qiskit.extensions.UnitaryGate: To which `to_matrix` is self.to_matrix^exponent. + .library.UnitaryGate: To which `to_matrix` is self.to_matrix^exponent. Raises: CircuitError: If Gate is not unitary """ - from qiskit.quantum_info.operators import Operator # pylint: disable=cyclic-import - from qiskit.extensions.unitary import UnitaryGate # pylint: disable=cyclic-import + # pylint: disable=cyclic-import + from qiskit.quantum_info.operators import Operator + from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate + from scipy.linalg import schur # Should be diagonalized because it's a unitary. diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 2e3637924dab..650b439b91e9 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -181,6 +181,7 @@ :template: autosummary/class_no_inherited_members.rst Diagonal + DiagonalGate MCMT MCMTVChain Permutation @@ -198,6 +199,13 @@ RVGate PauliGate LinearFunction + Isometry + UnitaryGate + UCGate + UCPauliRotGate + UCRXGate + UCRYGate + UCRZGate Boolean Logic Circuits ====================== @@ -324,6 +332,7 @@ PhaseOracle EvolvedOperatorAnsatz PauliEvolutionGate + HamiltonianGate N-local circuits @@ -360,6 +369,7 @@ ZFeatureMap ZZFeatureMap StatePreparation + Initialize Template circuits ================= @@ -492,6 +502,7 @@ from .blueprintcircuit import BlueprintCircuit from .generalized_gates import ( Diagonal, + DiagonalGate, MCMT, MCMTVChain, Permutation, @@ -505,8 +516,16 @@ RVGate, PauliGate, LinearFunction, + Isometry, + UnitaryGate, + UCGate, + UCPauliRotGate, + UCRXGate, + UCRYGate, + UCRZGate, ) from .pauli_evolution import PauliEvolutionGate +from .hamiltonian_gate import HamiltonianGate from .boolean_logic import ( AND, OR, @@ -542,7 +561,13 @@ ExcitationPreserving, QAOAAnsatz, ) -from .data_preparation import PauliFeatureMap, ZFeatureMap, ZZFeatureMap, StatePreparation +from .data_preparation import ( + PauliFeatureMap, + ZFeatureMap, + ZZFeatureMap, + StatePreparation, + Initialize, +) from .quantum_volume import QuantumVolume from .fourier_checking import FourierChecking from .graph_state import GraphState diff --git a/qiskit/circuit/library/arithmetic/exact_reciprocal.py b/qiskit/circuit/library/arithmetic/exact_reciprocal.py index cd88880f590e..5b53cf6e47b5 100644 --- a/qiskit/circuit/library/arithmetic/exact_reciprocal.py +++ b/qiskit/circuit/library/arithmetic/exact_reciprocal.py @@ -14,7 +14,7 @@ from math import isclose import numpy as np from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.extensions.quantum_initializer import UCRYGate +from qiskit.circuit.library.generalized_gates import UCRYGate class ExactReciprocal(QuantumCircuit): diff --git a/qiskit/circuit/library/data_preparation/__init__.py b/qiskit/circuit/library/data_preparation/__init__.py index fbd033996c9e..38611c911fa4 100644 --- a/qiskit/circuit/library/data_preparation/__init__.py +++ b/qiskit/circuit/library/data_preparation/__init__.py @@ -42,5 +42,6 @@ from .z_feature_map import ZFeatureMap from .zz_feature_map import ZZFeatureMap from .state_preparation import StatePreparation +from .initializer import Initialize -__all__ = ["PauliFeatureMap", "ZFeatureMap", "ZZFeatureMap", "StatePreparation"] +__all__ = ["PauliFeatureMap", "ZFeatureMap", "ZZFeatureMap", "StatePreparation", "Initialize"] diff --git a/qiskit/circuit/library/data_preparation/initializer.py b/qiskit/circuit/library/data_preparation/initializer.py new file mode 100644 index 000000000000..394f863191d5 --- /dev/null +++ b/qiskit/circuit/library/data_preparation/initializer.py @@ -0,0 +1,96 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017. +# +# 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. + +""" +Initialize qubit registers to desired arbitrary state. +""" + +from __future__ import annotations +from collections.abc import Sequence +import typing + +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.instruction import Instruction +from .state_preparation import StatePreparation + +if typing.TYPE_CHECKING: + from qiskit.quantum_info.states.statevector import Statevector + +_EPS = 1e-10 # global variable used to chop very small numbers to zero + + +class Initialize(Instruction): + """Complex amplitude initialization. + + Class that initializes some flexible collection of qubit registers, implemented by calling + the :class:`~.library.StatePreparation` class. + Note that ``Initialize`` is an :class:`~.circuit.Instruction` and not a :class:`.Gate` since it + contains a reset instruction, which is not unitary. + """ + + def __init__( + self, + params: Statevector | Sequence[complex] | str | int, + num_qubits: int | None = None, + normalize: bool = False, + ) -> None: + r""" + Args: + params: The state to initialize to, can be either of the following. + + * Statevector or vector of complex amplitudes to initialize to. + * Labels of basis states of the Pauli eigenstates Z, X, Y. See + :meth:`.Statevector.from_label`. Notice the order of the labels is reversed with + respect to the qubit index to be applied to. Example label '01' initializes the + qubit zero to :math:`|1\rangle` and the qubit one to :math:`|0\rangle`. + * An integer that is used as a bitmap indicating which qubits to initialize to + :math:`|1\rangle`. Example: setting params to 5 would initialize qubit 0 and qubit + 2 to :math:`|1\rangle` and qubit 1 to :math:`|0\rangle`. + + num_qubits: This parameter is only used if params is an int. Indicates the total + number of qubits in the `initialize` call. Example: `initialize` covers 5 qubits + and params is 3. This allows qubits 0 and 1 to be initialized to :math:`|1\rangle` + and the remaining 3 qubits to be initialized to :math:`|0\rangle`. + normalize: Whether to normalize an input array to a unit vector. + """ + self._stateprep = StatePreparation(params, num_qubits, normalize=normalize) + + super().__init__("initialize", self._stateprep.num_qubits, 0, self._stateprep.params) + + def _define(self): + q = QuantumRegister(self.num_qubits, "q") + initialize_circuit = QuantumCircuit(q, name="init_def") + initialize_circuit.reset(q) + initialize_circuit.append(self._stateprep, q) + self.definition = initialize_circuit + + def gates_to_uncompute(self) -> QuantumCircuit: + """Call to create a circuit with gates that take the desired vector to zero. + + Returns: + Circuit to take ``self.params`` vector to :math:`|{00\\ldots0}\\rangle` + """ + return self._stateprep._gates_to_uncompute() + + @property + def params(self): + """Return initialize params.""" + return self._stateprep.params + + @params.setter + def params(self, parameters: Statevector | Sequence[complex] | str | int) -> None: + """Set initialize params.""" + self._stateprep.params = parameters + + def broadcast_arguments(self, qargs, cargs): + return self._stateprep.broadcast_arguments(qargs, cargs) diff --git a/qiskit/circuit/library/data_preparation/state_preparation.py b/qiskit/circuit/library/data_preparation/state_preparation.py index 3e26c0e355d8..d717630d2da6 100644 --- a/qiskit/circuit/library/data_preparation/state_preparation.py +++ b/qiskit/circuit/library/data_preparation/state_preparation.py @@ -17,7 +17,8 @@ import numpy as np from qiskit.exceptions import QiskitError -from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumregister import QuantumRegister, Qubit from qiskit.circuit.gate import Gate from qiskit.circuit.library.standard_gates.x import CXGate, XGate from qiskit.circuit.library.standard_gates.h import HGate @@ -25,7 +26,7 @@ from qiskit.circuit.library.standard_gates.ry import RYGate from qiskit.circuit.library.standard_gates.rz import RZGate from qiskit.circuit.exceptions import CircuitError -from qiskit.quantum_info import Statevector +from qiskit.quantum_info.states.statevector import Statevector # pylint: disable=cyclic-import _EPS = 1e-10 # global variable used to chop very small numbers to zero diff --git a/qiskit/circuit/library/evolved_operator_ansatz.py b/qiskit/circuit/library/evolved_operator_ansatz.py index 5dd1914543b8..a3832bfb71a6 100644 --- a/qiskit/circuit/library/evolved_operator_ansatz.py +++ b/qiskit/circuit/library/evolved_operator_ansatz.py @@ -175,7 +175,9 @@ def preferred_init_points(self): def _evolve_operator(self, operator, time): from qiskit.opflow import OperatorBase, EvolutionBase - from qiskit.extensions import HamiltonianGate + + # pylint: disable=cyclic-import + from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate if isinstance(operator, OperatorBase): if not isinstance(self.evolution, EvolutionBase): diff --git a/qiskit/circuit/library/fourier_checking.py b/qiskit/circuit/library/fourier_checking.py index 696d94ba654c..b31dc9748e83 100644 --- a/qiskit/circuit/library/fourier_checking.py +++ b/qiskit/circuit/library/fourier_checking.py @@ -18,6 +18,8 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError +from .generalized_gates.diagonal import Diagonal + class FourierChecking(QuantumCircuit): """Fourier checking circuit. @@ -83,11 +85,11 @@ def __init__(self, f: List[int], g: List[int]) -> None: circuit.h(circuit.qubits) - circuit.diagonal(f, circuit.qubits) + circuit.compose(Diagonal(f), inplace=True) circuit.h(circuit.qubits) - circuit.diagonal(g, circuit.qubits) + circuit.compose(Diagonal(g), inplace=True) circuit.h(circuit.qubits) diff --git a/qiskit/circuit/library/generalized_gates/__init__.py b/qiskit/circuit/library/generalized_gates/__init__.py index 43c0b4c36e5a..6a704cdef286 100644 --- a/qiskit/circuit/library/generalized_gates/__init__.py +++ b/qiskit/circuit/library/generalized_gates/__init__.py @@ -12,7 +12,7 @@ """The circuit library module on generalized gates.""" -from .diagonal import Diagonal +from .diagonal import Diagonal, DiagonalGate from .permutation import Permutation, PermutationGate from .mcmt import MCMT, MCMTVChain from .gms import GMS, MSGate @@ -20,3 +20,11 @@ from .pauli import PauliGate from .rv import RVGate from .linear_function import LinearFunction +from .isometry import Isometry +from .uc import UCGate +from .uc_pauli_rot import UCPauliRotGate +from .ucrx import UCRXGate +from .ucry import UCRYGate +from .ucrz import UCRZGate +from .unitary import UnitaryGate +from .mcg_up_to_diagonal import MCGupDiag diff --git a/qiskit/circuit/library/generalized_gates/diagonal.py b/qiskit/circuit/library/generalized_gates/diagonal.py index af601d88cac3..3b2bd3a40182 100644 --- a/qiskit/circuit/library/generalized_gates/diagonal.py +++ b/qiskit/circuit/library/generalized_gates/diagonal.py @@ -14,12 +14,17 @@ """Diagonal matrix circuit.""" from __future__ import annotations +from collections.abc import Sequence + import cmath import numpy as np +from qiskit.circuit.gate import Gate from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError +from .ucrz import UCRZGate + _EPS = 1e-10 @@ -50,16 +55,16 @@ class Diagonal(QuantumCircuit): \end{pmatrix} Diagonal gates are useful as representations of Boolean functions, - as they can map from {0,1}^2**n to {0,1}^2**n space. For example a phase - oracle can be seen as a diagonal gate with {+1, -1} on the diagonals. Such - an oracle will induce a +1 or -1 phase on the amplitude of any corresponding + as they can map from :math:`\{0,1\}^{2^n}` to :math:`\{0,1\}^{2^n}` space. For example a phase + oracle can be seen as a diagonal gate with :math:`\{1, -1\}` on the diagonals. Such + an oracle will induce a :math:`+1` or :math`-1` phase on the amplitude of any corresponding basis state. Diagonal gates appear in many classically hard oracular problems such as Forrelation or Hidden Shift circuits. Diagonal gates are represented and simulated more efficiently than a dense - 2**n x 2**n unitary matrix. + :math:`2^n \times 2^n` unitary matrix. The reference implementation is via the method described in Theorem 7 of [1]. The code is based on Emanuel Malvetti's semester thesis @@ -71,25 +76,18 @@ class Diagonal(QuantumCircuit): `arXiv:0406176 `_ """ - def __init__(self, diag: list[complex] | np.ndarray) -> None: - """Create a new Diagonal circuit. - + def __init__(self, diag: Sequence[complex]) -> None: + r""" Args: - diag: list of the 2^k diagonal entries (for a diagonal gate on k qubits). + diag: List of the :math:`2^k` diagonal entries (for a diagonal gate on :math:`k` qubits). Raises: CircuitError: if the list of the diagonal entries or the qubit list is in bad format; - if the number of diagonal entries is not 2^k, where k denotes the number of qubits + if the number of diagonal entries is not :math:`2^k`, where :math:`k` denotes the + number of qubits. """ - if not isinstance(diag, (list, np.ndarray)): - raise CircuitError("Diagonal entries must be in a list or numpy array.") - num_qubits = np.log2(len(diag)) - if num_qubits < 1 or not num_qubits.is_integer(): - raise CircuitError("The number of diagonal entries is not a positive power of 2.") - if not np.allclose(np.abs(diag), 1, atol=_EPS): - raise CircuitError("A diagonal element does not have absolute value one.") - - num_qubits = int(num_qubits) + self._check_input(diag) + num_qubits = int(np.log2(len(diag))) circuit = QuantumCircuit(num_qubits, name="Diagonal") @@ -105,19 +103,62 @@ def __init__(self, diag: list[complex] | np.ndarray) -> None: num_act_qubits = int(np.log2(n)) ctrl_qubits = list(range(num_qubits - num_act_qubits + 1, num_qubits)) target_qubit = num_qubits - num_act_qubits - circuit.ucrz(angles_rz, ctrl_qubits, target_qubit) + + ucrz = UCRZGate(angles_rz) + circuit.append(ucrz, [target_qubit] + ctrl_qubits) + n //= 2 circuit.global_phase += diag_phases[0] super().__init__(num_qubits, name="Diagonal") self.append(circuit.to_gate(), self.qubits) + @staticmethod + def _check_input(diag): + """Check if ``diag`` is in valid format.""" + if not isinstance(diag, (list, np.ndarray)): + raise CircuitError("Diagonal entries must be in a list or numpy array.") + num_qubits = np.log2(len(diag)) + if num_qubits < 1 or not num_qubits.is_integer(): + raise CircuitError("The number of diagonal entries is not a positive power of 2.") + if not np.allclose(np.abs(diag), 1, atol=_EPS): + raise CircuitError("A diagonal element does not have absolute value one.") + -# extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) -# is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2) +class DiagonalGate(Gate): + """Gate implementing a diagonal transformation.""" + + def __init__(self, diag: Sequence[complex]) -> None: + r""" + Args: + diag: list of the :math:`2^k` diagonal entries (for a diagonal gate on :math:`k` qubits). + """ + Diagonal._check_input(diag) + num_qubits = int(np.log2(len(diag))) + + super().__init__("diagonal", num_qubits, diag) + + def _define(self): + self.definition = Diagonal(self.params).decompose() + + def validate_parameter(self, parameter): + """Diagonal Gate parameter should accept complex + (in addition to the Gate parameter types) and always return build-in complex.""" + if isinstance(parameter, complex): + return complex(parameter) + else: + return complex(super().validate_parameter(parameter)) + + def inverse(self): + """Return the inverse of the diagonal gate.""" + return DiagonalGate([np.conj(entry) for entry in self.params]) def _extract_rz(phi1, phi2): + """ + Extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) + is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2). + """ phase = (phi1 + phi2) / 2.0 z_angle = phi2 - phi1 return phase, z_angle diff --git a/qiskit/extensions/quantum_initializer/isometry.py b/qiskit/circuit/library/generalized_gates/isometry.py similarity index 84% rename from qiskit/extensions/quantum_initializer/isometry.py rename to qiskit/circuit/library/generalized_gates/isometry.py index 611413e9f56f..dc5dc3c85bb3 100644 --- a/qiskit/extensions/quantum_initializer/isometry.py +++ b/qiskit/circuit/library/generalized_gates/isometry.py @@ -19,6 +19,8 @@ Generic isometries from m to n qubits. """ +from __future__ import annotations + import itertools import numpy as np from qiskit.circuit.exceptions import CircuitError @@ -27,33 +29,26 @@ from qiskit.circuit.quantumregister import QuantumRegister from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_isometry -from qiskit.extensions.quantum_initializer.uc import UCGate -from qiskit.extensions.quantum_initializer.mcg_up_to_diagonal import MCGupDiag + +from .diagonal import Diagonal +from .uc import UCGate +from .mcg_up_to_diagonal import MCGupDiag _EPS = 1e-10 # global variable used to chop very small numbers to zero class Isometry(Instruction): - """ - Decomposition of arbitrary isometries from m to n qubits. In particular, this allows to - decompose unitaries (m=n) and to do state preparation (m=0). + r"""Decomposition of arbitrary isometries from :math:`m` to :math:`n` qubits. - The decomposition is based on https://arxiv.org/abs/1501.06911. + In particular, this allows to decompose unitaries (m=n) and to do state preparation (:math:`m=0`). - Args: - isometry (ndarray): an isometry from m to n qubits, i.e., a (complex) - np.ndarray of dimension 2^n*2^m with orthonormal columns (given - in the computational basis specified by the order of the ancillas - and the input qubits, where the ancillas are considered to be more - significant than the input qubits). + The decomposition is based on [1]. - num_ancillas_zero (int): number of additional ancillas that start in the state ket(0) - (the n-m ancillas required for providing the output of the isometry are - not accounted for here). + **References:** - num_ancillas_dirty (int): number of additional ancillas that start in an arbitrary state + [1] Iten et al., Quantum circuits for isometries (2016). + `Phys. Rev. A 93, 032318 `__. - epsilon (float) (optional): error tolerance of calculations """ # Notation: In the following decomposition we label the qubit by @@ -63,7 +58,26 @@ class Isometry(Instruction): # finally, we convert the labels back to the qubit numbering used in Qiskit # (using: _get_qubits_by_label) - def __init__(self, isometry, num_ancillas_zero, num_ancillas_dirty, epsilon=_EPS): + def __init__( + self, + isometry: np.ndarray, + num_ancillas_zero: int, + num_ancillas_dirty: int, + epsilon: float = _EPS, + ) -> None: + r""" + Args: + isometry: An isometry from :math:`m` to :math`n` qubits, i.e., a complex + ``np.ndarray`` of dimension :math:`2^n \times 2^m` with orthonormal columns (given + in the computational basis specified by the order of the ancillas + and the input qubits, where the ancillas are considered to be more + significant than the input qubits). + num_ancillas_zero: Number of additional ancillas that start in the state :math:`|0\rangle` + (the :math:`n-m` ancillas required for providing the output of the isometry are + not accounted for here). + num_ancillas_dirty: Number of additional ancillas that start in an arbitrary state. + epsilon: Error tolerance of calculations. + """ # Convert to numpy array in case not already an array isometry = np.array(isometry, dtype=complex) @@ -103,7 +117,6 @@ def __init__(self, isometry, num_ancillas_zero, num_ancillas_dirty, epsilon=_EPS super().__init__("isometry", num_qubits, 0, [isometry]) def _define(self): - # TODO The inverse().inverse() is because there is code to uncompute (_gates_to_uncompute) # an isometry, but not for generating its decomposition. It would be cheaper to do the # later here instead. @@ -149,7 +162,8 @@ def _gates_to_uncompute(self): # remove first column (which is now stored in diag) remaining_isometry = remaining_isometry[:, 1:] if len(diag) > 1 and not _diag_is_identity_up_to_global_phase(diag, self._epsilon): - circuit.diagonal(np.conj(diag).tolist(), q_input) + diagonal = Diagonal(np.conj(diag)) + circuit.append(diagonal, q_input) return circuit def _decompose_column(self, circuit, q, diag, remaining_isometry, column_index): @@ -557,71 +571,3 @@ def _diag_is_identity_up_to_global_phase(diag, epsilon): if not np.abs(global_phase * d - 1) < epsilon: return False return True - - -def iso( - self, - isometry, - q_input, - q_ancillas_for_output, - q_ancillas_zero=None, - q_ancillas_dirty=None, - epsilon=_EPS, -): - """ - Attach an arbitrary isometry from m to n qubits to a circuit. In particular, - this allows to attach arbitrary unitaries on n qubits (m=n) or to prepare any state - on n qubits (m=0). - The decomposition used here was introduced by Iten et al. in https://arxiv.org/abs/1501.06911. - - Args: - isometry (ndarray): an isometry from m to n qubits, i.e., a (complex) ndarray of - dimension 2^n×2^m with orthonormal columns (given in the computational basis - specified by the order of the ancillas and the input qubits, where the ancillas - are considered to be more significant than the input qubits.). - q_input (QuantumRegister|list[Qubit]): list of m qubits where the input - to the isometry is fed in (empty list for state preparation). - q_ancillas_for_output (QuantumRegister|list[Qubit]): list of n-m ancilla - qubits that are used for the output of the isometry and which are assumed to start - in the zero state. The qubits are listed with increasing significance. - q_ancillas_zero (QuantumRegister|list[Qubit]): list of ancilla qubits - which are assumed to start in the zero state. Default is q_ancillas_zero = None. - q_ancillas_dirty (QuantumRegister|list[Qubit]): list of ancilla qubits - which can start in an arbitrary state. Default is q_ancillas_dirty = None. - epsilon (float): error tolerance of calculations. - Default is epsilon = _EPS. - - Returns: - QuantumCircuit: the isometry is attached to the quantum circuit. - - Raises: - QiskitError: if the array is not an isometry of the correct size corresponding to - the provided number of qubits. - """ - if q_input is None: - q_input = [] - if q_ancillas_for_output is None: - q_ancillas_for_output = [] - if q_ancillas_zero is None: - q_ancillas_zero = [] - if q_ancillas_dirty is None: - q_ancillas_dirty = [] - - if isinstance(q_input, QuantumRegister): - q_input = q_input[:] - if isinstance(q_ancillas_for_output, QuantumRegister): - q_ancillas_for_output = q_ancillas_for_output[:] - if isinstance(q_ancillas_zero, QuantumRegister): - q_ancillas_zero = q_ancillas_zero[:] - if isinstance(q_ancillas_dirty, QuantumRegister): - q_ancillas_dirty = q_ancillas_dirty[:] - - return self.append( - Isometry(isometry, len(q_ancillas_zero), len(q_ancillas_dirty), epsilon=epsilon), - q_input + q_ancillas_for_output + q_ancillas_zero + q_ancillas_dirty, - ) - - -# support both QuantumCircuit.iso and QuantumCircuit.isometry -QuantumCircuit.iso = iso -QuantumCircuit.isometry = iso diff --git a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py b/qiskit/circuit/library/generalized_gates/mcg_up_to_diagonal.py similarity index 85% rename from qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py rename to qiskit/circuit/library/generalized_gates/mcg_up_to_diagonal.py index 787dcb636376..fbec69f2e884 100644 --- a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py +++ b/qiskit/circuit/library/generalized_gates/mcg_up_to_diagonal.py @@ -12,9 +12,7 @@ # pylint: disable=unused-variable -""" -Multi controlled single-qubit unitary up to diagonal. -""" +"""Multi controlled single-qubit unitary up to diagonal.""" # ToDo: This code should be merged wth the implementation of MCGs # ToDo: (introducing a decomposition mode "up_to_diagonal"). @@ -23,30 +21,33 @@ from qiskit.circuit import Gate from qiskit.circuit.quantumcircuit import QuantumRegister, QuantumCircuit -from qiskit.quantum_info.operators.predicates import is_isometry -from qiskit.exceptions import QiskitError from qiskit.circuit.exceptions import CircuitError -from qiskit.extensions.quantum_initializer.uc import UCGate +from qiskit.exceptions import QiskitError +from qiskit.quantum_info.operators.predicates import is_isometry + +from .uc import UCGate _EPS = 1e-10 # global variable used to chop very small numbers to zero class MCGupDiag(Gate): - """ - Decomposes a multi-controlled gate u up to a diagonal d acting on the control and target qubit - (but not on the ancilla qubits), i.e., it implements a circuit corresponding to a unitary u' - such that u=d.u'. + r""" + Decomposes a multi-controlled gate :math:`U` up to a diagonal :math:`D` acting on the control + and target qubit (but not on the ancilla qubits), i.e., it implements a circuit corresponding to + a unitary :math:`U'`, such that :math:`U = D U'`. """ - def __init__(self, gate, num_controls, num_ancillas_zero, num_ancillas_dirty): - """Initialize a multi controlled gate. - + def __init__( + self, gate: np.ndarray, num_controls: int, num_ancillas_zero: int, num_ancillas_dirty: int + ) -> None: + r""" Args: - gate (ndarray): 2*2 unitary (given as a (complex) ndarray) - num_controls (int): number of control qubits - num_ancillas_zero (int): number of ancilla qubits that start in the state zero - num_ancillas_dirty (int): number of ancilla qubits that are allowed to start in an - arbitrary state + gate: :math:`2 \times 2` unitary given as a (complex) ``ndarray``. + num_controls: Number of control qubits. + num_ancillas_zero: Number of ancilla qubits that start in the state zero. + num_ancillas_dirty: Number of ancilla qubits that are allowed to start in an + arbitrary state. + Raises: QiskitError: if the input format is wrong; if the array gate is not unitary """ @@ -72,7 +73,7 @@ def _define(self): mcg_up_diag_circuit.append(gate, q[:]) self.definition = mcg_up_diag_circuit - def inverse(self): + def inverse(self) -> Gate: """Return the inverse. Note that the resulting Gate object has an empty ``params`` property. diff --git a/qiskit/circuit/library/generalized_gates/mcmt.py b/qiskit/circuit/library/generalized_gates/mcmt.py index dd90900d5a21..f72424ccefb4 100644 --- a/qiskit/circuit/library/generalized_gates/mcmt.py +++ b/qiskit/circuit/library/generalized_gates/mcmt.py @@ -19,6 +19,8 @@ from qiskit import circuit from qiskit.circuit import ControlledGate, Gate, Qubit, QuantumRegister, QuantumCircuit from qiskit.exceptions import QiskitError + +# pylint: disable=cyclic-import from ..standard_gates import XGate, YGate, ZGate, HGate, TGate, TdgGate, SGate, SdgGate diff --git a/qiskit/extensions/quantum_initializer/uc.py b/qiskit/circuit/library/generalized_gates/uc.py similarity index 73% rename from qiskit/extensions/quantum_initializer/uc.py rename to qiskit/circuit/library/generalized_gates/uc.py index d859ed4a96de..276480496c50 100644 --- a/qiskit/extensions/quantum_initializer/uc.py +++ b/qiskit/circuit/library/generalized_gates/uc.py @@ -17,22 +17,9 @@ # pylint: disable=missing-param-doc # pylint: disable=missing-type-doc -""" -Uniformly controlled gates (also called multiplexed gates). +"""Uniformly controlled gates (also called multiplexed gates).""" -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state |i> (in the computational basis), -a single-qubit unitary U_i is applied to the target qubit. - -This gate is represented by a block-diagonal matrix, where each block is a -2x2 unitary: - - [[U_0, 0, ...., 0], - [0, U_1, ...., 0], - . - . - [0, 0, ...., U_(2^k-1)]] -""" +from __future__ import annotations import cmath import math @@ -46,28 +33,53 @@ from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.exceptions import CircuitError from qiskit.exceptions import QiskitError -from qiskit.quantum_info.synthesis import OneQubitEulerDecomposer + +# pylint: disable=cyclic-import +from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer + +from .diagonal import Diagonal _EPS = 1e-10 # global variable used to chop very small numbers to zero _DECOMPOSER1Q = OneQubitEulerDecomposer("U3") class UCGate(Gate): - """Uniformly controlled gate (also called multiplexed gate). - The decomposition is based on: https://arxiv.org/pdf/quant-ph/0410066.pdf. - """ + r"""Uniformly controlled gate (also called multiplexed gate). - def __init__(self, gate_list, up_to_diagonal=False): - """UCGate Gate initializer. + These gates can have several control qubits and a single target qubit. + If the k control qubits are in the state :math:`|i\rangle` (in the computational basis), + a single-qubit unitary :math:`U_i` is applied to the target qubit. - Args: - gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], - where each single-qubit unitary U_i is given as a 2*2 numpy array. + This gate is represented by a block-diagonal matrix, where each block is a + :math:`2\times 2` unitary, that is + + .. math:: + + \begin{pmatrix} + U_0 & 0 & \cdots & 0 \\ + 0 & U_1 & \cdots & 0 \\ + \vdots & & \ddots & \vdots \\ + 0 & 0 & \cdots & U_{2^{k-1}} + \end{pmatrix}. + + The decomposition is based on Ref. [1]. + + **References:** + + [1] Bergholm et al., Quantum circuits with uniformly controlled one-qubit gates (2005). + `Phys. Rev. A 71, 052330 `__. - up_to_diagonal (bool): determines if the gate is implemented up to a diagonal. + """ + + def __init__(self, gate_list: list[np.ndarray], up_to_diagonal: bool = False): + r""" + Args: + gate_list: List of two qubit unitaries :math:`[U_0, ..., U_{2^{k-1}}]`, where each + single-qubit unitary :math:`U_i` is given as a :math:`2 \times 2` numpy array. + up_to_diagonal: Determines if the gate is implemented up to a diagonal. or if it is decomposed completely (default: False). - If the UCGate u is decomposed up to a diagonal d, this means that the circuit - implements a unitary u' such that d.u'=u. + If the ``UCGate`` :math:`U` is decomposed up to a diagonal :math:`D`, this means + that the circuit implements a unitary :math:`U'` such that :math:`D U' = U`. Raises: QiskitError: in case of bad input to the constructor @@ -97,7 +109,7 @@ def __init__(self, gate_list, up_to_diagonal=False): super().__init__("multiplexer", int(num_contr) + 1, gate_list) self.up_to_diagonal = up_to_diagonal - def inverse(self): + def inverse(self) -> Gate: """Return the inverse. This does not re-compute the decomposition for the multiplexer with the inverse of the @@ -176,7 +188,8 @@ def _dec_ucg(self): # Important: the diagonal gate is given in the computational basis of the qubits # q[k-1],...,q[0],q_target (ordered with decreasing significance), # where q[i] are the control qubits and t denotes the target qubit. - circuit.diagonal(diag.tolist(), q) + diagonal = Diagonal(diag) + circuit.append(diagonal, q) return circuit, diag def _dec_ucg_help(self): @@ -289,68 +302,3 @@ def validate_parameter(self, parameter): return parameter else: raise CircuitError(f"invalid param type {type(parameter)} in gate {self.name}") - - -def uc(self, gate_list, q_controls, q_target, up_to_diagonal=False): - """Attach a uniformly controlled gates (also called multiplexed gates) to a circuit. - - The decomposition was introduced by Bergholm et al. in - https://arxiv.org/pdf/quant-ph/0410066.pdf. - - Args: - gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], - where each single-qubit unitary U_i is a given as a 2*2 array - q_controls (QuantumRegister|list[(QuantumRegister,int)]): list of k control qubits. - The qubits are ordered according to their significance in the computational basis. - For example if q_controls=[q[1],q[2]] (with q = QuantumRegister(2)), - the unitary U_0 is performed if q[1] and q[2] are in the state zero, U_1 is - performed if q[2] is in the state zero and q[1] is in the state one, and so on - q_target (QuantumRegister|(QuantumRegister,int)): target qubit, where we act on with - the single-qubit gates. - up_to_diagonal (bool): If set to True, the uniformly controlled gate is decomposed up - to a diagonal gate, i.e. a unitary u' is implemented such that there exists a - diagonal gate d with u = d.dot(u'), where the unitary u describes the uniformly - controlled gate - - Returns: - QuantumCircuit: the uniformly controlled gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError( - "The target qubit is a QuantumRegister containing more than one qubit." - ) - # Check if q_controls has type "list" - if not isinstance(q_controls, list): - raise QiskitError( - "The control qubits must be provided as a list" - " (also if there is only one control qubit)." - ) - # Check if gate_list has type "list" - if not isinstance(gate_list, list): - raise QiskitError("The single-qubit unitaries are not provided in a list.") - # Check if number of gates in gate_list is a positive power of two - num_contr = math.log2(len(gate_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError( - "The number of controlled single-qubit gates is not a non negative power of 2." - ) - # Check if number of control qubits does correspond to the number of single-qubit rotations - if num_contr != len(q_controls): - raise QiskitError( - "Number of controlled gates does not correspond to the number of control qubits." - ) - return self.append(UCGate(gate_list, up_to_diagonal), [q_target] + q_controls) - - -QuantumCircuit.uc = uc diff --git a/qiskit/extensions/quantum_initializer/uc_pauli_rot.py b/qiskit/circuit/library/generalized_gates/uc_pauli_rot.py similarity index 85% rename from qiskit/extensions/quantum_initializer/uc_pauli_rot.py rename to qiskit/circuit/library/generalized_gates/uc_pauli_rot.py index 87a1b1fd3642..8c1de96c413c 100644 --- a/qiskit/extensions/quantum_initializer/uc_pauli_rot.py +++ b/qiskit/circuit/library/generalized_gates/uc_pauli_rot.py @@ -13,39 +13,36 @@ # The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, # which was supervised by Raban Iten and Prof. Renato Renner. -""" -(Abstract) base class for uniformly controlled (also called multiplexed) single-qubit rotations R_t. -This class provides a basis for the decomposition of uniformly controlled R_x,R_y and R_z gates -(i.e., for t=x,y,z). These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_t(a_i) is applied to the target qubit for a (real) angle a_i. -""" +"""Uniformly controlled Pauli rotations.""" + +from __future__ import annotations import math import numpy as np -from qiskit.circuit import Gate, QuantumCircuit -from qiskit.circuit.quantumcircuit import QuantumRegister +from qiskit.circuit.gate import Gate +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumregister import QuantumRegister from qiskit.exceptions import QiskitError _EPS = 1e-10 # global variable used to chop very small numbers to zero class UCPauliRotGate(Gate): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on 'Synthesis of Quantum Logic Circuits' - by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf) + r"""Uniformly controlled Pauli rotations. - Input: - angle_list = list of (real) rotation angles [a_0,...,a_{2^k-1}]. Must have at least one entry. - - rot_axis = rotation axis for the single qubit rotations - (currently, 'X', 'Y' and 'Z' are supported) + Implements the :class:`.UCGate` for the special case that all unitaries are Pauli rotations, + :math:`U_i = R_P(a_i)` where :math:`P \in \{X, Y, Z\}` and :math:`a_i \in \mathbb{R}` is + the rotation angle. """ - def __init__(self, angle_list, rot_axis): + def __init__(self, angle_list: list[float], rot_axis: str) -> None: + r""" + Args: + angle_list: List of rotation angles :math:`[a_0, ..., a_{2^{k-1}}]`. + rot_axis: Rotation axis. Must be either of ``"X"``, ``"Y"`` or ``"Z"``. + """ self.rot_axes = rot_axis # Check if angle_list has type "list" if not isinstance(angle_list, list): diff --git a/qiskit/circuit/library/generalized_gates/ucrx.py b/qiskit/circuit/library/generalized_gates/ucrx.py new file mode 100644 index 000000000000..dff52f47e1c5 --- /dev/null +++ b/qiskit/circuit/library/generalized_gates/ucrx.py @@ -0,0 +1,32 @@ +# 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. + +"""Uniformly controlled Pauli-X rotations.""" + +from __future__ import annotations + +from .uc_pauli_rot import UCPauliRotGate + + +class UCRXGate(UCPauliRotGate): + r"""Uniformly controlled Pauli-X rotations. + + Implements the :class:`.UCGate` for the special case that all unitaries are Pauli-X rotations, + :math:`U_i = R_X(a_i)` where :math:`a_i \in \mathbb{R}` is the rotation angle. + """ + + def __init__(self, angle_list: list[float]) -> None: + r""" + Args: + angle_list: List of rotation angles :math:`[a_0, ..., a_{2^{k-1}}]`. + """ + super().__init__(angle_list, "X") diff --git a/qiskit/circuit/library/generalized_gates/ucry.py b/qiskit/circuit/library/generalized_gates/ucry.py new file mode 100644 index 000000000000..010a3893863b --- /dev/null +++ b/qiskit/circuit/library/generalized_gates/ucry.py @@ -0,0 +1,32 @@ +# 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. + +"""Uniformly controlled Pauli-Y rotations.""" + +from __future__ import annotations + +from .uc_pauli_rot import UCPauliRotGate + + +class UCRYGate(UCPauliRotGate): + r"""Uniformly controlled Pauli-Y rotations. + + Implements the :class:`.UCGate` for the special case that all unitaries are Pauli-Y rotations, + :math:`U_i = R_Y(a_i)` where :math:`a_i \in \mathbb{R}` is the rotation angle. + """ + + def __init__(self, angle_list: list[float]) -> None: + r""" + Args: + angle_list: List of rotation angles :math:`[a_0, ..., a_{2^{k-1}}]`. + """ + super().__init__(angle_list, "Y") diff --git a/qiskit/circuit/library/generalized_gates/ucrz.py b/qiskit/circuit/library/generalized_gates/ucrz.py new file mode 100644 index 000000000000..eb9f94b6b2c6 --- /dev/null +++ b/qiskit/circuit/library/generalized_gates/ucrz.py @@ -0,0 +1,32 @@ +# 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. + +"""Uniformly controlled Pauli-Z rotations.""" + +from __future__ import annotations + +from .uc_pauli_rot import UCPauliRotGate + + +class UCRZGate(UCPauliRotGate): + r"""Uniformly controlled Pauli-Z rotations. + + Implements the :class:`.UCGate` for the special case that all unitaries are Pauli-Z rotations, + :math:`U_i = R_Z(a_i)` where :math:`a_i \in \mathbb{R}` is the rotation angle. + """ + + def __init__(self, angle_list: list[float]): + r""" + Args: + angle_list: List of rotation angles :math:`[a_0, ..., a_{2^{k-1}}]`. + """ + super().__init__(angle_list, "Z") diff --git a/qiskit/extensions/unitary.py b/qiskit/circuit/library/generalized_gates/unitary.py similarity index 66% rename from qiskit/extensions/unitary.py rename to qiskit/circuit/library/generalized_gates/unitary.py index d486461986f5..fc9817cc08e5 100644 --- a/qiskit/extensions/unitary.py +++ b/qiskit/circuit/library/generalized_gates/unitary.py @@ -10,27 +10,34 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" -Arbitrary unitary circuit instruction. -""" +"""Arbitrary unitary circuit instruction.""" +from __future__ import annotations + +import typing import numpy -from qiskit.circuit import Gate, ControlledGate -from qiskit.circuit import QuantumCircuit -from qiskit.circuit import QuantumRegister, Qubit +from qiskit.circuit.gate import Gate +from qiskit.circuit.controlledgate import ControlledGate +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.exceptions import CircuitError from qiskit.circuit._utils import _compute_control_matrix -from qiskit.circuit.library.standard_gates import UGate -from qiskit.extensions.quantum_initializer import isometry +from qiskit.circuit.library.standard_gates.u import UGate from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.quantum_info.operators.predicates import is_unitary_matrix + +# pylint: disable=cyclic-import from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer from qiskit.quantum_info.synthesis.two_qubit_decompose import two_qubit_cnot_decompose -from qiskit.extensions.exceptions import ExtensionError + +from .isometry import Isometry _DECOMPOSER1Q = OneQubitEulerDecomposer("U") +if typing.TYPE_CHECKING: + from qiskit.quantum_info.operators.base_operator import BaseOperator + class UnitaryGate(Gate): """Class quantum gates specified by a unitary matrix. @@ -44,7 +51,7 @@ class UnitaryGate(Gate): .. code-block:: python from qiskit import QuantumCircuit - from qiskit.extensions import UnitaryGate + from qiskit.circuit.library import UnitaryGate matrix = [[0, 0, 0, 1], [0, 0, 1, 0], @@ -56,15 +63,14 @@ class UnitaryGate(Gate): circuit.append(gate, [0, 1]) """ - def __init__(self, data, label=None): - """Create a gate from a numeric unitary matrix. - + def __init__(self, data: numpy.ndarray | Gate | BaseOperator, label: str | None = None) -> None: + """ Args: - data (matrix or Operator): unitary operator. - label (str): unitary name for backend [Default: None]. + data: Unitary operator. + label: Unitary name for backend [Default: None]. Raises: - ExtensionError: if input data is not an N-qubit unitary operator. + ValueError: If input data is not an N-qubit unitary operator. """ if hasattr(data, "to_matrix"): # If input is Gate subclass or some other class object that has @@ -79,12 +85,12 @@ def __init__(self, data, label=None): data = numpy.array(data, dtype=complex) # Check input is unitary if not is_unitary_matrix(data): - raise ExtensionError("Input matrix is not unitary.") + raise ValueError("Input matrix is not unitary.") # Check input is N-qubit matrix input_dim, output_dim = data.shape num_qubits = int(numpy.log2(input_dim)) if input_dim != output_dim or 2**num_qubits != input_dim: - raise ExtensionError("Input matrix is not an N-qubit operator.") + raise ValueError("Input matrix is not an N-qubit operator.") # Store instruction params super().__init__("unitary", num_qubits, [data], label=label) @@ -137,25 +143,26 @@ def _define(self): self.definition = qs_decomposition(self.to_matrix()) - def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): - """Return controlled version of gate + def control( + self, + num_ctrl_qubits: int = 1, + label: int | None = None, + ctrl_state: int | str | None = None, + ) -> ControlledGate: + """Return controlled version of gate. Args: - num_ctrl_qubits (int): number of controls to add to gate (default=1) - label (str): optional gate label - ctrl_state (int or str or None): The control state in decimal or as a - bit string (e.g. '1011'). If None, use 2**num_ctrl_qubits-1. + num_ctrl_qubits: Number of controls to add to gate (default is 1). + label: Optional gate label. + ctrl_state: The control state in decimal or as a bit string (e.g. ``"1011"``). + If ``None``, use ``2**num_ctrl_qubits - 1``. Returns: - UnitaryGate: controlled version of gate. - - Raises: - QiskitError: Invalid ctrl_state. - ExtensionError: Non-unitary controlled unitary. + Controlled version of gate. """ mat = self.to_matrix() cmat = _compute_control_matrix(mat, num_ctrl_qubits, ctrl_state=None) - iso = isometry.Isometry(cmat, 0, 0) + iso = Isometry(cmat, 0, 0) return ControlledGate( "c-unitary", num_qubits=self.num_qubits + num_ctrl_qubits, @@ -180,45 +187,3 @@ def validate_parameter(self, parameter): return parameter else: raise CircuitError(f"invalid param type {type(parameter)} in gate {self.name}") - - -def unitary(self, obj, qubits, label=None): - """Apply unitary gate specified by ``obj`` to ``qubits``. - - Args: - obj (matrix or Operator): unitary operator. - qubits (Union[int, Tuple[int]]): The circuit qubits to apply the - transformation to. - label (str): unitary name for backend [Default: None]. - - Returns: - QuantumCircuit: The quantum circuit. - - Raises: - ExtensionError: if input data is not an N-qubit unitary operator. - - Example: - - Apply a gate specified by a unitary matrix to a quantum circuit - - .. code-block:: python - - from qiskit import QuantumCircuit - matrix = [[0, 0, 0, 1], - [0, 0, 1, 0], - [1, 0, 0, 0], - [0, 1, 0, 0]] - circuit = QuantumCircuit(2) - circuit.unitary(matrix, [0, 1]) - """ - gate = UnitaryGate(obj, label=label) - if isinstance(qubits, QuantumRegister): - qubits = qubits[:] - # for single qubit unitary gate, allow an 'int' or a 'list of ints' as qubits. - if gate.num_qubits == 1: - if isinstance(qubits, (int, Qubit)) or len(qubits) > 1: - qubits = [qubits] - return self.append(gate, qubits, []) - - -QuantumCircuit.unitary = unitary diff --git a/qiskit/extensions/hamiltonian_gate.py b/qiskit/circuit/library/hamiltonian_gate.py similarity index 60% rename from qiskit/extensions/hamiltonian_gate.py rename to qiskit/circuit/library/hamiltonian_gate.py index 54c9bb723532..8bca4b42c692 100644 --- a/qiskit/extensions/hamiltonian_gate.py +++ b/qiskit/circuit/library/hamiltonian_gate.py @@ -14,38 +14,49 @@ Gate described by the time evolution of a Hermitian Hamiltonian operator. """ +from __future__ import annotations +import typing + from numbers import Number -import numpy +import numpy as np -from qiskit.circuit import Gate, QuantumCircuit, QuantumRegister, ParameterExpression +from qiskit.circuit.gate import Gate +from qiskit.circuit.quantumcircuit import QuantumCircuit +from qiskit.circuit.quantumregister import QuantumRegister +from qiskit.circuit.parameterexpression import ParameterExpression +from qiskit.circuit.exceptions import CircuitError from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.quantum_info.operators.predicates import is_hermitian_matrix -from qiskit.extensions.exceptions import ExtensionError -from qiskit.circuit.exceptions import CircuitError from qiskit.utils.deprecation import deprecate_func -from .unitary import UnitaryGate +from .generalized_gates.unitary import UnitaryGate + +if typing.TYPE_CHECKING: + from qiskit.quantum_info.operators.base_operator import BaseOperator class HamiltonianGate(Gate): - """Class for representing evolution by a Hamiltonian operator as a gate. + r"""Class for representing evolution by a Hamiltonian operator as a gate. - This gate resolves to a :class:`.UnitaryGate` as :math:`U(t) = exp(-i t H)`, + This gate resolves to a :class:`~.library.UnitaryGate` as :math:`U(t) = \exp(-i t H)`, which can be decomposed into basis gates if it is 2 qubits or less, or - simulated directly in Aer for more qubits. Note that you can also directly - use :meth:`.QuantumCircuit.hamiltonian`. + simulated directly in Aer for more qubits. """ - def __init__(self, data, time, label=None): - """Create a gate from a hamiltonian operator and evolution time parameter t - + def __init__( + self, + data: np.ndarray | Gate | BaseOperator, + time: float | ParameterExpression, + label: str | None = None, + ) -> None: + """ Args: - data (matrix or Operator): a hermitian operator. - time (float or ParameterExpression): time evolution parameter. - label (str): unitary name for backend [Default: None]. + data: A hermitian operator. + time: Time evolution parameter. + label: Unitary name for backend [Default: None]. Raises: - ExtensionError: if input data is not an N-qubit unitary operator. + ValueError: if input data is not an N-qubit unitary operator. """ if hasattr(data, "to_matrix"): # If input is Gate subclass or some other class object that has @@ -56,18 +67,18 @@ def __init__(self, data, time, label=None): # the object to an Operator so that we can extract the underlying # numpy matrix from `Operator.data`. data = data.to_operator().data - # Convert to numpy array in case not already an array - data = numpy.array(data, dtype=complex) + # Convert to np array in case not already an array + data = np.array(data, dtype=complex) # Check input is unitary if not is_hermitian_matrix(data): - raise ExtensionError("Input matrix is not Hermitian.") - if isinstance(time, Number) and time != numpy.real(time): - raise ExtensionError("Evolution time is not real.") + raise ValueError("Input matrix is not Hermitian.") + if isinstance(time, Number) and time != np.real(time): + raise ValueError("Evolution time is not real.") # Check input is N-qubit matrix input_dim, output_dim = data.shape - num_qubits = int(numpy.log2(input_dim)) + num_qubits = int(np.log2(input_dim)) if input_dim != output_dim or 2**num_qubits != input_dim: - raise ExtensionError("Input matrix is not an N-qubit operator.") + raise ValueError("Input matrix is not an N-qubit operator.") # Store instruction params super().__init__("hamiltonian", num_qubits, [data, time], label=label) @@ -100,7 +111,7 @@ def inverse(self): def conjugate(self): """Return the conjugate of the Hamiltonian.""" - return HamiltonianGate(numpy.conj(self.params[0]), -self.params[1]) + return HamiltonianGate(np.conj(self.params[0]), -self.params[1]) def adjoint(self): """Return the adjoint of the unitary.""" @@ -108,7 +119,7 @@ def adjoint(self): def transpose(self): """Return the transpose of the Hamiltonian.""" - return HamiltonianGate(numpy.transpose(self.params[0]), self.params[1]) + return HamiltonianGate(np.transpose(self.params[0]), self.params[1]) def _define(self): """Calculate a subcircuit that implements this unitary.""" @@ -122,42 +133,13 @@ def _define(self): ) def qasm(self): """Raise an error, as QASM is not defined for the HamiltonianGate.""" - raise ExtensionError("HamiltonianGate has no QASM definition.") + raise CircuitError("HamiltonianGate has no QASM definition.") def validate_parameter(self, parameter): """Hamiltonian parameter has to be an ndarray, operator or float.""" - if isinstance(parameter, (float, int, numpy.ndarray)): + if isinstance(parameter, (float, int, np.ndarray)): return parameter elif isinstance(parameter, ParameterExpression) and len(parameter.parameters) == 0: return float(parameter) else: raise CircuitError(f"invalid param type {type(parameter)} for gate {self.name}") - - -def hamiltonian(self, operator, time, qubits, label=None): - """Apply hamiltonian evolution to qubits. - - This gate resolves to a :class:`.UnitaryGate` as :math:`U(t) = exp(-i t H)`, - which can be decomposed into basis gates if it is 2 qubits or less, or - simulated directly in Aer for more qubits. - - Args: - operator (matrix or Operator): a hermitian operator. - time (float or ParameterExpression): time evolution parameter. - qubits (Union[int, Tuple[int]]): The circuit qubits to apply the - transformation to. - label (str): unitary name for backend [Default: None]. - - Returns: - QuantumCircuit: The quantum circuit. - - Raises: - ExtensionError: if input data is not an N-qubit unitary operator. - """ - if not isinstance(qubits, list): - qubits = [qubits] - - return self.append(HamiltonianGate(data=operator, time=time, label=label), qubits, []) - - -QuantumCircuit.hamiltonian = hamiltonian diff --git a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py index ce7f7861440b..18d312f67e1a 100644 --- a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +++ b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py @@ -113,7 +113,7 @@ def _mcsu2_real_diagonal( """ # pylint: disable=cyclic-import from .x import MCXVChain - from qiskit.extensions import UnitaryGate + from qiskit.circuit.library.generalized_gates import UnitaryGate from qiskit.quantum_info.operators.predicates import is_unitary_matrix from qiskit.compiler import transpile diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 6e01f3f32572..b26c91e83d18 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -19,6 +19,7 @@ import multiprocessing as mp import warnings import typing +import math from collections import OrderedDict, defaultdict, namedtuple from typing import ( Union, @@ -64,6 +65,7 @@ if typing.TYPE_CHECKING: import qiskit # pylint: disable=cyclic-import from qiskit.transpiler.layout import TranspileLayout # pylint: disable=cyclic-import + from qiskit.quantum_info.operators.base_operator import BaseOperator BitLocations = namedtuple("BitLocations", ("index", "registers")) @@ -4120,6 +4122,669 @@ def pauli( return self.append(PauliGate(pauli_string), qubits, []) + def initialize( + self, + params: Sequence[complex] | str | int, + qubits: Sequence[QubitSpecifier] | None = None, + normalize: bool = False, + ): + r"""Initialize qubits in a specific state. + + Qubit initialization is done by first resetting the qubits to :math:`|0\rangle` + followed by calling :class:`qiskit.extensions.StatePreparation` + class to prepare the qubits in a specified state. + Both these steps are included in the + :class:`qiskit.extensions.Initialize` instruction. + + Args: + params: The state to initialize to, can be either of the following. + + * Statevector or vector of complex amplitudes to initialize to. + * Labels of basis states of the Pauli eigenstates Z, X, Y. See + :meth:`.Statevector.from_label`. Notice the order of the labels is reversed with + respect to the qubit index to be applied to. Example label '01' initializes the + qubit zero to :math:`|1\rangle` and the qubit one to :math:`|0\rangle`. + * An integer that is used as a bitmap indicating which qubits to initialize to + :math:`|1\rangle`. Example: setting params to 5 would initialize qubit 0 and qubit + 2 to :math:`|1\rangle` and qubit 1 to :math:`|0\rangle`. + + qubits: Qubits to initialize. If ``None`` the initialization is applied to all qubits in + the circuit. + normalize: Whether to normalize an input array to a unit vector. + + Returns: + A handle to the instructions created. + + Examples: + Prepare a qubit in the state :math:`(|0\rangle - |1\rangle) / \sqrt{2}`. + + .. code-block:: + + import numpy as np + from qiskit import QuantumCircuit + + circuit = QuantumCircuit(1) + circuit.initialize([1/np.sqrt(2), -1/np.sqrt(2)], 0) + circuit.draw() + + output: + + .. parsed-literal:: + + ┌──────────────────────────────┐ + q_0: ┤ Initialize(0.70711,-0.70711) ├ + └──────────────────────────────┘ + + + Initialize from a string two qubits in the state :math:`|10\rangle`. + The order of the labels is reversed with respect to qubit index. + More information about labels for basis states are in + :meth:`.Statevector.from_label`. + + .. code-block:: + + import numpy as np + from qiskit import QuantumCircuit + + circuit = QuantumCircuit(2) + circuit.initialize('01', circuit.qubits) + circuit.draw() + + output: + + .. parsed-literal:: + + ┌──────────────────┐ + q_0: ┤0 ├ + │ Initialize(0,1) │ + q_1: ┤1 ├ + └──────────────────┘ + + Initialize two qubits from an array of complex amplitudes. + + .. code-block:: + + import numpy as np + from qiskit import QuantumCircuit + + circuit = QuantumCircuit(2) + circuit.initialize([0, 1/np.sqrt(2), -1.j/np.sqrt(2), 0], circuit.qubits) + circuit.draw() + + output: + + .. parsed-literal:: + + ┌────────────────────────────────────┐ + q_0: ┤0 ├ + │ Initialize(0,0.70711,-0.70711j,0) │ + q_1: ┤1 ├ + └────────────────────────────────────┘ + """ + # pylint: disable=cyclic-import + from .library.data_preparation.initializer import Initialize + + if qubits is None: + qubits = self.qubits + elif isinstance(qubits, (int, np.integer, slice, Qubit)): + qubits = [qubits] + + num_qubits = len(qubits) if isinstance(params, int) else None + + return self.append(Initialize(params, num_qubits, normalize), qubits) + + def unitary( + self, + obj: np.ndarray | Gate | BaseOperator, + qubits: Sequence[QubitSpecifier], + label: str | None = None, + ): + """Apply unitary gate specified by ``obj`` to ``qubits``. + + Args: + obj: Unitary operator. + qubits: The circuit qubits to apply the transformation to. + label: Unitary name for backend [Default: None]. + + Returns: + QuantumCircuit: The quantum circuit. + + Example: + + Apply a gate specified by a unitary matrix to a quantum circuit + + .. code-block:: python + + from qiskit import QuantumCircuit + matrix = [[0, 0, 0, 1], + [0, 0, 1, 0], + [1, 0, 0, 0], + [0, 1, 0, 0]] + circuit = QuantumCircuit(2) + circuit.unitary(matrix, [0, 1]) + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.unitary import UnitaryGate + + gate = UnitaryGate(obj, label=label) + + # correctly treat as single-qubit gate if it only acts as 1 qubit, i.e. + # allow a single qubit specifier and enable broadcasting + if gate.num_qubits == 1: + if isinstance(qubits, (int, Qubit)) or len(qubits) > 1: + qubits = [qubits] + + return self.append(gate, qubits, []) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, compose the circuit with a qiskit.circuit.library.Diagonal circuit.", + pending=True, + ) + def diagonal(self, diag, qubit): + """Attach a diagonal gate to a circuit. + + The decomposition is based on Theorem 7 given in "Synthesis of Quantum Logic Circuits" by + Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). + + Args: + diag (list): list of the 2^k diagonal entries (for a diagonal gate on k qubits). + Must contain at least two entries + qubit (QuantumRegister | list): list of k qubits the diagonal is + acting on (the order of the qubits specifies the computational basis in which the + diagonal gate is provided: the first element in diag acts on the state where all + the qubits in q are in the state 0, the second entry acts on the state where all + the qubits q[1],...,q[k-1] are in the state zero and q[0] is in the state 1, + and so on) + + Returns: + QuantumCircuit: the diagonal gate which was attached to the circuit. + + Raises: + QiskitError: if the list of the diagonal entries or the qubit list is in bad format; + if the number of diagonal entries is not 2^k, where k denotes the number of qubits + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.diagonal import DiagonalGate + + if isinstance(qubit, QuantumRegister): + qubit = qubit[:] + # Check if q has type "list" + if not isinstance(qubit, list): + raise QiskitError( + "The qubits must be provided as a list (also if there is only one qubit)." + ) + # Check if diag has type "list" + if not isinstance(diag, list): + raise QiskitError("The diagonal entries are not provided in a list.") + num_action_qubits = math.log2(len(diag)) + if not len(qubit) == num_action_qubits: + raise QiskitError( + "The number of diagonal entries does not correspond to the number of qubits." + ) + + return self.append(DiagonalGate(diag), qubit) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, append a qiskit.circuit.library.Isometry to the circuit.", + pending=True, + ) + def iso( + self, + isometry, + q_input, + q_ancillas_for_output, + q_ancillas_zero=None, + q_ancillas_dirty=None, + epsilon=1e-10, + ): + """ + Attach an arbitrary isometry from m to n qubits to a circuit. In particular, + this allows to attach arbitrary unitaries on n qubits (m=n) or to prepare any state + on n qubits (m=0). + The decomposition used here was introduced by Iten et al. in https://arxiv.org/abs/1501.06911. + + Args: + isometry (ndarray): an isometry from m to n qubits, i.e., a (complex) ndarray of + dimension 2^n×2^m with orthonormal columns (given in the computational basis + specified by the order of the ancillas and the input qubits, where the ancillas + are considered to be more significant than the input qubits.). + q_input (QuantumRegister | list[Qubit]): list of m qubits where the input + to the isometry is fed in (empty list for state preparation). + q_ancillas_for_output (QuantumRegister | list[Qubit]): list of n-m ancilla + qubits that are used for the output of the isometry and which are assumed to start + in the zero state. The qubits are listed with increasing significance. + q_ancillas_zero (QuantumRegister | list[Qubit]): list of ancilla qubits + which are assumed to start in the zero state. Default is q_ancillas_zero = None. + q_ancillas_dirty (QuantumRegister | list[Qubit]): list of ancilla qubits + which can start in an arbitrary state. Default is q_ancillas_dirty = None. + epsilon (float): error tolerance of calculations. + Default is epsilon = _EPS. + + Returns: + QuantumCircuit: the isometry is attached to the quantum circuit. + + Raises: + QiskitError: if the array is not an isometry of the correct size corresponding to + the provided number of qubits. + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.isometry import Isometry + + if q_input is None: + q_input = [] + if q_ancillas_for_output is None: + q_ancillas_for_output = [] + if q_ancillas_zero is None: + q_ancillas_zero = [] + if q_ancillas_dirty is None: + q_ancillas_dirty = [] + + if isinstance(q_input, QuantumRegister): + q_input = q_input[:] + if isinstance(q_ancillas_for_output, QuantumRegister): + q_ancillas_for_output = q_ancillas_for_output[:] + if isinstance(q_ancillas_zero, QuantumRegister): + q_ancillas_zero = q_ancillas_zero[:] + if isinstance(q_ancillas_dirty, QuantumRegister): + q_ancillas_dirty = q_ancillas_dirty[:] + + return self.append( + Isometry(isometry, len(q_ancillas_zero), len(q_ancillas_dirty), epsilon=epsilon), + q_input + q_ancillas_for_output + q_ancillas_zero + q_ancillas_dirty, + ) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, append a qiskit.circuit.library.HamiltonianGate to the circuit.", + pending=True, + ) + def hamiltonian(self, operator, time, qubits, label=None): + """Apply hamiltonian evolution to qubits. + + This gate resolves to a :class:`~.library.UnitaryGate` as :math:`U(t) = exp(-i t H)`, + which can be decomposed into basis gates if it is 2 qubits or less, or + simulated directly in Aer for more qubits. + + Args: + operator (matrix or Operator): a hermitian operator. + time (float or ParameterExpression): time evolution parameter. + qubits (Union[int, Tuple[int]]): The circuit qubits to apply the + transformation to. + label (str): unitary name for backend [Default: None]. + + Returns: + QuantumCircuit: The quantum circuit. + """ + # pylint: disable=cyclic-import + from .library.hamiltonian_gate import HamiltonianGate + + if not isinstance(qubits, list): + qubits = [qubits] + + return self.append(HamiltonianGate(data=operator, time=time, label=label), qubits, []) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, append a qiskit.circuit.library.UCGate to the circuit.", + pending=True, + ) + def uc(self, gate_list, q_controls, q_target, up_to_diagonal=False): + """Attach a uniformly controlled gates (also called multiplexed gates) to a circuit. + + The decomposition was introduced by Bergholm et al. in + https://arxiv.org/pdf/quant-ph/0410066.pdf. + + Args: + gate_list (list[ndarray]): list of two qubit unitaries [U_0,...,U_{2^k-1}], + where each single-qubit unitary U_i is a given as a 2*2 array + q_controls (QuantumRegister | list[(QuantumRegister,int)]): list of k control qubits. + The qubits are ordered according to their significance in the computational basis. + For example if q_controls=[q[1],q[2]] (with q = QuantumRegister(2)), + the unitary U_0 is performed if q[1] and q[2] are in the state zero, U_1 is + performed if q[2] is in the state zero and q[1] is in the state one, and so on + q_target (QuantumRegister | tuple(QuantumRegister, int)): target qubit, where we act on with + the single-qubit gates. + up_to_diagonal (bool): If set to True, the uniformly controlled gate is decomposed up + to a diagonal gate, i.e. a unitary u' is implemented such that there exists a + diagonal gate d with u = d.dot(u'), where the unitary u describes the uniformly + controlled gate + + Returns: + QuantumCircuit: the uniformly controlled gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.uc import UCGate + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError( + "The target qubit is a QuantumRegister containing more than one qubit." + ) + # Check if q_controls has type "list" + if not isinstance(q_controls, list): + raise QiskitError( + "The control qubits must be provided as a list" + " (also if there is only one control qubit)." + ) + # Check if gate_list has type "list" + if not isinstance(gate_list, list): + raise QiskitError("The single-qubit unitaries are not provided in a list.") + # Check if number of gates in gate_list is a positive power of two + num_contr = math.log2(len(gate_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError( + "The number of controlled single-qubit gates is not a non negative power of 2." + ) + # Check if number of control qubits does correspond to the number of single-qubit rotations + if num_contr != len(q_controls): + raise QiskitError( + "Number of controlled gates does not correspond to the number of control qubits." + ) + return self.append(UCGate(gate_list, up_to_diagonal), [q_target] + q_controls) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, append a qiskit.circuit.library.UCRXGate to the circuit.", + pending=True, + ) + def ucrx( + self, + angle_list: list[float], + q_controls: Sequence[QubitSpecifier], + q_target: QubitSpecifier, + ): + r"""Attach a uniformly controlled (also called multiplexed) Rx rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[float]): list of (real) rotation angles :math:`[a_0,...,a_{2^k-1}]` + q_controls (Sequence[QubitSpecifier]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if ``q_controls=[q[0],q[1]]`` + (with ``q = QuantumRegister(2)``), the rotation ``Rx(a_0)`` is performed if ``q[0]`` + and ``q[1]`` are in the state zero, the rotation ``Rx(a_1)`` is performed if ``q[0]`` + is in the state one and ``q[1]`` is in the state zero, and so on + q_target (QubitSpecifier): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.ucrx import UCRXGate + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError( + "The target qubit is a QuantumRegister containing more than one qubit." + ) + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError( + "The number of controlled rotation gates is not a non-negative power of 2." + ) + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError( + "Number of controlled rotations does not correspond to the number of control-qubits." + ) + return self.append(UCRXGate(angle_list), [q_target] + q_controls, []) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, append a qiskit.circuit.library.UCRYGate to the circuit.", + pending=True, + ) + def ucry( + self, + angle_list: list[float], + q_controls: Sequence[QubitSpecifier], + q_target: QubitSpecifier, + ): + r"""Attach a uniformly controlled (also called multiplexed) Ry rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[float]): list of (real) rotation angles :math:`[a_0,...,a_{2^k-1}]` + q_controls (Sequence[QubitSpecifier]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if ``q_controls=[q[0],q[1]]`` + (with ``q = QuantumRegister(2)``), the rotation ``Ry(a_0)`` is performed if ``q[0]`` + and ``q[1]`` are in the state zero, the rotation ``Ry(a_1)`` is performed if ``q[0]`` + is in the state one and ``q[1]`` is in the state zero, and so on + q_target (QubitSpecifier): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.ucry import UCRYGate + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError( + "The target qubit is a QuantumRegister containing more than one qubit." + ) + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError( + "The number of controlled rotation gates is not a non-negative power of 2." + ) + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError( + "Number of controlled rotations does not correspond to the number of control-qubits." + ) + return self.append(UCRYGate(angle_list), [q_target] + q_controls, []) + + @deprecate_func( + since="0.45.0", + additional_msg="Instead, append a qiskit.circuit.library.UCRZGate to the circuit.", + pending=True, + ) + def ucrz( + self, + angle_list: list[float], + q_controls: Sequence[QubitSpecifier], + q_target: QubitSpecifier, + ): + r"""Attach a uniformly controlled (also called multiplexed) Rz rotation gate to a circuit. + + The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. + + Args: + angle_list (list[float]): list of (real) rotation angles :math:`[a_0,...,a_{2^k-1}]` + q_controls (Sequence[QubitSpecifier]): list of k control qubits + (or empty list if no controls). The control qubits are ordered according to their + significance in increasing order: For example if ``q_controls=[q[0],q[1]]`` + (with ``q = QuantumRegister(2)``), the rotation ``Rz(a_0)`` is performed if ``q[0]`` + and ``q[1]`` are in the state zero, the rotation ``Rz(a_1)`` is performed if ``q[0]`` + is in the state one and ``q[1]`` is in the state zero, and so on + q_target (QubitSpecifier): target qubit, where we act on with + the single-qubit rotation gates + + Returns: + QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. + + Raises: + QiskitError: if the list number of control qubits does not correspond to the provided + number of single-qubit unitaries; if an input is of the wrong type + """ + # pylint: disable=cyclic-import + from .library.generalized_gates.ucrz import UCRZGate + + if isinstance(q_controls, QuantumRegister): + q_controls = q_controls[:] + if isinstance(q_target, QuantumRegister): + q_target = q_target[:] + if len(q_target) == 1: + q_target = q_target[0] + else: + raise QiskitError( + "The target qubit is a QuantumRegister containing more than one qubit." + ) + # Check if q_controls has type "list" + if not isinstance(angle_list, list): + raise QiskitError("The angles must be provided as a list.") + num_contr = math.log2(len(angle_list)) + if num_contr < 0 or not num_contr.is_integer(): + raise QiskitError( + "The number of controlled rotation gates is not a non-negative power of 2." + ) + # Check if number of control qubits does correspond to the number of rotations + if num_contr != len(q_controls): + raise QiskitError( + "Number of controlled rotations does not correspond to the number of control-qubits." + ) + return self.append(UCRZGate(angle_list), [q_target] + q_controls, []) + + @deprecate_func( + since="0.45.0", additional_msg="Instead, use the QuantumCircuit.unitary method." + ) + def squ( + self, + unitary_matrix, + qubit, + mode="ZYZ", + up_to_diagonal=False, + ): + """Decompose an arbitrary 2*2 unitary into three rotation gates. + + Note that the decomposition is up to a global phase shift. + (This is a well known decomposition which can be found for example in Nielsen and Chuang's book + "Quantum computation and quantum information".) + + Args: + unitary_matrix (ndarray): 2*2 unitary (given as a (complex) ndarray). + qubit (QuantumRegister or Qubit): The qubit which the gate is acting on. + mode (string): determines the used decomposition by providing the rotation axes. + The allowed modes are: "ZYZ" (default) + up_to_diagonal (bool): if set to True, the single-qubit unitary is decomposed up to + a diagonal matrix, i.e. a unitary u' is implemented such that there exists a 2*2 + diagonal gate d with u = d.dot(u') + + Returns: + InstructionSet: The single-qubit unitary instruction attached to the circuit. + + Raises: + QiskitError: if the format is wrong; if the array u is not unitary + """ + # pylint: disable=cyclic-import + from qiskit.extensions.quantum_initializer.squ import SingleQubitUnitary + + if isinstance(qubit, QuantumRegister): + qubit = qubit[:] + if len(qubit) == 1: + qubit = qubit[0] + else: + raise QiskitError( + "The target qubit is a QuantumRegister containing more than one qubit." + ) + # Check if there is one target qubit provided + if not isinstance(qubit, Qubit): + raise QiskitError("The target qubit is not a single qubit from a QuantumRegister.") + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + squ = SingleQubitUnitary(unitary_matrix, mode, up_to_diagonal) + + return self.append(squ, [qubit], []) + + @deprecate_func( + since="0.45.0", + additional_msg="The Snapshot instruction has been superseded by Qiskit Aer's save " + "instructions, see " + "https://qiskit.org/ecosystem/aer/apidocs/aer_library.html#saving-simulator-data.", + ) + def snapshot(self, label, snapshot_type="statevector", qubits=None, params=None): + """Take a statevector snapshot of the internal simulator representation. + Works on all qubits, and prevents reordering (like barrier). + + For other types of snapshots use the Snapshot extension directly. + + Args: + label (str): a snapshot label to report the result. + snapshot_type (str): the type of the snapshot. + qubits (list or None): the qubits to apply snapshot to [Default: None]. + params (list or None): the parameters for snapshot_type [Default: None]. + + Returns: + QuantumCircuit: with attached command + + Raises: + ExtensionError: malformed command + """ + # pylint: disable-cyclic-import + from qiskit.extensions.simulator.snapshot import Snapshot + from qiskit.extensions.exceptions import ExtensionError + + # If no qubits are specified we add all qubits so it acts as a barrier + # This is needed for full register snapshots like statevector + if isinstance(qubits, QuantumRegister): + qubits = qubits[:] + if not qubits: + tuples = [] + if isinstance(self, QuantumCircuit): + for register in self.qregs: + tuples.append(register) + if not tuples: + raise ExtensionError("no qubits for snapshot") + qubits = [] + for tuple_element in tuples: + if isinstance(tuple_element, QuantumRegister): + for j in range(tuple_element.size): + qubits.append(tuple_element[j]) + else: + qubits.append(tuple_element) + + # catch deprecation warning from instantiating the Snapshot instruction, + # as a warning is already triggered from this method + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + snap = Snapshot( + label, snapshot_type=snapshot_type, num_qubits=len(qubits), params=params + ) + + return self.append(snap, qubits) + def _push_scope( self, qubits: Iterable[Qubit] = (), @@ -4880,6 +5545,10 @@ def qubit_stop_time(self, *qubits: Union[Qubit, int]) -> float: return 0 # If there are no instructions over bits +# isometry is an alias for iso +QuantumCircuit.isometry = QuantumCircuit.iso + + class _ParameterBindsDict: __slots__ = ("mapping", "allowed_keys") diff --git a/qiskit/extensions/__init__.py b/qiskit/extensions/__init__.py index dc87d869cd8a..d0685f0a0d03 100644 --- a/qiskit/extensions/__init__.py +++ b/qiskit/extensions/__init__.py @@ -23,8 +23,6 @@ .. autosummary:: :toctree: ../stubs/ - UnitaryGate - HamiltonianGate SingleQubitUnitary Simulator Extensions @@ -35,25 +33,6 @@ Snapshot -Initialization -============== - -.. autosummary:: - :toctree: ../stubs/ - - Initialize - -Uniformly Controlled Rotations -============================== - -.. autosummary:: - :toctree: ../stubs - - UCPauliRotGate - UCRXGate - UCRYGate - UCRZGate - Exceptions ========== @@ -63,8 +42,12 @@ .. autoexception:: ExtensionError """ +import warnings + # import all standard gates from qiskit.circuit.library.standard_gates import * +from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate +from qiskit.circuit.library.generalized_gates import UnitaryGate from qiskit.circuit.barrier import Barrier from .exceptions import ExtensionError @@ -76,6 +59,12 @@ UCRYGate, UCRZGate, ) -from .unitary import UnitaryGate -from .hamiltonian_gate import HamiltonianGate from .simulator import Snapshot + + +warnings.warn( + "The qiskit.extensions module is pending deprecation since Qiskit 0.45.0. It will be deprecated " + "in a following release, no sooner than 3 months after the 0.45.0 release.", + stacklevel=2, + category=PendingDeprecationWarning, +) diff --git a/qiskit/extensions/exceptions.py b/qiskit/extensions/exceptions.py index b125dfd178b8..7a3f052e19f1 100644 --- a/qiskit/extensions/exceptions.py +++ b/qiskit/extensions/exceptions.py @@ -14,11 +14,13 @@ Exception for errors raised by extensions module. """ from qiskit.exceptions import QiskitError +from qiskit.utils.deprecation import deprecate_func class ExtensionError(QiskitError): """Base class for errors raised by extensions module.""" + @deprecate_func(since="0.45.0", pending=True) def __init__(self, *message): """Set the error message.""" super().__init__(*message) diff --git a/qiskit/extensions/quantum_initializer/__init__.py b/qiskit/extensions/quantum_initializer/__init__.py index accfd7e8ae7a..27f24443bd02 100644 --- a/qiskit/extensions/quantum_initializer/__init__.py +++ b/qiskit/extensions/quantum_initializer/__init__.py @@ -12,12 +12,15 @@ """Initialize qubit registers to desired arbitrary state.""" +from qiskit.circuit.library import ( + UCPauliRotGate, + UCRZGate, + UCRYGate, + UCRXGate, + DiagonalGate, + UCGate, + Isometry, + Initialize, +) + from .squ import SingleQubitUnitary -from .uc_pauli_rot import UCPauliRotGate -from .ucrz import UCRZGate -from .ucry import UCRYGate -from .ucrx import UCRXGate -from .diagonal import DiagonalGate -from .uc import UCGate -from .isometry import Isometry -from .initializer import Initialize diff --git a/qiskit/extensions/quantum_initializer/diagonal.py b/qiskit/extensions/quantum_initializer/diagonal.py deleted file mode 100644 index b4c24fc41b61..000000000000 --- a/qiskit/extensions/quantum_initializer/diagonal.py +++ /dev/null @@ -1,157 +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. - -# The structure of the code is based on Emanuel Malvetti's semester thesis at ETH in 2018, -# which was supervised by Raban Iten and Prof. Renato Renner. - -# pylint: disable=missing-param-doc -# pylint: disable=missing-type-doc - -""" -Decomposes a diagonal matrix into elementary gates using the method described in Theorem 7 in -"Synthesis of Quantum Logic Circuits" by Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). -""" -import cmath -import math - -import numpy as np - -from qiskit.circuit import Gate -from qiskit.circuit.quantumcircuit import QuantumCircuit, QuantumRegister -from qiskit.exceptions import QiskitError - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class DiagonalGate(Gate): - """ - diag = list of the 2^k diagonal entries (for a diagonal gate on k qubits). Must contain at - least two entries. - """ - - def __init__(self, diag): - """Check types""" - # Check if diag has type "list" - if not isinstance(diag, list): - raise QiskitError("The diagonal entries are not provided in a list.") - # Check if the right number of diagonal entries is provided and if the diagonal entries - # have absolute value one. - num_action_qubits = math.log2(len(diag)) - if num_action_qubits < 1 or not num_action_qubits.is_integer(): - raise QiskitError("The number of diagonal entries is not a positive power of 2.") - for z in diag: - try: - complex(z) - except TypeError as ex: - raise QiskitError( - "Not all of the diagonal entries can be converted to complex numbers." - ) from ex - if not np.abs(np.abs(z) - 1) < _EPS: - raise QiskitError("A diagonal entry has not absolute value one.") - # Create new gate. - super().__init__("diagonal", int(num_action_qubits), diag) - - def _define(self): - diag_circuit = self._dec_diag() - gate = diag_circuit.to_instruction() - q = QuantumRegister(self.num_qubits) - diag_circuit = QuantumCircuit(q) - diag_circuit.append(gate, q[:]) - self.definition = diag_circuit - - def validate_parameter(self, parameter): - """Diagonal Gate parameter should accept complex - (in addition to the Gate parameter types) and always return build-in complex.""" - if isinstance(parameter, complex): - return complex(parameter) - else: - return complex(super().validate_parameter(parameter)) - - def inverse(self): - """Return the inverse of the diagonal gate.""" - return DiagonalGate([np.conj(entry) for entry in self.params]) - - def _dec_diag(self): - """ - Call to create a circuit implementing the diagonal gate. - """ - q = QuantumRegister(self.num_qubits) - circuit = QuantumCircuit(q) - # Since the diagonal is a unitary, all its entries have absolute value one and the diagonal - # is fully specified by the phases of its entries - diag_phases = [cmath.phase(z) for z in self.params] - n = len(self.params) - while n >= 2: - angles_rz = [] - for i in range(0, n, 2): - diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) - angles_rz.append(rz_angle) - num_act_qubits = int(np.log2(n)) - contr_qubits = q[self.num_qubits - num_act_qubits + 1 : self.num_qubits] - target_qubit = q[self.num_qubits - num_act_qubits] - circuit.ucrz(angles_rz, contr_qubits, target_qubit) - n //= 2 - circuit.global_phase += diag_phases[0] - return circuit - - -# extract a Rz rotation (angle given by first output) such that exp(j*phase)*Rz(z_angle) -# is equal to the diagonal matrix with entires exp(1j*ph1) and exp(1j*ph2) -def _extract_rz(phi1, phi2): - phase = (phi1 + phi2) / 2.0 - z_angle = phi2 - phi1 - return phase, z_angle - - -def diagonal(self, diag, qubit): - """Attach a diagonal gate to a circuit. - - The decomposition is based on Theorem 7 given in "Synthesis of Quantum Logic Circuits" by - Shende et al. (https://arxiv.org/pdf/quant-ph/0406176.pdf). - - Args: - diag (list): list of the 2^k diagonal entries (for a diagonal gate on k qubits). - Must contain at least two entries - qubit (QuantumRegister|list): list of k qubits the diagonal is - acting on (the order of the qubits specifies the computational basis in which the - diagonal gate is provided: the first element in diag acts on the state where all - the qubits in q are in the state 0, the second entry acts on the state where all - the qubits q[1],...,q[k-1] are in the state zero and q[0] is in the state 1, - and so on) - - Returns: - QuantumCircuit: the diagonal gate which was attached to the circuit. - - Raises: - QiskitError: if the list of the diagonal entries or the qubit list is in bad format; - if the number of diagonal entries is not 2^k, where k denotes the number of qubits - """ - - if isinstance(qubit, QuantumRegister): - qubit = qubit[:] - # Check if q has type "list" - if not isinstance(qubit, list): - raise QiskitError( - "The qubits must be provided as a list (also if there is only one qubit)." - ) - # Check if diag has type "list" - if not isinstance(diag, list): - raise QiskitError("The diagonal entries are not provided in a list.") - num_action_qubits = math.log2(len(diag)) - if not len(qubit) == num_action_qubits: - raise QiskitError( - "The number of diagonal entries does not correspond to the number of qubits." - ) - return self.append(DiagonalGate(diag), qubit) - - -QuantumCircuit.diagonal = diagonal diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py deleted file mode 100644 index 33f6c2314dd7..000000000000 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ /dev/null @@ -1,197 +0,0 @@ -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017. -# -# 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. - -""" -Initialize qubit registers to desired arbitrary state. -""" -import numpy as np - -from qiskit.circuit import QuantumCircuit -from qiskit.circuit import QuantumRegister -from qiskit.circuit import Instruction -from qiskit.circuit import Qubit -from qiskit.circuit.library.data_preparation import StatePreparation - -_EPS = 1e-10 # global variable used to chop very small numbers to zero - - -class Initialize(Instruction): - """Complex amplitude initialization. - - Class that initializes some flexible collection of qubit registers, implemented by calling - the :class:`qiskit.extensions.StatePreparation` Class. - Note that Initialize is an Instruction and not a Gate since it contains a reset instruction, - which is not unitary. - """ - - def __init__(self, params, num_qubits=None, normalize=False): - r"""Create new initialize composite. - - Args: - params (str, list, int or Statevector): - * Statevector: Statevector to initialize to. - * list: vector of complex amplitudes to initialize to. - * string: labels of basis states of the Pauli eigenstates Z, X, Y. See - :meth:`.Statevector.from_label`. - Notice the order of the labels is reversed with respect to the qubit index to - be applied to. Example label '01' initializes the qubit zero to :math:`|1\rangle` - and the qubit one to :math:`|0\rangle`. - * int: an integer that is used as a bitmap indicating which qubits to initialize - to :math:`|1\rangle`. Example: setting params to 5 would initialize qubit 0 and qubit 2 - to :math:`|1\rangle` and qubit 1 to :math:`|0\rangle`. - - num_qubits (int): This parameter is only used if params is an int. Indicates the total - number of qubits in the `initialize` call. Example: `initialize` covers 5 qubits - and params is 3. This allows qubits 0 and 1 to be initialized to :math:`|1\rangle` - and the remaining 3 qubits to be initialized to :math:`|0\rangle`. - normalize (bool): Whether to normalize an input array to a unit vector. - """ - self._stateprep = StatePreparation(params, num_qubits, normalize=normalize) - - super().__init__("initialize", self._stateprep.num_qubits, 0, self._stateprep.params) - - def _define(self): - q = QuantumRegister(self.num_qubits, "q") - initialize_circuit = QuantumCircuit(q, name="init_def") - initialize_circuit.reset(q) - initialize_circuit.append(self._stateprep, q) - self.definition = initialize_circuit - - def gates_to_uncompute(self): - """Call to create a circuit with gates that take the desired vector to zero. - - Returns: - QuantumCircuit: circuit to take self.params vector to :math:`|{00\\ldots0}\\rangle` - """ - return self._stateprep._gates_to_uncompute() - - @property - def params(self): - """Return initialize params.""" - return self._stateprep.params - - @params.setter - def params(self, parameters): - """Set initialize params.""" - self._stateprep.params = parameters - - def broadcast_arguments(self, qargs, cargs): - return self._stateprep.broadcast_arguments(qargs, cargs) - - -def initialize(self, params, qubits=None, normalize=False): - r"""Initialize qubits in a specific state. - - Qubit initialization is done by first resetting the qubits to :math:`|0\rangle` - followed by calling :class:`qiskit.extensions.StatePreparation` - class to prepare the qubits in a specified state. - Both these steps are included in the - :class:`qiskit.extensions.Initialize` instruction. - - Args: - params (str or list or int): - * str: labels of basis states of the Pauli eigenstates Z, X, Y. See - :meth:`.Statevector.from_label`. Notice the order of the labels is reversed with respect - to the qubit index to be applied to. Example label '01' initializes the qubit zero to - :math:`|1\rangle` and the qubit one to :math:`|0\rangle`. - * list: vector of complex amplitudes to initialize to. - * int: an integer that is used as a bitmap indicating which qubits to initialize - to :math:`|1\rangle`. Example: setting params to 5 would initialize qubit 0 and qubit 2 - to :math:`|1\rangle` and qubit 1 to :math:`|0\rangle`. - - qubits (QuantumRegister or Qubit or int): - * QuantumRegister: A list of qubits to be initialized [Default: None]. - * Qubit: Single qubit to be initialized [Default: None]. - * int: Index of qubit to be initialized [Default: None]. - * list: Indexes of qubits to be initialized [Default: None]. - - normalize (bool): whether to normalize an input array to a unit vector. - - Returns: - qiskit.circuit.Instruction: a handle to the instruction that was just initialized - - Examples: - Prepare a qubit in the state :math:`(|0\rangle - |1\rangle) / \sqrt{2}`. - - .. code-block:: - - import numpy as np - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(1) - circuit.initialize([1/np.sqrt(2), -1/np.sqrt(2)], 0) - circuit.draw() - - output: - - .. parsed-literal:: - - ┌──────────────────────────────┐ - q_0: ┤ Initialize(0.70711,-0.70711) ├ - └──────────────────────────────┘ - - - Initialize from a string two qubits in the state :math:`|10\rangle`. - The order of the labels is reversed with respect to qubit index. - More information about labels for basis states are in - :meth:`.Statevector.from_label`. - - .. code-block:: - - import numpy as np - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.initialize('01', circuit.qubits) - circuit.draw() - - output: - - .. parsed-literal:: - - ┌──────────────────┐ - q_0: ┤0 ├ - │ Initialize(0,1) │ - q_1: ┤1 ├ - └──────────────────┘ - - Initialize two qubits from an array of complex amplitudes. - - .. code-block:: - - import numpy as np - from qiskit import QuantumCircuit - - circuit = QuantumCircuit(2) - circuit.initialize([0, 1/np.sqrt(2), -1.j/np.sqrt(2), 0], circuit.qubits) - circuit.draw() - - output: - - .. parsed-literal:: - - ┌────────────────────────────────────┐ - q_0: ┤0 ├ - │ Initialize(0,0.70711,-0.70711j,0) │ - q_1: ┤1 ├ - └────────────────────────────────────┘ - """ - if qubits is None: - qubits = self.qubits - elif isinstance(qubits, (int, np.integer, slice, Qubit)): - qubits = [qubits] - num_qubits = len(qubits) if isinstance(params, int) else None - - return self.append(Initialize(params, num_qubits, normalize), qubits) - - -QuantumCircuit.initialize = initialize diff --git a/qiskit/extensions/quantum_initializer/squ.py b/qiskit/extensions/quantum_initializer/squ.py index b4dceef8f1b6..8356dc86fb9f 100644 --- a/qiskit/extensions/quantum_initializer/squ.py +++ b/qiskit/extensions/quantum_initializer/squ.py @@ -21,11 +21,12 @@ import numpy as np -from qiskit.circuit import QuantumRegister, Qubit, QuantumCircuit +from qiskit.circuit import QuantumRegister, QuantumCircuit from qiskit.circuit.gate import Gate from qiskit.circuit.exceptions import CircuitError from qiskit.quantum_info.operators.predicates import is_unitary_matrix from qiskit.exceptions import QiskitError +from qiskit.utils.deprecation import deprecate_func _EPS = 1e-10 # global variable used to chop very small numbers to zero @@ -41,6 +42,10 @@ class SingleQubitUnitary(Gate): matrix :math:`D` with :math:`U = D U'`. """ + @deprecate_func( + since="0.45.0", + additional_msg="Instead, you can use qiskit.circuit.library.UnitaryGate.", + ) def __init__(self, unitary_matrix, mode="ZYZ", up_to_diagonal=False): """Create a new single qubit gate based on the unitary ``u``.""" if mode not in ["ZYZ"]: @@ -156,49 +161,3 @@ def validate_parameter(self, parameter): return parameter else: raise CircuitError(f"invalid param type {type(parameter)} in gate {self.name}") - - -def squ( - self, - unitary_matrix, - qubit, - mode="ZYZ", - up_to_diagonal=False, -): - """Decompose an arbitrary 2*2 unitary into three rotation gates. - - Note that the decomposition is up to a global phase shift. - (This is a well known decomposition which can be found for example in Nielsen and Chuang's book - "Quantum computation and quantum information".) - - Args: - unitary_matrix (ndarray): 2*2 unitary (given as a (complex) ndarray). - qubit (QuantumRegister or Qubit): The qubit which the gate is acting on. - mode (string): determines the used decomposition by providing the rotation axes. - The allowed modes are: "ZYZ" (default) - up_to_diagonal (bool): if set to True, the single-qubit unitary is decomposed up to - a diagonal matrix, i.e. a unitary u' is implemented such that there exists a 2*2 - diagonal gate d with u = d.dot(u') - - Returns: - InstructionSet: The single-qubit unitary instruction attached to the circuit. - - Raises: - QiskitError: if the format is wrong; if the array u is not unitary - """ - - if isinstance(qubit, QuantumRegister): - qubit = qubit[:] - if len(qubit) == 1: - qubit = qubit[0] - else: - raise QiskitError( - "The target qubit is a QuantumRegister containing more than one qubit." - ) - # Check if there is one target qubit provided - if not isinstance(qubit, Qubit): - raise QiskitError("The target qubit is not a single qubit from a QuantumRegister.") - return self.append(SingleQubitUnitary(unitary_matrix, mode, up_to_diagonal), [qubit], []) - - -QuantumCircuit.squ = squ diff --git a/qiskit/extensions/quantum_initializer/ucrx.py b/qiskit/extensions/quantum_initializer/ucrx.py deleted file mode 100644 index 422204498635..000000000000 --- a/qiskit/extensions/quantum_initializer/ucrx.py +++ /dev/null @@ -1,94 +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. - -""" -Implementation of the abstract class UCPauliRotGate for uniformly controlled -(also called multiplexed) single-qubit rotations around the X-axes -(i.e., uniformly controlled R_x rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_x(a_i) is applied to the target qubit. -""" -import math -from typing import List, Sequence - -from qiskit.circuit.quantumcircuit import QuantumCircuit, QubitSpecifier -from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.exceptions import QiskitError -from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate - - -class UCRXGate(UCPauliRotGate): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "X") - - -def ucrx( - self, angle_list: List[float], q_controls: Sequence[QubitSpecifier], q_target: QubitSpecifier -): - r"""Attach a uniformly controlled (also called multiplexed) Rx rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (List[float]): list of (real) rotation angles :math:`[a_0,...,a_{2^k-1}]` - q_controls (Sequence[QubitSpecifier]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if ``q_controls=[q[0],q[1]]`` - (with ``q = QuantumRegister(2)``), the rotation ``Rx(a_0)`` is performed if ``q[0]`` - and ``q[1]`` are in the state zero, the rotation ``Rx(a_1)`` is performed if ``q[0]`` - is in the state one and ``q[1]`` is in the state zero, and so on - q_target (QubitSpecifier): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError( - "The target qubit is a QuantumRegister containing more than one qubit." - ) - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError( - "The number of controlled rotation gates is not a non-negative power of 2." - ) - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError( - "Number of controlled rotations does not correspond to the number of control-qubits." - ) - return self.append(UCRXGate(angle_list), [q_target] + q_controls, []) - - -QuantumCircuit.ucrx = ucrx diff --git a/qiskit/extensions/quantum_initializer/ucry.py b/qiskit/extensions/quantum_initializer/ucry.py deleted file mode 100644 index bebf1b613888..000000000000 --- a/qiskit/extensions/quantum_initializer/ucry.py +++ /dev/null @@ -1,94 +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. - -""" -Implementation of the abstract class UCPauliRotGate for uniformly controlled -(also called multiplexed) single-qubit rotations around the Y-axes -(i.e., uniformly controlled R_y rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_y(a_i) is applied to the target qubit. -""" -import math -from typing import List, Sequence - -from qiskit.circuit.quantumcircuit import QuantumCircuit, QubitSpecifier -from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.exceptions import QiskitError -from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate - - -class UCRYGate(UCPauliRotGate): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "Y") - - -def ucry( - self, angle_list: List[float], q_controls: Sequence[QubitSpecifier], q_target: QubitSpecifier -): - r"""Attach a uniformly controlled (also called multiplexed) Ry rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (List[float]): list of (real) rotation angles :math:`[a_0,...,a_{2^k-1}]` - q_controls (Sequence[QubitSpecifier]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if ``q_controls=[q[0],q[1]]`` - (with ``q = QuantumRegister(2)``), the rotation ``Ry(a_0)`` is performed if ``q[0]`` - and ``q[1]`` are in the state zero, the rotation ``Ry(a_1)`` is performed if ``q[0]`` - is in the state one and ``q[1]`` is in the state zero, and so on - q_target (QubitSpecifier): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError( - "The target qubit is a QuantumRegister containing more than one qubit." - ) - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError( - "The number of controlled rotation gates is not a non-negative power of 2." - ) - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError( - "Number of controlled rotations does not correspond to the number of control-qubits." - ) - return self.append(UCRYGate(angle_list), [q_target] + q_controls, []) - - -QuantumCircuit.ucry = ucry diff --git a/qiskit/extensions/quantum_initializer/ucrz.py b/qiskit/extensions/quantum_initializer/ucrz.py deleted file mode 100644 index d7404d8fe4e1..000000000000 --- a/qiskit/extensions/quantum_initializer/ucrz.py +++ /dev/null @@ -1,94 +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. - -""" -Implementation of the abstract class UCPauliRotGate for uniformly controlled -(also called multiplexed) single-qubit rotations around the Z-axes -(i.e., uniformly controlled R_z rotations). -These gates can have several control qubits and a single target qubit. -If the k control qubits are in the state ket(i) (in the computational bases), -a single-qubit rotation R_z(a_i) is applied to the target qubit. -""" -import math -from typing import List, Sequence - -from qiskit.circuit.quantumcircuit import QuantumCircuit, QubitSpecifier -from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.exceptions import QiskitError -from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate - - -class UCRZGate(UCPauliRotGate): - """ - Uniformly controlled rotations (also called multiplexed rotations). - The decomposition is based on - 'Synthesis of Quantum Logic Circuits' by V. Shende et al. - (https://arxiv.org/pdf/quant-ph/0406176.pdf) - """ - - def __init__(self, angle_list): - super().__init__(angle_list, "Z") - - -def ucrz( - self, angle_list: List[float], q_controls: Sequence[QubitSpecifier], q_target: QubitSpecifier -): - r"""Attach a uniformly controlled (also called multiplexed gates) Rz rotation gate to a circuit. - - The decomposition is base on https://arxiv.org/pdf/quant-ph/0406176.pdf by Shende et al. - - Args: - angle_list (List[float]): list of (real) rotation angles :math:`[a_0,...,a_{2^k-1}]` - q_controls (Sequence[QubitSpecifier]): list of k control qubits - (or empty list if no controls). The control qubits are ordered according to their - significance in increasing order: For example if ``q_controls=[q[0],q[1]]`` - (with ``q = QuantumRegister(2)``), the rotation ``Rx(a_0)`` is performed if ``q[0]`` - and ``q[1]`` are in the state zero, the rotation ``Rx(a_1)`` is performed if ``q[0]`` - is in the state one and ``q[1]`` is in the state zero, and so on - q_target (QubitSpecifier): target qubit, where we act on with - the single-qubit rotation gates - - Returns: - QuantumCircuit: the uniformly controlled rotation gate is attached to the circuit. - - Raises: - QiskitError: if the list number of control qubits does not correspond to the provided - number of single-qubit unitaries; if an input is of the wrong type - """ - - if isinstance(q_controls, QuantumRegister): - q_controls = q_controls[:] - if isinstance(q_target, QuantumRegister): - q_target = q_target[:] - if len(q_target) == 1: - q_target = q_target[0] - else: - raise QiskitError( - "The target qubit is a QuantumRegister containing more than one qubit." - ) - # Check if q_controls has type "list" - if not isinstance(angle_list, list): - raise QiskitError("The angles must be provided as a list.") - num_contr = math.log2(len(angle_list)) - if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError( - "The number of controlled rotation gates is not a non-negative power of 2." - ) - # Check if number of control qubits does correspond to the number of rotations - if num_contr != len(q_controls): - raise QiskitError( - "Number of controlled rotations does not correspond to the number of control-qubits." - ) - return self.append(UCRZGate(angle_list), [q_target] + q_controls, []) - - -QuantumCircuit.ucrz = ucrz diff --git a/qiskit/extensions/simulator/snapshot.py b/qiskit/extensions/simulator/snapshot.py index 434b6da7c344..ca45e56354ac 100644 --- a/qiskit/extensions/simulator/snapshot.py +++ b/qiskit/extensions/simulator/snapshot.py @@ -13,17 +13,23 @@ Simulator command to snapshot internal simulator representation. """ -from qiskit.circuit.quantumcircuit import QuantumCircuit -from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.instruction import Instruction from qiskit.extensions.exceptions import QiskitError, ExtensionError +from qiskit.utils.deprecation import deprecate_func + class Snapshot(Instruction): """Simulator snapshot instruction.""" _directive = True + @deprecate_func( + since="0.45.0", + additional_msg="The Snapshot instruction has been superseded by Qiskit Aer's save " + "instructions, see " + "https://qiskit.org/ecosystem/aer/apidocs/aer_library.html#saving-simulator-data.", + ) def __init__(self, label, snapshot_type="statevector", num_qubits=0, num_clbits=0, params=None): """Create new snapshot instruction. @@ -62,48 +68,3 @@ def snapshot_type(self): def c_if(self, classical, val): raise QiskitError("Snapshots are simulator directives and cannot be conditional.") - - -def snapshot(self, label, snapshot_type="statevector", qubits=None, params=None): - """Take a statevector snapshot of the internal simulator representation. - Works on all qubits, and prevents reordering (like barrier). - - For other types of snapshots use the Snapshot extension directly. - - Args: - label (str): a snapshot label to report the result. - snapshot_type (str): the type of the snapshot. - qubits (list or None): the qubits to apply snapshot to [Default: None]. - params (list or None): the parameters for snapshot_type [Default: None]. - - Returns: - QuantumCircuit: with attached command - - Raises: - ExtensionError: malformed command - """ - # If no qubits are specified we add all qubits so it acts as a barrier - # This is needed for full register snapshots like statevector - if isinstance(qubits, QuantumRegister): - qubits = qubits[:] - if not qubits: - tuples = [] - if isinstance(self, QuantumCircuit): - for register in self.qregs: - tuples.append(register) - if not tuples: - raise ExtensionError("no qubits for snapshot") - qubits = [] - for tuple_element in tuples: - if isinstance(tuple_element, QuantumRegister): - for j in range(tuple_element.size): - qubits.append(tuple_element[j]) - else: - qubits.append(tuple_element) - return self.append( - Snapshot(label, snapshot_type=snapshot_type, num_qubits=len(qubits), params=params), qubits - ) - - -# Add to QuantumCircuit class -QuantumCircuit.snapshot = snapshot diff --git a/qiskit/opflow/primitive_ops/matrix_op.py b/qiskit/opflow/primitive_ops/matrix_op.py index be8c8ac90eec..5afe0ac54570 100644 --- a/qiskit/opflow/primitive_ops/matrix_op.py +++ b/qiskit/opflow/primitive_ops/matrix_op.py @@ -18,7 +18,7 @@ from qiskit import QuantumCircuit from qiskit.circuit import Instruction, ParameterExpression -from qiskit.extensions.hamiltonian_gate import HamiltonianGate +from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate from qiskit.opflow.exceptions import OpflowError from qiskit.opflow.list_ops.summed_op import SummedOp from qiskit.opflow.list_ops.tensored_op import TensoredOp diff --git a/qiskit/primitives/utils.py b/qiskit/primitives/utils.py index 20db6c0d7295..611fbe86662c 100644 --- a/qiskit/primitives/utils.py +++ b/qiskit/primitives/utils.py @@ -22,7 +22,7 @@ from qiskit.circuit import Instruction, ParameterExpression, QuantumCircuit from qiskit.circuit.bit import Bit -from qiskit.extensions.quantum_initializer.initializer import Initialize +from qiskit.circuit.library.data_preparation import Initialize from qiskit.quantum_info import SparsePauliOp, Statevector from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.quantum_info.operators.symplectic.base_pauli import BasePauli diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py index 71a5201f5ba9..5de9efc380e5 100644 --- a/qiskit/quantum_info/operators/operator.py +++ b/qiskit/quantum_info/operators/operator.py @@ -204,7 +204,7 @@ def draw(self, output=None, **drawer_args): else: raise ValueError( - f"""'{output}' is not a valid option for drawing {type(self).__name__} objects. + f"""'{output}' is not a valid option for drawing {type(self).__name__} objects. Please choose from: 'text', 'latex', or 'latex_source'.""" ) @@ -441,7 +441,7 @@ def to_operator(self) -> Operator: def to_instruction(self): """Convert to a UnitaryGate instruction.""" # pylint: disable=cyclic-import - from qiskit.extensions.unitary import UnitaryGate + from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate return UnitaryGate(self.data) diff --git a/qiskit/quantum_info/states/statevector.py b/qiskit/quantum_info/states/statevector.py index 5cb3120ab9c4..0f6953b3af30 100644 --- a/qiskit/quantum_info/states/statevector.py +++ b/qiskit/quantum_info/states/statevector.py @@ -891,7 +891,7 @@ def _evolve_instruction(statevec, obj, qargs=None): # imports the StatePreparation, which again requires the Statevector (this file), # but as this is a local import, it's not actually an issue and can be ignored # pylint: disable=cyclic-import - from qiskit.extensions.quantum_initializer.initializer import Initialize + from qiskit.circuit.library.data_preparation.initializer import Initialize mat = Operator._instruction_to_matrix(obj) if mat is not None: diff --git a/qiskit/quantum_info/synthesis/qsd.py b/qiskit/quantum_info/synthesis/qsd.py index 90278dad62ed..b6b68c8f4393 100644 --- a/qiskit/quantum_info/synthesis/qsd.py +++ b/qiskit/quantum_info/synthesis/qsd.py @@ -20,7 +20,9 @@ from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.quantum_info.synthesis import two_qubit_decompose, one_qubit_decompose from qiskit.quantum_info.operators.predicates import is_hermitian_matrix -from qiskit.extensions.quantum_initializer.uc_pauli_rot import UCPauliRotGate, _EPS +from qiskit.circuit.library.generalized_gates.uc_pauli_rot import UCPauliRotGate, _EPS +from qiskit.circuit.library.generalized_gates.ucry import UCRYGate +from qiskit.circuit.library.generalized_gates.ucrz import UCRZGate def qs_decomposition( @@ -89,7 +91,7 @@ def qs_decomposition( elif dim == 4: if decomposer_2q is None: if opt_a2 and _depth > 0: - from qiskit.extensions.unitary import UnitaryGate # pylint: disable=cyclic-import + from qiskit.circuit.library import UnitaryGate # pylint: disable=cyclic-import def decomp_2q(mat): ugate = UnitaryGate(mat) @@ -120,7 +122,8 @@ def decomp_2q(mat): # merge final cz with right-side generic multiplexer u2[:, half_size:] = np.negative(u2[:, half_size:]) else: - circ.ucry((2 * vtheta).tolist(), qr[:-1], qr[-1]) + ucry = UCRYGate((2 * vtheta).tolist()) + circ.append(ucry, [qr[-1]] + qr[:-1]) # right circ right_circ = _demultiplex(u1, u2, opt_a1=opt_a1, opt_a2=opt_a2, _depth=_depth) circ.append(right_circ.to_instruction(), qr) @@ -189,7 +192,8 @@ def _demultiplex(um0, um1, opt_a1=False, opt_a2=False, *, _depth=0): # multiplexed Rz angles = 2 * np.angle(np.conj(dvals)) - circ.ucrz(angles.tolist(), list(range(nqubits - 1)), [nqubits - 1]) + ucrz = UCRZGate(angles.tolist()) + circ.append(ucrz, [nqubits - 1] + list(range(nqubits - 1))) # right gate right_gate = qs_decomposition( @@ -232,9 +236,7 @@ def _get_ucry_cz(nqubits, angles): def _apply_a2(circ): from qiskit import transpile from qiskit.quantum_info import Operator - - # from qiskit.extensions.unitary import UnitaryGate - import qiskit.extensions.unitary + from qiskit.circuit.library.generalized_gates import UnitaryGate decomposer = two_qubit_decompose.TwoQubitDecomposeUpToDiagonal() ccirc = transpile(circ, basis_gates=["u", "cx", "qsd2q"], optimization_level=0) @@ -257,7 +259,7 @@ def _apply_a2(circ): dmat, qc2cx = decomposer(mat1) ccirc.data[ind1] = instr1.replace(operation=qc2cx.to_gate()) mat2 = mat2 @ dmat - ccirc.data[ind2] = instr2.replace(qiskit.extensions.unitary.UnitaryGate(mat2)) + ccirc.data[ind2] = instr2.replace(UnitaryGate(mat2)) qc3 = two_qubit_decompose.two_qubit_cnot_decompose(mat2) ccirc.data[ind2] = ccirc.data[ind2].replace(operation=qc3.to_gate()) return ccirc diff --git a/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py b/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py index 6eab4c81e662..e83cf5c18dab 100644 --- a/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py +++ b/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py @@ -251,7 +251,7 @@ def __call__( basis_fidelity, approximate=approximate ) - from qiskit.extensions import UnitaryGate # pylint: disable=cyclic-import + from qiskit.circuit.library import UnitaryGate # pylint: disable=cyclic-import # get the associated _positive_ canonical coordinate weyl_decomposition = TwoQubitWeylDecomposition(unitary) diff --git a/qiskit/synthesis/discrete_basis/gate_sequence.py b/qiskit/synthesis/discrete_basis/gate_sequence.py index 611babcce964..69d6b224996e 100644 --- a/qiskit/synthesis/discrete_basis/gate_sequence.py +++ b/qiskit/synthesis/discrete_basis/gate_sequence.py @@ -19,7 +19,7 @@ import numpy as np from qiskit.circuit import Gate, QuantumCircuit, Qubit -from qiskit.extensions import UnitaryGate +from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate class GateSequence: diff --git a/qiskit/synthesis/evolution/matrix_synthesis.py b/qiskit/synthesis/evolution/matrix_synthesis.py index 97d0167a3ac4..1761cda18ff1 100644 --- a/qiskit/synthesis/evolution/matrix_synthesis.py +++ b/qiskit/synthesis/evolution/matrix_synthesis.py @@ -27,7 +27,8 @@ class MatrixExponential(EvolutionSynthesis): """ def synthesize(self, evolution): - from qiskit.extensions import HamiltonianGate + # pylint: disable=cyclic-import + from qiskit.circuit.library.hamiltonian_gate import HamiltonianGate # get operators and time to evolve operators = evolution.operator diff --git a/qiskit/transpiler/passes/optimization/consolidate_blocks.py b/qiskit/transpiler/passes/optimization/consolidate_blocks.py index 90ad8ec46e31..5a3bcbe064d9 100644 --- a/qiskit/transpiler/passes/optimization/consolidate_blocks.py +++ b/qiskit/transpiler/passes/optimization/consolidate_blocks.py @@ -21,7 +21,7 @@ from qiskit.dagcircuit.dagnode import DAGOpNode from qiskit.quantum_info import Operator from qiskit.quantum_info.synthesis import TwoQubitBasisDecomposer -from qiskit.extensions import UnitaryGate +from qiskit.circuit.library.generalized_gates.unitary import UnitaryGate from qiskit.circuit.library.standard_gates import CXGate from qiskit.transpiler.basepasses import TransformationPass from qiskit.circuit.controlflow import ControlFlowOp diff --git a/qiskit/transpiler/passes/optimization/hoare_opt.py b/qiskit/transpiler/passes/optimization/hoare_opt.py index d028f80dbe7a..ebc89ca66283 100644 --- a/qiskit/transpiler/passes/optimization/hoare_opt.py +++ b/qiskit/transpiler/passes/optimization/hoare_opt.py @@ -14,7 +14,7 @@ from qiskit.transpiler.basepasses import TransformationPass from qiskit.circuit import QuantumRegister, ControlledGate, Gate from qiskit.dagcircuit import DAGCircuit -from qiskit.extensions.unitary import UnitaryGate +from qiskit.circuit.library import UnitaryGate from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.circuit.exceptions import CircuitError from qiskit.circuit.library.standard_gates import CZGate, CU1Gate, MCU1Gate diff --git a/releasenotes/notes/0.21/quantum_shannon_decomp-facaa362a3ca8ba3.yaml b/releasenotes/notes/0.21/quantum_shannon_decomp-facaa362a3ca8ba3.yaml index dcba89bdbfb8..246e8b6a2ee3 100644 --- a/releasenotes/notes/0.21/quantum_shannon_decomp-facaa362a3ca8ba3.yaml +++ b/releasenotes/notes/0.21/quantum_shannon_decomp-facaa362a3ca8ba3.yaml @@ -5,7 +5,7 @@ features: Shannon Decomposition of arbitrary unitaries. This functionality replaces the previous isometry-based approach in the default unitary synthesis transpiler pass as well as when adding unitaries to a circuit using a - :class:`.UnitaryGate`. + :class:`~.library.UnitaryGate`. The Quantum Shannon Decomposition uses about half the cnot gates as the isometry implementation when decomposing unitary matrices of greater than diff --git a/releasenotes/notes/0.22/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml b/releasenotes/notes/0.22/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml index 7cbd2a5cdfcc..89939452712e 100644 --- a/releasenotes/notes/0.22/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml +++ b/releasenotes/notes/0.22/fix-qasm2-identity-as-unitary-aa2feeb05707a597.yaml @@ -2,6 +2,6 @@ fixes: - | The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now correctly - define the qubit parameters for :class:`.UnitaryGate` operations that do + define the qubit parameters for :class:`~.library.UnitaryGate` operations that do not affect all the qubits they are defined over. Fixed `#8224 `__. diff --git a/releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml b/releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml index a6573de88e5b..c6170b62a8fa 100644 --- a/releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml +++ b/releasenotes/notes/0.24/qasm2-exporter-rewrite-8993dd24f930b180.yaml @@ -10,7 +10,7 @@ fixes: `#9805 `__. - | The OpenQASM 2 exporter (:meth:`.QuantumCircuit.qasm`) will now handle multiple and nested - definitions of :class:`.UnitaryGate`. See + definitions of :class:`~.library.UnitaryGate`. See `#4623 `__, `#6712 `__, `#7772 `__, and diff --git a/releasenotes/notes/deprecate-extensions-d7ab3fca19962e2e.yaml b/releasenotes/notes/deprecate-extensions-d7ab3fca19962e2e.yaml new file mode 100644 index 000000000000..27d0abd9ac26 --- /dev/null +++ b/releasenotes/notes/deprecate-extensions-d7ab3fca19962e2e.yaml @@ -0,0 +1,40 @@ +--- +deprecations: + - | + To streamline the structure of Qiskit's gates and operations, the :mod:`qiskit.extensions` + module is pending deprecation and will be deprecated in a future release. + The following objects have been moved to :mod:`qiskit.circuit.library` + + * :class:`~.library.DiagonalGate`, + * :class:`~.library.HamiltonianGateGate`, + * :class:`~.library.Initialize`, + * :class:`~.library.Isometry`, + * :class:`~.library.generalized_gates.mcg_up_diag.MCGupDiag`, + * :class:`~.library.UCGate`, + * :class:`~.library.UCPauliRotGate`, + * :class:`~.library.UCRXGate`, + * :class:`~.library.UCRYGate`, + * :class:`~.library.UCRZGate`, + * :class:`~.library.UnitaryGate`. + + These instructions have already been deprecated in this release, + + * :class:`~.SingleQubitUnitary`, instead use :class:`.library.UnitaryGate`, + * :class:`~.extensions.Snapshot`, which has been superseded by Qiskit Aer's save instructions, + + along with their circuit methods + + * :meth:`.QuantumCircuit.snapshot`, + * :meth:`.QuantumCircuit.squ`. + + In addition, the following circuit methods are pending deprecation + + * :meth:`.QuantumCircuit.diagonal`, + * :meth:`.QuantumCircuit.hamiltonian`, + * :meth:`.QuantumCircuit.isometry` and :meth:`.QuantumCircuit.iso`, + * :meth:`.QuantumCircuit.uc`, + * :meth:`.QuantumCircuit.ucrx`, + * :meth:`.QuantumCircuit.ucry`, + * :meth:`.QuantumCircuit.ucrz`. + + Since the entire module is pending deprecation, so is :class:`.ExtensionError`. diff --git a/test/python/circuit/library/test_evolved_op_ansatz.py b/test/python/circuit/library/test_evolved_op_ansatz.py index 492e35029443..06bcc206d8a4 100644 --- a/test/python/circuit/library/test_evolved_op_ansatz.py +++ b/test/python/circuit/library/test_evolved_op_ansatz.py @@ -19,7 +19,7 @@ from qiskit.opflow import X, Y, Z, I, MatrixEvolution from qiskit.quantum_info import SparsePauliOp, Operator, Pauli -from qiskit.circuit.library import EvolvedOperatorAnsatz +from qiskit.circuit.library import EvolvedOperatorAnsatz, HamiltonianGate from qiskit.synthesis.evolution import MatrixExponential from qiskit.test import QiskitTestCase @@ -69,7 +69,7 @@ def test_custom_evolution(self, use_opflow): parameters = evo.parameters reference = QuantumCircuit(3) - reference.hamiltonian(matrix, parameters[0], [0, 1, 2]) + reference.append(HamiltonianGate(matrix, parameters[0]), [0, 1, 2]) decomposed = evo.decompose() if not use_opflow: diff --git a/test/python/circuit/test_circuit_load_from_qpy.py b/test/python/circuit/test_circuit_load_from_qpy.py index 157b325050e6..c2d17c454d30 100644 --- a/test/python/circuit/test_circuit_load_from_qpy.py +++ b/test/python/circuit/test_circuit_load_from_qpy.py @@ -40,12 +40,15 @@ MCXGrayCode, MCXRecursive, MCXVChain, + UCRXGate, + UCRYGate, + UCRZGate, + UnitaryGate, ) from qiskit.circuit.instruction import Instruction from qiskit.circuit.parameter import Parameter from qiskit.circuit.parametervector import ParameterVector from qiskit.synthesis import LieTrotter, SuzukiTrotter -from qiskit.extensions import UnitaryGate from qiskit.test import QiskitTestCase from qiskit.qpy import dump, load from qiskit.quantum_info import Pauli, SparsePauliOp @@ -1194,9 +1197,11 @@ def test_empty_tuple_param(self): def test_ucr_gates(self): """Test qpy with UCRX, UCRY, and UCRZ gates.""" qc = QuantumCircuit(3) - qc.ucrz([0, 0, 0, -np.pi], [0, 1], 2) - qc.ucry([0, 0, 0, -np.pi], [0, 2], 1) - qc.ucrx([0, 0, 0, -np.pi], [2, 1], 0) + angles = [0, 0, 0, -np.pi] + ucrx, ucry, ucrz = UCRXGate(angles), UCRYGate(angles), UCRZGate(angles) + qc.append(ucrz, [2, 0, 1]) + qc.append(ucry, [1, 0, 2]) + qc.append(ucrx, [0, 2, 1]) qc.measure_all() qpy_file = io.BytesIO() dump(qc, qpy_file) @@ -1438,7 +1443,9 @@ def test_incomplete_owned_bits(self): def test_diagonal_gate(self): """Test that a `DiagonalGate` successfully roundtrips.""" qc = QuantumCircuit(2) - qc.diagonal([1, -1, -1, 1], [0, 1]) + with self.assertWarns(PendingDeprecationWarning): + qc.diagonal([1, -1, -1, 1], [0, 1]) + with io.BytesIO() as fptr: dump(qc, fptr) fptr.seek(0) diff --git a/test/python/circuit/test_circuit_properties.py b/test/python/circuit/test_circuit_properties.py index 806b771ea469..f2531954676e 100644 --- a/test/python/circuit/test_circuit_properties.py +++ b/test/python/circuit/test_circuit_properties.py @@ -578,7 +578,8 @@ def test_circuit_depth_snap1(self): circ = QuantumCircuit(q, c) circ.h(0) circ.cx(0, 1) - circ.append(Snapshot("snap", num_qubits=4), [0, 1, 2, 3]) + with self.assertWarns(DeprecationWarning): + circ.append(Snapshot("snap", num_qubits=4), [0, 1, 2, 3]) circ.h(2) circ.cx(2, 3) self.assertEqual(circ.depth(), 4) @@ -599,11 +600,14 @@ def test_circuit_depth_snap2(self): c = ClassicalRegister(4, "c") circ = QuantumCircuit(q, c) circ.h(0) - circ.append(Snapshot("snap0", num_qubits=4), [0, 1, 2, 3]) + with self.assertWarns(DeprecationWarning): + circ.append(Snapshot("snap0", num_qubits=4), [0, 1, 2, 3]) circ.cx(0, 1) - circ.append(Snapshot("snap1", num_qubits=4), [0, 1, 2, 3]) + with self.assertWarns(DeprecationWarning): + circ.append(Snapshot("snap1", num_qubits=4), [0, 1, 2, 3]) circ.h(2) - circ.append(Snapshot("snap2", num_qubits=4), [0, 1, 2, 3]) + with self.assertWarns(DeprecationWarning): + circ.append(Snapshot("snap2", num_qubits=4), [0, 1, 2, 3]) circ.cx(2, 3) self.assertEqual(circ.depth(), 4) @@ -624,8 +628,9 @@ def test_circuit_depth_snap3(self): circ = QuantumCircuit(q, c) circ.h(0) circ.cx(0, 1) - circ.append(Snapshot("snap0", num_qubits=4), [0, 1, 2, 3]) - circ.append(Snapshot("snap1", num_qubits=4), [0, 1, 2, 3]) + with self.assertWarns(DeprecationWarning): + circ.append(Snapshot("snap0", num_qubits=4), [0, 1, 2, 3]) + circ.append(Snapshot("snap1", num_qubits=4), [0, 1, 2, 3]) circ.h(2) circ.cx(2, 3) self.assertEqual(circ.depth(), 4) @@ -751,7 +756,8 @@ def test_circuit_size_ignores_barriers_snapshots(self): self.assertEqual(qc.size(), 2) qc.barrier(q) self.assertEqual(qc.size(), 2) - qc.append(Snapshot("snapshot_label", num_qubits=4), [0, 1, 2, 3]) + with self.assertWarns(DeprecationWarning): + qc.append(Snapshot("snapshot_label", num_qubits=4), [0, 1, 2, 3]) self.assertEqual(qc.size(), 2) def test_circuit_count_ops(self): diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py index 248aabab7839..cbef06d630a8 100644 --- a/test/python/circuit/test_controlled_gate.py +++ b/test/python/circuit/test_controlled_gate.py @@ -73,10 +73,10 @@ C4XGate, MCPhaseGate, GlobalPhaseGate, + UnitaryGate, ) from qiskit.circuit._utils import _compute_control_matrix import qiskit.circuit.library.standard_gates as allGates -from qiskit.extensions import UnitaryGate from qiskit.circuit.library.standard_gates.multi_control_rotation_gates import _mcsu2_real_diagonal from .gate_utils import _get_free_params diff --git a/test/python/circuit/test_diagonal_gate.py b/test/python/circuit/test_diagonal_gate.py index f12d4ab92258..c0379e4fd9c1 100644 --- a/test/python/circuit/test_diagonal_gate.py +++ b/test/python/circuit/test_diagonal_gate.py @@ -16,7 +16,7 @@ import unittest import numpy as np -from qiskit import QuantumCircuit, QuantumRegister, BasicAer, execute +from qiskit import QuantumCircuit, QuantumRegister, BasicAer, execute, assemble from qiskit import QiskitError from qiskit.test import QiskitTestCase @@ -46,7 +46,8 @@ def test_diag_gate(self): num_qubits = int(np.log2(len(diag))) q = QuantumRegister(num_qubits) qc = QuantumCircuit(q) - qc.diagonal(diag, q[0:num_qubits]) + with self.assertWarns(PendingDeprecationWarning): + qc.diagonal(diag, q[0:num_qubits]) # Decompose the gate qc = transpile(qc, basis_gates=["u1", "u3", "u2", "cx", "id"], optimization_level=0) # Simulate the decomposed gate @@ -61,7 +62,27 @@ def test_mod1_entries(self): from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT, RTOL_DEFAULT with self.assertRaises(QiskitError): - DiagonalGate([1, 1 - 2 * ATOL_DEFAULT - RTOL_DEFAULT]) + with self.assertWarns(PendingDeprecationWarning): + DiagonalGate([1, 1 - 2 * ATOL_DEFAULT - RTOL_DEFAULT]) + + def test_npcomplex_params_conversion(self): + """Verify diagonal gate converts numpy.complex to complex.""" + # ref: https://github.com/Qiskit/qiskit-aer/issues/696 + diag = np.array([1 + 0j, 1 + 0j]) + qc = QuantumCircuit(1) + with self.assertWarns(PendingDeprecationWarning): + qc.diagonal(diag.tolist(), [0]) + + params = qc.data[0].operation.params + self.assertTrue( + all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) + ) + + qobj = assemble(qc) + params = qobj.experiments[0].instructions[0].params + self.assertTrue( + all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) + ) def _get_diag_gate_matrix(diag): diff --git a/test/python/circuit/test_gate_power.py b/test/python/circuit/test_gate_power.py index 45cf92fddfb7..b10f16a80487 100644 --- a/test/python/circuit/test_gate_power.py +++ b/test/python/circuit/test_gate_power.py @@ -21,7 +21,7 @@ from ddt import data, ddt, unpack from qiskit.circuit import Gate, QuantumCircuit -from qiskit.extensions import ( +from qiskit.circuit.library import ( CPhaseGate, CSdgGate, CSGate, diff --git a/test/python/circuit/test_hamiltonian_gate.py b/test/python/circuit/test_hamiltonian_gate.py index a27818489447..0769d765968a 100644 --- a/test/python/circuit/test_hamiltonian_gate.py +++ b/test/python/circuit/test_hamiltonian_gate.py @@ -18,13 +18,13 @@ import qiskit -from qiskit.extensions.hamiltonian_gate import HamiltonianGate, UnitaryGate -from qiskit.extensions.exceptions import ExtensionError +from qiskit.circuit.library import HamiltonianGate, UnitaryGate from qiskit.test import QiskitTestCase from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Parameter from qiskit.quantum_info import Operator from qiskit.converters import circuit_to_dag, dag_to_circuit +from qiskit.circuit.exceptions import CircuitError class TestHamiltonianGate(QiskitTestCase): @@ -37,12 +37,12 @@ def test_set_matrix(self): def test_set_matrix_raises(self): """test non-unitary""" - with self.assertRaises(ExtensionError): + with self.assertRaises(ValueError): HamiltonianGate([[1, 0], [1, 1]], 1) def test_complex_time_raises(self): """test non-unitary""" - with self.assertRaises(ExtensionError): + with self.assertRaises(ValueError): HamiltonianGate([[1, 0], [1, 1]], 1j) def test_conjugate(self): @@ -93,7 +93,7 @@ def test_error_and_deprecation_warning_on_qasm(self): """test that an error is thrown if the method `qasm` is called.""" matrix = np.zeros((2, 2)) hamiltonian_gate = HamiltonianGate(data=matrix, time=1) - with self.assertRaises(ExtensionError): + with self.assertRaises(CircuitError): with self.assertWarns(DeprecationWarning): hamiltonian_gate.qasm() diff --git a/test/python/circuit/test_initializer.py b/test/python/circuit/test_initializer.py index eb81c6766831..4710cfcaeba0 100644 --- a/test/python/circuit/test_initializer.py +++ b/test/python/circuit/test_initializer.py @@ -19,15 +19,19 @@ import numpy as np from ddt import ddt, data -from qiskit import QuantumCircuit -from qiskit import QuantumRegister -from qiskit import ClassicalRegister -from qiskit import transpile -from qiskit import execute, assemble, BasicAer +from qiskit import ( + QuantumCircuit, + QuantumRegister, + ClassicalRegister, + BasicAer, + transpile, + execute, + assemble, +) from qiskit.quantum_info import state_fidelity, Statevector, Operator from qiskit.exceptions import QiskitError from qiskit.test import QiskitTestCase -from qiskit.extensions.quantum_initializer import Initialize +from qiskit.circuit.library import Initialize @ddt @@ -487,24 +491,6 @@ def test_mutating_params(self): class TestInstructionParam(QiskitTestCase): """Test conversion of numpy type parameters.""" - def test_diag(self): - """Verify diagonal gate converts numpy.complex to complex.""" - # ref: https://github.com/Qiskit/qiskit-aer/issues/696 - diag = np.array([1 + 0j, 1 + 0j]) - qc = QuantumCircuit(1) - qc.diagonal(list(diag), [0]) - - params = qc.data[0].operation.params - self.assertTrue( - all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) - ) - - qobj = assemble(qc) - params = qobj.experiments[0].instructions[0].params - self.assertTrue( - all(isinstance(p, complex) and not isinstance(p, np.number) for p in params) - ) - def test_init(self): """Verify initialize gate converts numpy.complex to complex.""" # ref: https://github.com/Qiskit/qiskit-terra/issues/4151 diff --git a/test/python/circuit/test_instruction_repeat.py b/test/python/circuit/test_instruction_repeat.py index bb2d3d01cfe3..99f79f8f09d8 100644 --- a/test/python/circuit/test_instruction_repeat.py +++ b/test/python/circuit/test_instruction_repeat.py @@ -19,8 +19,7 @@ from qiskit.transpiler import PassManager from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.test import QiskitTestCase -from qiskit.extensions import UnitaryGate -from qiskit.circuit.library import SGate, U3Gate, CXGate +from qiskit.circuit.library import SGate, U3Gate, CXGate, UnitaryGate from qiskit.circuit import Instruction, Measure, Gate from qiskit.transpiler.passes import Unroller from qiskit.circuit.exceptions import CircuitError diff --git a/test/python/circuit/test_isometry.py b/test/python/circuit/test_isometry.py index bffd9c69bac3..774e2368803f 100644 --- a/test/python/circuit/test_isometry.py +++ b/test/python/circuit/test_isometry.py @@ -25,7 +25,7 @@ from qiskit.test import QiskitTestCase from qiskit.compiler import transpile from qiskit.quantum_info import Operator -from qiskit.extensions.quantum_initializer.isometry import Isometry +from qiskit.circuit.library.generalized_gates import Isometry @ddt @@ -54,7 +54,9 @@ def test_isometry(self, iso): num_q_input = int(np.log2(iso.shape[1])) q = QuantumRegister(num_q_output) qc = QuantumCircuit(q) - qc.isometry(iso, q[:num_q_input], q[num_q_input:]) + + with self.assertWarns(PendingDeprecationWarning): + qc.iso(iso, q[:num_q_input], q[num_q_input:]) # Verify the circuit can be decomposed self.assertIsInstance(qc.decompose(), QuantumCircuit) @@ -68,6 +70,7 @@ def test_isometry(self, iso): unitary = result.get_unitary(qc) iso_from_circuit = unitary[::, 0 : 2**num_q_input] iso_desired = iso + self.assertTrue(np.allclose(iso_from_circuit, iso_desired)) @data( @@ -94,7 +97,8 @@ def test_isometry_tolerance(self, iso): qc = QuantumCircuit(q) # Compute isometry with custom tolerance - qc.isometry(iso, q[:num_q_input], q[num_q_input:], epsilon=1e-3) + with self.assertWarns(PendingDeprecationWarning): + qc.isometry(iso, q[:num_q_input], q[num_q_input:], epsilon=1e-3) # Verify the circuit can be decomposed self.assertIsInstance(qc.decompose(), QuantumCircuit) @@ -107,6 +111,7 @@ def test_isometry_tolerance(self, iso): result = execute(qc, simulator).result() unitary = result.get_unitary(qc) iso_from_circuit = unitary[::, 0 : 2**num_q_input] + self.assertTrue(np.allclose(iso_from_circuit, iso)) @data( diff --git a/test/python/circuit/test_operation.py b/test/python/circuit/test_operation.py index 86db2c0ac456..7e98142ae612 100644 --- a/test/python/circuit/test_operation.py +++ b/test/python/circuit/test_operation.py @@ -18,9 +18,8 @@ from qiskit.test import QiskitTestCase from qiskit.circuit import QuantumCircuit, Barrier, Measure, Reset, Gate, Operation -from qiskit.circuit.library import XGate, CXGate +from qiskit.circuit.library import XGate, CXGate, Initialize, Isometry from qiskit.quantum_info.operators import Clifford, CNOTDihedral, Pauli -from qiskit.extensions.quantum_initializer import Initialize, Isometry class TestOperationClass(QiskitTestCase): diff --git a/test/python/circuit/test_squ.py b/test/python/circuit/test_squ.py index 250ad2077de1..e80a17e20b68 100644 --- a/test/python/circuit/test_squ.py +++ b/test/python/circuit/test_squ.py @@ -19,10 +19,7 @@ import numpy as np from ddt import ddt from qiskit.quantum_info.random import random_unitary -from qiskit import BasicAer -from qiskit import QuantumCircuit -from qiskit import QuantumRegister -from qiskit import execute +from qiskit import BasicAer, QuantumCircuit, QuantumRegister from qiskit.test import QiskitTestCase from qiskit.extensions.quantum_initializer.squ import SingleQubitUnitary from qiskit.compiler import transpile @@ -48,15 +45,17 @@ def test_squ(self, u, up_to_diagonal): """Tests for single-qubit unitary decomposition.""" qr = QuantumRegister(1, "qr") qc = QuantumCircuit(qr) - qc.squ(u, qr[0], up_to_diagonal=up_to_diagonal) + with self.assertWarns(DeprecationWarning): + qc.squ(u, qr[0], up_to_diagonal=up_to_diagonal) # Decompose the gate qc = transpile(qc, basis_gates=["u1", "u3", "u2", "cx", "id"]) # Simulate the decomposed gate simulator = BasicAer.get_backend("unitary_simulator") - result = execute(qc, simulator).result() + result = simulator.run(qc).result() unitary = result.get_unitary(qc) if up_to_diagonal: - squ = SingleQubitUnitary(u, up_to_diagonal=up_to_diagonal) + with self.assertWarns(DeprecationWarning): + squ = SingleQubitUnitary(u, up_to_diagonal=up_to_diagonal) unitary = np.dot(np.diagflat(squ.diag), unitary) unitary_desired = u self.assertTrue(matrix_equal(unitary_desired, unitary, ignore_phase=True)) diff --git a/test/python/circuit/test_uc.py b/test/python/circuit/test_uc.py index 1f50b650623c..9a7b5757a769 100644 --- a/test/python/circuit/test_uc.py +++ b/test/python/circuit/test_uc.py @@ -23,7 +23,7 @@ import numpy as np from scipy.linalg import block_diag -from qiskit.extensions.quantum_initializer.uc import UCGate +from qiskit.circuit.library.generalized_gates import UCGate from qiskit import QuantumCircuit, QuantumRegister, BasicAer, execute from qiskit.test import QiskitTestCase @@ -58,7 +58,11 @@ def test_ucg(self, squs, up_to_diagonal): num_con = int(np.log2(len(squs))) q = QuantumRegister(num_con + 1) qc = QuantumCircuit(q) - qc.uc(squs, q[1:], q[0], up_to_diagonal=up_to_diagonal) + + with self.assertWarns(PendingDeprecationWarning): + # TODO: change to directly appending UCGate once deprecation period of the method is over + qc.uc(squs, q[1:], q[0], up_to_diagonal=up_to_diagonal) + # Decompose the gate qc = transpile(qc, basis_gates=["u1", "u3", "u2", "cx", "id"]) # Simulate the decomposed gate @@ -78,7 +82,9 @@ def test_global_phase_ucg(self): q = QuantumRegister(num_con + 1) qc = QuantumCircuit(q) - qc.uc(gates, q[1:], q[0], up_to_diagonal=False) + uc = UCGate(gates, up_to_diagonal=False) + qc.append(uc, q) + simulator = BasicAer.get_backend("unitary_simulator") result = execute(qc, simulator).result() unitary = result.get_unitary(qc) @@ -93,7 +99,8 @@ def test_inverse_ucg(self): q = QuantumRegister(num_con + 1) qc = QuantumCircuit(q) - qc.uc(gates, q[1:], q[0], up_to_diagonal=False) + uc = UCGate(gates, up_to_diagonal=False) + qc.append(uc, q) qc.append(qc.inverse(), qc.qubits) unitary = Operator(qc).data diff --git a/test/python/circuit/test_ucx_y_z.py b/test/python/circuit/test_ucx_y_z.py index 4113c9336505..23ea57111fbb 100644 --- a/test/python/circuit/test_ucx_y_z.py +++ b/test/python/circuit/test_ucx_y_z.py @@ -14,6 +14,7 @@ import itertools import unittest +from ddt import ddt, data import numpy as np from scipy.linalg import block_diag @@ -23,6 +24,7 @@ from qiskit.quantum_info.operators.predicates import matrix_equal from qiskit.compiler import transpile +from qiskit.circuit.library import UCRXGate, UCRYGate, UCRZGate angles_list = [ [0], @@ -39,22 +41,28 @@ rot_axis_list = ["X", "Y", "Z"] +@ddt class TestUCRXYZ(QiskitTestCase): """Qiskit tests for UCRXGate, UCRYGate and UCRZGate rotations gates.""" - def test_ucy(self): + @data(True, False) + def test_ucy(self, use_method): """Test the decomposition of uniformly controlled rotations.""" + methods = {"X": "ucrx", "Y": "ucry", "Z": "ucrz"} + gates = {"X": UCRXGate, "Y": UCRYGate, "Z": UCRZGate} + for angles, rot_axis in itertools.product(angles_list, rot_axis_list): with self.subTest(angles=angles, rot_axis=rot_axis): num_contr = int(np.log2(len(angles))) q = QuantumRegister(num_contr + 1) qc = QuantumCircuit(q) - if rot_axis == "X": - qc.ucrx(angles, q[1 : num_contr + 1], q[0]) - elif rot_axis == "Y": - qc.ucry(angles, q[1 : num_contr + 1], q[0]) + if use_method: + with self.assertWarns(PendingDeprecationWarning): + getattr(qc, methods[rot_axis])(angles, q[1 : num_contr + 1], q[0]) else: - qc.ucrz(angles, q[1 : num_contr + 1], q[0]) + gate = gates[rot_axis](angles) + qc.append(gate, q) + # Decompose the gate qc = transpile(qc, basis_gates=["u1", "u3", "u2", "cx", "id"]) # Simulate the decomposed gate diff --git a/test/python/circuit/test_unitary.py b/test/python/circuit/test_unitary.py index 8edbd77222b1..27367372f329 100644 --- a/test/python/circuit/test_unitary.py +++ b/test/python/circuit/test_unitary.py @@ -17,7 +17,7 @@ from numpy.testing import assert_allclose import qiskit -from qiskit.extensions.unitary import UnitaryGate +from qiskit.circuit.library import UnitaryGate from qiskit.test import QiskitTestCase from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.transpiler import PassManager diff --git a/test/python/compiler/test_disassembler.py b/test/python/compiler/test_disassembler.py index 9828f1ba3c1c..024ede19b628 100644 --- a/test/python/compiler/test_disassembler.py +++ b/test/python/compiler/test_disassembler.py @@ -24,7 +24,7 @@ from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Gate, Instruction, Parameter -from qiskit.circuit.library import RXGate +from qiskit.circuit.library import RXGate, Isometry from qiskit.pulse.transforms import target_qobj_transform from qiskit.test import QiskitTestCase from qiskit.providers.fake_provider import FakeOpenPulse2Q @@ -141,7 +141,7 @@ def test_disassemble_isometry(self): """Test disassembling a circuit with an isometry.""" q = QuantumRegister(2, name="q") circ = QuantumCircuit(q, name="circ") - circ.iso(qi.random_unitary(4).data, circ.qubits, []) + circ.append(Isometry(qi.random_unitary(4).data, 0, 0), circ.qubits) qobj = assemble(circ) circuits, run_config_out, header = disassemble(qobj) run_config_out = RunConfig(**run_config_out) diff --git a/test/python/opflow/test_evolution.py b/test/python/opflow/test_evolution.py index c768b6c14aa8..0e79b9ad1199 100644 --- a/test/python/opflow/test_evolution.py +++ b/test/python/opflow/test_evolution.py @@ -19,7 +19,7 @@ import qiskit from qiskit.circuit import Parameter, ParameterVector -from qiskit.extensions import UnitaryGate +from qiskit.circuit.library import UnitaryGate from qiskit.opflow import ( CX, CircuitOp, diff --git a/test/python/opflow/test_op_construction.py b/test/python/opflow/test_op_construction.py index d52165887f01..a3dcb7dd6671 100644 --- a/test/python/opflow/test_op_construction.py +++ b/test/python/opflow/test_op_construction.py @@ -33,7 +33,6 @@ QuantumRegister, ) from qiskit.circuit.library import CZGate, ZGate -from qiskit.extensions.exceptions import ExtensionError from qiskit.opflow import ( CX, CircuitOp, @@ -297,7 +296,7 @@ def test_matrix_to_instruction(self): matop = ((H ^ 3) + (Z ^ 3)).to_matrix_op() with self.subTest("matrix operator is not unitary"): - with self.assertRaises(ExtensionError): + with self.assertRaises(ValueError): matop.to_instruction() def test_adjoint(self): @@ -862,8 +861,8 @@ def test_composed_op_to_circuit(self): pm1 = (X ^ Y) ^ m_op1 # non-unitary TensoredOp pm2 = (X ^ Y) ^ m_op2 # non-unitary TensoredOp - self.assertRaises(ExtensionError, pm1.to_circuit) - self.assertRaises(ExtensionError, pm2.to_circuit) + self.assertRaises(ValueError, pm1.to_circuit) + self.assertRaises(ValueError, pm2.to_circuit) summed_op = pm1 + pm2 # unitary SummedOp([TensoredOp, TensoredOp]) circuit = summed_op.to_circuit() # should transpile without any exception diff --git a/test/python/primitives/test_sampler.py b/test/python/primitives/test_sampler.py index 1195adb101fd..2dc2aac11098 100644 --- a/test/python/primitives/test_sampler.py +++ b/test/python/primitives/test_sampler.py @@ -18,8 +18,7 @@ from qiskit import QuantumCircuit from qiskit.circuit import Parameter -from qiskit.circuit.library import RealAmplitudes -from qiskit.extensions.unitary import UnitaryGate +from qiskit.circuit.library import RealAmplitudes, UnitaryGate from qiskit.primitives import Sampler, SamplerResult from qiskit.providers import JobStatus, JobV1 from qiskit.test import QiskitTestCase diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py index e0e337e7cd96..84fd607a544e 100644 --- a/test/python/providers/test_fake_backends.py +++ b/test/python/providers/test_fake_backends.py @@ -59,13 +59,15 @@ RYGate, CZGate, ECRGate, + UnitaryGate, + UCGate, + Initialize, + DiagonalGate, ) from qiskit.circuit import ControlledGate, Parameter from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel from qiskit.quantum_info.operators.channel.kraus import Kraus from qiskit.quantum_info.operators.channel import SuperOp -from qiskit.extensions import Initialize, UnitaryGate -from qiskit.extensions.quantum_initializer import DiagonalGate, UCGate from qiskit.circuit.controlflow import ( IfElseOp, WhileLoopOp, diff --git a/test/python/quantum_info/test_synthesis.py b/test/python/quantum_info/test_synthesis.py index 19ec8b8139e9..2a82e18316bb 100644 --- a/test/python/quantum_info/test_synthesis.py +++ b/test/python/quantum_info/test_synthesis.py @@ -27,7 +27,6 @@ from qiskit import execute, QiskitError, transpile from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.converters import dag_to_circuit, circuit_to_dag -from qiskit.extensions import UnitaryGate from qiskit.circuit.library import ( HGate, IGate, @@ -51,6 +50,7 @@ RXGate, RYGate, RZGate, + UnitaryGate, ) from qiskit.providers.basicaer import UnitarySimulatorPy from qiskit.quantum_info.operators import Operator diff --git a/test/python/quantum_info/xx_decompose/test_circuits.py b/test/python/quantum_info/xx_decompose/test_circuits.py index 2ee86fde94d2..59ba16e25918 100644 --- a/test/python/quantum_info/xx_decompose/test_circuits.py +++ b/test/python/quantum_info/xx_decompose/test_circuits.py @@ -21,7 +21,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library import RZGate +from qiskit.circuit.library import RZGate, UnitaryGate import qiskit.quantum_info.operators from qiskit.quantum_info.synthesis.weyl import weyl_coordinates from qiskit.quantum_info.synthesis.xx_decompose.circuits import ( @@ -111,13 +111,9 @@ def test_xx_circuit_step(self): source_coordinate, interaction, target_coordinate = self._generate_xxyy_test_case() source_embodiment = qiskit.QuantumCircuit(2) - source_embodiment.append( - qiskit.extensions.UnitaryGate(canonical_matrix(*source_coordinate)), [0, 1] - ) + source_embodiment.append(UnitaryGate(canonical_matrix(*source_coordinate)), [0, 1]) interaction_embodiment = qiskit.QuantumCircuit(2) - interaction_embodiment.append( - qiskit.extensions.UnitaryGate(canonical_matrix(*interaction)), [0, 1] - ) + interaction_embodiment.append(UnitaryGate(canonical_matrix(*interaction)), [0, 1]) prefix_circuit, affix_circuit = itemgetter("prefix_circuit", "affix_circuit")( xx_circuit_step( diff --git a/test/python/synthesis/test_cnot_phase_synthesis.py b/test/python/synthesis/test_cnot_phase_synthesis.py index 725ad7f84967..d96d7cbc8448 100644 --- a/test/python/synthesis/test_cnot_phase_synthesis.py +++ b/test/python/synthesis/test_cnot_phase_synthesis.py @@ -17,8 +17,8 @@ import ddt from qiskit.circuit import QuantumCircuit, QuantumRegister +from qiskit.circuit.library import UnitaryGate from qiskit.quantum_info.operators import Operator -from qiskit.extensions.unitary import UnitaryGate from qiskit.synthesis.linear import synth_cnot_count_full_pmh from qiskit.synthesis.linear_phase import synth_cnot_phase_aam from qiskit.transpiler.synthesis.graysynth import ( diff --git a/test/python/transpiler/test_consolidate_blocks.py b/test/python/transpiler/test_consolidate_blocks.py index 6eb78086dd9b..f228952aa213 100644 --- a/test/python/transpiler/test_consolidate_blocks.py +++ b/test/python/transpiler/test_consolidate_blocks.py @@ -18,8 +18,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit, QuantumRegister, IfElseOp -from qiskit.circuit.library import U2Gate, SwapGate, CXGate, CZGate -from qiskit.extensions import UnitaryGate +from qiskit.circuit.library import U2Gate, SwapGate, CXGate, CZGate, UnitaryGate from qiskit.converters import circuit_to_dag from qiskit.transpiler.passes import ConsolidateBlocks from qiskit.quantum_info.operators import Operator diff --git a/test/python/transpiler/test_unroll_3q_or_more.py b/test/python/transpiler/test_unroll_3q_or_more.py index 8b4b3b84efc4..744620d64fbe 100644 --- a/test/python/transpiler/test_unroll_3q_or_more.py +++ b/test/python/transpiler/test_unroll_3q_or_more.py @@ -15,13 +15,12 @@ from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Qubit, Clbit -from qiskit.circuit.library import CCXGate, RCCXGate +from qiskit.circuit.library import CCXGate, RCCXGate, UnitaryGate from qiskit.transpiler.passes import Unroll3qOrMore from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.random import random_unitary from qiskit.test import QiskitTestCase -from qiskit.extensions import UnitaryGate from qiskit.transpiler import Target diff --git a/test/python/transpiler/test_unroller.py b/test/python/transpiler/test_unroller.py index 0f8507a0104e..156a017c596e 100644 --- a/test/python/transpiler/test_unroller.py +++ b/test/python/transpiler/test_unroller.py @@ -761,7 +761,8 @@ def test_unroll_z(self): def test_unroll_snapshot(self): """test unroll snapshot""" num_qubits = self.circuit.num_qubits - instr = Snapshot("0", num_qubits=num_qubits) + with self.assertWarns(DeprecationWarning): + instr = Snapshot("0", num_qubits=num_qubits) self.circuit.append(instr, range(num_qubits)) self.ref_circuit.append(instr, range(num_qubits)) self.compare_dags() diff --git a/test/python/visualization/test_circuit_latex.py b/test/python/visualization/test_circuit_latex.py index b5f06529702a..85f6e0c7c4e6 100644 --- a/test/python/visualization/test_circuit_latex.py +++ b/test/python/visualization/test_circuit_latex.py @@ -21,8 +21,16 @@ from qiskit.visualization import circuit_drawer from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile from qiskit.providers.fake_provider import FakeTenerife -from qiskit.circuit.library import XGate, MCXGate, RZZGate, SwapGate, DCXGate, CPhaseGate -from qiskit.extensions import HamiltonianGate +from qiskit.circuit.library import ( + XGate, + MCXGate, + RZZGate, + SwapGate, + DCXGate, + CPhaseGate, + HamiltonianGate, + Isometry, +) from qiskit.circuit import Parameter, Qubit, Clbit from qiskit.circuit.library import IQP from qiskit.quantum_info.random import random_unitary @@ -242,7 +250,8 @@ def test_plot_barriers(self): # this import appears to be unused, but is actually needed to get snapshot instruction import qiskit.extensions.simulator # pylint: disable=unused-import - circuit.snapshot("sn 1") + with self.assertWarns(DeprecationWarning): + circuit.snapshot("sn 1") # check the barriers plot properly when plot_barriers= True circuit_drawer(circuit, filename=filename1, output="latex_source", plot_barriers=True) @@ -306,7 +315,7 @@ def test_big_gates(self): theta = Parameter("theta") circuit.append(HamiltonianGate(matrix, theta), [qr[1], qr[2]]) circuit = circuit.assign_parameters({theta: 1}) - circuit.isometry(np.eye(4, 4), list(range(3, 5)), []) + circuit.append(Isometry(np.eye(4, 4), 0, 0), list(range(3, 5))) circuit_drawer(circuit, filename=filename, output="latex_source") diff --git a/test/python/visualization/test_circuit_text_drawer.py b/test/python/visualization/test_circuit_text_drawer.py index 8ff1c1a8b3fe..80a5478477fb 100644 --- a/test/python/visualization/test_circuit_text_drawer.py +++ b/test/python/visualization/test_circuit_text_drawer.py @@ -30,8 +30,6 @@ from qiskit.visualization import circuit_drawer from qiskit.visualization.circuit import text as elements from qiskit.visualization.circuit.circuit_visualization import _text_circuit_drawer -from qiskit.extensions import UnitaryGate, HamiltonianGate -from qiskit.extensions.quantum_initializer import UCGate from qiskit.providers.fake_provider import FakeBelemV2 from qiskit.circuit.library import ( HGate, @@ -47,6 +45,9 @@ CU3Gate, CU1Gate, CPhaseGate, + UnitaryGate, + HamiltonianGate, + UCGate, ) from qiskit.transpiler.passes import ApplyLayout from qiskit.utils.optionals import HAS_TWEEDLEDUM diff --git a/test/randomized/test_synthesis.py b/test/randomized/test_synthesis.py index 84e8ca0b34f1..2b3e387b9baf 100644 --- a/test/randomized/test_synthesis.py +++ b/test/randomized/test_synthesis.py @@ -18,7 +18,7 @@ from qiskit import execute from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.extensions import UnitaryGate +from qiskit.circuit.library import UnitaryGate from qiskit.providers.basicaer import UnitarySimulatorPy from qiskit.quantum_info.random import random_unitary from qiskit.quantum_info.synthesis.two_qubit_decompose import ( diff --git a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py index 82a65b33e131..a60711fb77ed 100644 --- a/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py +++ b/test/visual/mpl/circuit/test_circuit_matplotlib_drawer.py @@ -36,9 +36,10 @@ SGate, U1Gate, CPhaseGate, + HamiltonianGate, + Isometry, ) from qiskit.circuit.library import MCXVChain -from qiskit.extensions import HamiltonianGate from qiskit.circuit import Parameter, Qubit, Clbit from qiskit.circuit.library import IQP from qiskit.quantum_info.random import random_unitary @@ -430,7 +431,8 @@ def test_plot_barriers(self): # this import appears to be unused, but is actually needed to get snapshot instruction import qiskit.extensions.simulator # pylint: disable=unused-import - circuit.snapshot("1") + with self.assertWarns(DeprecationWarning): + circuit.snapshot("1") # check the barriers plot properly when plot_barriers= True fname = "plot_barriers_true.png" @@ -543,7 +545,7 @@ def test_big_gates(self): theta = Parameter("theta") circuit.append(HamiltonianGate(matrix, theta), [qr[1], qr[2]]) circuit = circuit.assign_parameters({theta: 1}) - circuit.isometry(np.eye(4, 4), list(range(3, 5)), []) + circuit.append(Isometry(np.eye(4, 4), 0, 0), list(range(3, 5))) fname = "big_gates.png" self.circuit_drawer(circuit, filename=fname)