diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 2b6a2ea2a2eb..4eb46dffe72f 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -148,9 +148,9 @@ NLocal TwoLocal - RY - RYRZ - SwapRZ + RealAmplitudes + EfficientSU2 + ExcitationPreserving Data encoding circuits @@ -159,9 +159,10 @@ .. autosummary:: :toctree: ../stubs/ - PauliExpansion - FirstOrderExpansion - SecondOrderExpansion + PauliFeatureMap + ZFeatureMap + ZZFeatureMap + """ from .standard_gates import * @@ -195,14 +196,14 @@ from .n_local import ( NLocal, TwoLocal, - RY, - RYRZ, - SwapRZ, + RealAmplitudes, + EfficientSU2, + ExcitationPreserving, ) -from .data_encoding import ( - PauliExpansion, - FirstOrderExpansion, - SecondOrderExpansion +from .data_preparation import ( + PauliFeatureMap, + ZFeatureMap, + ZZFeatureMap ) from .quantum_volume import QuantumVolume from .fourier_checking import FourierChecking diff --git a/qiskit/circuit/library/data_encoding/__init__.py b/qiskit/circuit/library/data_preparation/__init__.py similarity index 89% rename from qiskit/circuit/library/data_encoding/__init__.py rename to qiskit/circuit/library/data_preparation/__init__.py index f29e62eadd22..422a70cfc209 100644 --- a/qiskit/circuit/library/data_encoding/__init__.py +++ b/qiskit/circuit/library/data_preparation/__init__.py @@ -40,12 +40,12 @@ """ -from .pauli_expansion import PauliExpansion -from .first_order_expansion import FirstOrderExpansion -from .second_order_expansion import SecondOrderExpansion +from .pauli_feature_map import PauliFeatureMap +from .z_feature_map import ZFeatureMap +from .zz_feature_map import ZZFeatureMap __all__ = [ - 'PauliExpansion', - 'FirstOrderExpansion', - 'SecondOrderExpansion', + 'PauliFeatureMap', + 'ZFeatureMap', + 'ZZFeatureMap', ] diff --git a/qiskit/circuit/library/data_encoding/pauli_expansion.py b/qiskit/circuit/library/data_preparation/pauli_feature_map.py similarity index 95% rename from qiskit/circuit/library/data_encoding/pauli_expansion.py rename to qiskit/circuit/library/data_preparation/pauli_feature_map.py index 29ad762c048c..475fdc49fc68 100644 --- a/qiskit/circuit/library/data_encoding/pauli_expansion.py +++ b/qiskit/circuit/library/data_preparation/pauli_feature_map.py @@ -25,7 +25,7 @@ from ..n_local.n_local import NLocal -class PauliExpansion(NLocal): +class PauliFeatureMap(NLocal): r"""The Pauli Expansion circuit. The Pauli Expansion circuit is a data encoding circuit that transforms input data @@ -60,13 +60,13 @@ class PauliExpansion(NLocal): ┤ H ├┤ U1(2.0*x[1]) ├┤ RX(pi/2) ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├┤ RX(-pi/2) ├ └───┘└──────────────┘└──────────┘└───┘└─────────────────────────────────┘└───┘└───────────┘ - Please refer to :class:`FirstOrderExpansion` for the case :math:`k = 1`, :math:`P_0 = Z` - and to :class:`SecondOrderExpansion` for the case :math:`k = 2`, :math:`P_0 = Z` and + Please refer to :class:`ZFeatureMap` for the case :math:`k = 1`, :math:`P_0 = Z` + and to :class:`ZZFeatureMap` for the case :math:`k = 2`, :math:`P_0 = Z` and :math:`P_{0,1} = ZZ`. Examples: - >>> prep = PauliExpansion(2, reps=1, paulis=['ZZ']) + >>> prep = PauliFeatureMap(2, reps=1, paulis=['ZZ']) >>> print(prep) ┌───┐ q_0: ┤ H ├──■───────────────────────────────────────■── @@ -74,7 +74,7 @@ class PauliExpansion(NLocal): q_1: ┤ H ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├ └───┘└───┘└─────────────────────────────────┘└───┘ - >>> prep = PauliExpansion(2, reps=1, paulis=['Z', 'XX']) + >>> prep = PauliFeatureMap(2, reps=1, paulis=['Z', 'XX']) >>> print(prep) ┌───┐┌──────────────┐┌───┐ ┌───┐ q_0: ┤ H ├┤ U1(2.0*x[0]) ├┤ H ├──■───────────────────────────────────────■──┤ H ├ @@ -82,7 +82,7 @@ class PauliExpansion(NLocal): q_1: ┤ H ├┤ U1(2.0*x[1]) ├┤ H ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├┤ H ├ └───┘└──────────────┘└───┘└───┘└─────────────────────────────────┘└───┘└───┘ - >>> prep = PauliExpansion(2, reps=1, paulis=['ZY']) + >>> prep = PauliFeatureMap(2, reps=1, paulis=['ZY']) >>> print(prep) ┌───┐┌──────────┐ ┌───────────┐ q_0: ┤ H ├┤ RX(pi/2) ├──■───────────────────────────────────────■──┤ RX(-pi/2) ├ @@ -90,9 +90,9 @@ class PauliExpansion(NLocal): q_1: ┤ H ├────────────┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├───────────── └───┘ └───┘└─────────────────────────────────┘└───┘ - >>> from qiskit.circuit.library import RYRZ - >>> prep = PauliExpansion(3, reps=3, paulis=['Z', 'YY', 'ZXZ']) - >>> wavefunction = RYRZ(3, entanglement_blocks='cx') + >>> from qiskit.circuit.library import EfficientSU2 + >>> prep = PauliFeatureMap(3, reps=3, paulis=['Z', 'YY', 'ZXZ']) + >>> wavefunction = EfficientSU2(3) >>> classifier = prep.compose(wavefunction >>> classifier.num_parameters 27 diff --git a/qiskit/circuit/library/data_encoding/first_order_expansion.py b/qiskit/circuit/library/data_preparation/z_feature_map.py similarity index 94% rename from qiskit/circuit/library/data_encoding/first_order_expansion.py rename to qiskit/circuit/library/data_preparation/z_feature_map.py index c4e6866050bd..6367840cd4fa 100644 --- a/qiskit/circuit/library/data_encoding/first_order_expansion.py +++ b/qiskit/circuit/library/data_preparation/z_feature_map.py @@ -17,10 +17,10 @@ from typing import Callable, Optional import numpy as np -from .pauli_expansion import PauliExpansion +from .pauli_feature_map import PauliFeatureMap -class FirstOrderExpansion(PauliExpansion): +class ZFeatureMap(PauliFeatureMap): """The first order Pauli Z-evolution circuit. On 3 qubits and with 2 repetitions the circuit is represented by: @@ -35,13 +35,13 @@ class FirstOrderExpansion(PauliExpansion): ┤ H ├┤ U1(2.0*x[2]) ├┤ H ├┤ U1(2.0*x[2]) ├ └───┘└──────────────┘└───┘└──────────────┘ - This is a sub-class of :class:`~qiskit.circuit.library.PauliExpansion` where the Pauli + This is a sub-class of :class:`~qiskit.circuit.library.PauliFeatureMap` where the Pauli strings are fixed as `['Z']`. As a result the first order expansion will be a circuit without entangling gates. Examples: - >>> prep = FirstOrderExpansion(3, reps=3, insert_barriers=True) + >>> prep = ZFeatureMap(3, reps=3, insert_barriers=True) >>> print(prep) ┌───┐ ░ ┌──────────────┐ ░ ┌───┐ ░ ┌──────────────┐ ░ ┌───┐ ░ ┌──────────────┐ q_0: ┤ H ├─░─┤ U1(2.0*x[0]) ├─░─┤ H ├─░─┤ U1(2.0*x[0]) ├─░─┤ H ├─░─┤ U1(2.0*x[0]) ├ @@ -52,7 +52,7 @@ class FirstOrderExpansion(PauliExpansion): └───┘ ░ └──────────────┘ ░ └───┘ ░ └──────────────┘ ░ └───┘ ░ └──────────────┘ >>> data_map = lambda x: x[0]*x[0] + 1 # note: input is an array - >>> prep = FirstOrderExpansion(3, reps=1, data_map_func=data_map) + >>> prep = ZFeatureMap(3, reps=1, data_map_func=data_map) >>> print(prep) ┌───┐┌───────────────────────┐ q_0: ┤ H ├┤ U1(2.0*x[0]**2 + 2.0) ├ @@ -62,7 +62,7 @@ class FirstOrderExpansion(PauliExpansion): q_2: ┤ H ├┤ U1(2.0*x[2]**2 + 2.0) ├ └───┘└───────────────────────┘ - >>> classifier = FirstOrderExpansion(3, reps=1) + RY(3, reps=1) + >>> classifier = ZFeatureMap(3, reps=1) + RY(3, reps=1) >>> print(classifier) ┌───┐┌──────────────┐┌──────────┐ ┌──────────┐ q_0: ┤ H ├┤ U1(2.0*x[0]) ├┤ RY(θ[0]) ├─■──■─┤ RY(θ[3]) ├──────────── diff --git a/qiskit/circuit/library/data_encoding/second_order_expansion.py b/qiskit/circuit/library/data_preparation/zz_feature_map.py similarity index 94% rename from qiskit/circuit/library/data_encoding/second_order_expansion.py rename to qiskit/circuit/library/data_preparation/zz_feature_map.py index 794175ea6459..8f3e961cb957 100644 --- a/qiskit/circuit/library/data_encoding/second_order_expansion.py +++ b/qiskit/circuit/library/data_preparation/zz_feature_map.py @@ -16,10 +16,10 @@ from typing import Callable, List, Union, Optional import numpy as np -from .pauli_expansion import PauliExpansion +from .pauli_feature_map import PauliFeatureMap -class SecondOrderExpansion(PauliExpansion): +class ZZFeatureMap(PauliFeatureMap): """Second-order Pauli-Z evolution circuit. For 3 qubits and 1 repetition and linear entanglement the circuit is represented by: @@ -39,7 +39,7 @@ class SecondOrderExpansion(PauliExpansion): Examples: - >>> prep = SecondOrderExpansion(2, reps=2) + >>> prep = ZZFeatureMap(2, reps=2) >>> print(prep) ┌───┐┌──────────────┐ q_0: ┤ H ├┤ U1(2.0*x[0]) ├──■───────────────────────────────────────■── @@ -47,11 +47,11 @@ class SecondOrderExpansion(PauliExpansion): q_1: ┤ H ├┤ U1(2.0*x[1]) ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├ └───┘└──────────────┘└───┘└─────────────────────────────────┘└───┘ - >>> from qiskit.circuit.library import RYRZ - >>> classifier = SecondOrderExpansion(3) + RYRZ(3) + >>> from qiskit.circuit.library import EfficientSU2 + >>> classifier = ZZFeatureMap(3) + EfficientSU2(3) >>> classifier.num_parameters 15 - >>> classifier.parameters # 'x' for the data preparation, 'θ' for the RYRZ parameters + >>> classifier.parameters # 'x' for the data preparation, 'θ' for the SU2 parameters {Parameter(θ[9]), Parameter(θ[4]), Parameter(θ[6]), Parameter(θ[1]), Parameter(x[2]), Parameter(θ[7]), Parameter(x[1]), Parameter(θ[8]), Parameter(θ[2]), Parameter(θ[10]), Parameter(θ[5]), Parameter(θ[0]), Parameter(θ[3]), Parameter(x[0]), Parameter(θ[11])} diff --git a/qiskit/circuit/library/n_local/__init__.py b/qiskit/circuit/library/n_local/__init__.py index fe251047cf22..2a5e3c57572f 100644 --- a/qiskit/circuit/library/n_local/__init__.py +++ b/qiskit/circuit/library/n_local/__init__.py @@ -16,14 +16,14 @@ from .n_local import NLocal from .two_local import TwoLocal -from .ry import RY -from .ryrz import RYRZ -from .swaprz import SwapRZ +from .real_amplitudes import RealAmplitudes +from .efficient_su2 import EfficientSU2 +from .excitation_preserving import ExcitationPreserving __all__ = [ 'NLocal', 'TwoLocal', - 'RY', - 'RYRZ', - 'SwapRZ' + 'RealAmplitudes', + 'EfficientSU2', + 'ExcitationPreserving' ] diff --git a/qiskit/circuit/library/n_local/efficient_su2.py b/qiskit/circuit/library/n_local/efficient_su2.py new file mode 100644 index 000000000000..d09852a7d13c --- /dev/null +++ b/qiskit/circuit/library/n_local/efficient_su2.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 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 EfficientSU2 2-local circuit.""" + +from typing import Union, Optional, List, Tuple, Callable, Any +from numpy import pi + +from qiskit.circuit import QuantumCircuit, Instruction +from qiskit.circuit.library.standard_gates import RYGate, RZGate, CXGate +from .two_local import TwoLocal + + +class EfficientSU2(TwoLocal): + r"""The hardware efficient SU(2) 2-local circuit. + + The ``EfficientSU2`` circuit consists of layers of single qubit operations spanned by SU(2) + and :math:`CX` entanglements. This is a heuristic pattern that can be used to prepare trial wave + functions for variational quantum algorithms or classification circuit for machine learning. + + SU(2) stands for special unitary group of degree 2, its elements are :math:`2 \times 2` + unitary matrices with determinant 1, such as the Pauli rotation gates. + + On 3 qubits and using the Pauli :math:`Y` and :math:`Z` su2_gates as single qubit gates, the + hardware efficient SU(2) circuit is represented by: + + .. parsed-literal:: + + ┌──────────┐┌──────────┐ ░ ░ ░ ┌───────────┐┌───────────┐ + ┤ RY(θ[0]) ├┤ RZ(θ[3]) ├─░───■────■────────░─ ... ─░─┤ RY(θ[12]) ├┤ RZ(θ[15]) ├ + ├──────────┤├──────────┤ ░ ┌─┴─┐ │ ░ ░ ├───────────┤├───────────┤ + ┤ RY(θ[1]) ├┤ RZ(θ[4]) ├─░─┤ X ├──┼────■───░─ ... ─░─┤ RY(θ[13]) ├┤ RZ(θ[16]) ├ + ├──────────┤├──────────┤ ░ └───┘┌─┴─┐┌─┴─┐ ░ ░ ├───────────┤├───────────┤ + ┤ RY(θ[2]) ├┤ RZ(θ[5]) ├─░──────┤ X ├┤ X ├─░─ ... ─░─┤ RY(θ[14]) ├┤ RZ(θ[17]) ├ + └──────────┘└──────────┘ ░ └───┘└───┘ ░ ░ └───────────┘└───────────┘ + + See :class:`~qiskit.circuit.library.RealAmplitudes` for more detail on the possible arguments + and options such as skipping unentanglement qubits, which apply here too. + + Examples: + + >>> circuit = EfficientSU2(3, reps=1) + >>> print(circuit) + ┌──────────┐┌──────────┐ ┌──────────┐┌──────────┐ + q_0: ┤ RY(θ[0]) ├┤ RZ(θ[3]) ├──■────■──┤ RY(θ[6]) ├┤ RZ(θ[9]) ├───────────── + ├──────────┤├──────────┤┌─┴─┐ │ └──────────┘├──────────┤┌───────────┐ + q_1: ┤ RY(θ[1]) ├┤ RZ(θ[4]) ├┤ X ├──┼───────■──────┤ RY(θ[7]) ├┤ RZ(θ[10]) ├ + ├──────────┤├──────────┤└───┘┌─┴─┐ ┌─┴─┐ ├──────────┤├───────────┤ + q_2: ┤ RY(θ[2]) ├┤ RZ(θ[5]) ├─────┤ X ├───┤ X ├────┤ RY(θ[8]) ├┤ RZ(θ[11]) ├ + └──────────┘└──────────┘ └───┘ └───┘ └──────────┘└───────────┘ + + >>> ansatz = EfficientSU2(4, su2_gates=['rx', 'y'], entanglement='circular', reps=1) + >>> qc = QuantumCircuit(4) # create a circuit and append the RY variational form + >>> qc.compose(ansatz, inplace=True) + >>> qc.draw() + ┌──────────┐┌───┐┌───┐ ┌──────────┐ ┌───┐ + q_0: ┤ RX(θ[0]) ├┤ Y ├┤ X ├──■──┤ RX(θ[4]) ├───┤ Y ├───────────────────── + ├──────────┤├───┤└─┬─┘┌─┴─┐└──────────┘┌──┴───┴───┐ ┌───┐ + q_1: ┤ RX(θ[1]) ├┤ Y ├──┼──┤ X ├─────■──────┤ RX(θ[5]) ├───┤ Y ├───────── + ├──────────┤├───┤ │ └───┘ ┌─┴─┐ └──────────┘┌──┴───┴───┐┌───┐ + q_2: ┤ RX(θ[2]) ├┤ Y ├──┼──────────┤ X ├─────────■──────┤ RX(θ[6]) ├┤ Y ├ + ├──────────┤├───┤ │ └───┘ ┌─┴─┐ ├──────────┤├───┤ + q_3: ┤ RX(θ[3]) ├┤ Y ├──■──────────────────────┤ X ├────┤ RX(θ[7]) ├┤ Y ├ + └──────────┘└───┘ └───┘ └──────────┘└───┘ + + """ + + def __init__(self, + num_qubits: Optional[int] = None, + su2_gates: Optional[Union[ + str, type, Instruction, QuantumCircuit, + List[Union[str, type, Instruction, QuantumCircuit]] + ]] = None, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', + reps: int = 3, + skip_unentangled_qubits: bool = False, + skip_final_rotation_layer: bool = False, + parameter_prefix: str = 'θ', + insert_barriers: bool = False, + initial_state: Optional[Any] = None, + ) -> None: + """Create a new EfficientSU2 2-local circuit. + + Args: + num_qubits: The number of qubits of the EfficientSU2 circuit. + reps: Specifies how often the structure of a rotation layer followed by an entanglement + layer is repeated. + su2_gates: The SU(2) single qubit gates to apply in single qubit gate layers. + If only one gate is provided, the same gate is applied to each qubit. + If a list of gates is provided, all gates are applied to each qubit in the provided + order. + entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear' + , 'circular' or 'sca'), a list of integer-pairs specifying the indices of qubits + entangled with one another, or a callable returning such a list provided with + the index of the entanglement layer. + See the Examples section of :class:`~qiskit.circuit.library.TwoLocal` for more + detail. + initial_state: An `InitialState` object to prepend to the circuit. + skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits + that are entangled with another qubit. If False, the single qubit gates are applied + to each qubit in the Ansatz. Defaults to False. + skip_final_rotation_layer: If False, a rotation layer is added at the end of the + ansatz. If True, no rotation layer is added. + parameter_prefix: The parameterized gates require a parameter to be defined, for which + we use :class:`~qiskit.circuit.ParameterVector`. + insert_barriers: If True, barriers are inserted in between each layer. If False, + no barriers are inserted. + + """ + if su2_gates is None: + su2_gates = [RYGate, RZGate] + super().__init__(num_qubits=num_qubits, + rotation_blocks=su2_gates, + entanglement_blocks=CXGate, + entanglement=entanglement, + reps=reps, + skip_unentangled_qubits=skip_unentangled_qubits, + skip_final_rotation_layer=skip_final_rotation_layer, + parameter_prefix=parameter_prefix, + insert_barriers=insert_barriers, + initial_state=initial_state) + + @property + def parameter_bounds(self) -> List[Tuple[float, float]]: + """Return the parameter bounds. + + Returns: + The parameter bounds. + """ + return self.num_parameters * [(-pi, pi)] diff --git a/qiskit/circuit/library/n_local/swaprz.py b/qiskit/circuit/library/n_local/excitation_preserving.py similarity index 63% rename from qiskit/circuit/library/n_local/swaprz.py rename to qiskit/circuit/library/n_local/excitation_preserving.py index 894d59c709bf..a9396ac9e62b 100644 --- a/qiskit/circuit/library/n_local/swaprz.py +++ b/qiskit/circuit/library/n_local/excitation_preserving.py @@ -12,7 +12,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""The SwapRZ 2-local circuit.""" +"""The ExcitationPreserving 2-local circuit.""" from typing import Union, Optional, List, Tuple, Callable, Any from numpy import pi @@ -22,42 +22,43 @@ from .two_local import TwoLocal -class SwapRZ(TwoLocal): - r"""The SwapRZ ansatz. +class ExcitationPreserving(TwoLocal): + r"""The heurisitic excitation-preserving wave function ansatz. - This trial wave function is layers of :math:`Z` rotations with entanglements using 2-qubit - :math:`XX+YY` rotations. It was designed principally to be a particle-preserving wave function - for :mod:`qiskit.chemistry`. Given an initial state as a set of 1's and 0's it will preserve - the number of 1's - where for chemistry a 1 will indicate a particle. + The ``ExcitationPreserving`` circuit preserves the ratio of :math:`|00\rangle`, + :math:`|01\rangle + |10\rangle` and :math:`|11\rangle` states. The matrix representing + the operation is - Note: - - In chemistry, to define the particles for SwapRZ, use a - :class:`~qiskit.chemistry.components.initial_states.HartreeFock` initial state with - the `Jordan-Wigner` qubit mapping + .. math:: - See :class:`~qiskit.circuit.library.RY` for more detail on the possible arguments and options - such as skipping unentanglement qubits, which apply here too. + \newcommand{\th}{\theta/2} - The rotations of the SwapRZ ansatz can be written as + \begin{pmatrix} + 1 & 0 & 0 & 0 \\ + 0 & \cos(\th) & -\sin(\th) & 0 \\ + 0 & \sin(\th) & \cos(\th) & 0 \\ + 0 & 0 & 0 e^{-i\phi} + \end{pmatrix} - .. math:: - R_Z(\theta) = e^{-i \theta Z} + for the mode ``'fsim'`` or with :math:`e^{-i\phi} = 1` for the mode ``'iswap'``. - and + Note that other wave functions, such as UCC-ansatzes, are also excitation preserving. + However these can become complex quickly, while this heuristically motivated circuit follows + a simpler pattern. - .. math:: + This trial wave function consists of layers of :math:`Z` rotations with 2-qubit entanglements. + The entangling is creating using :math:`XX+YY` rotations and optionally a controlled-phase + gate for the mode ``'fsim'``. - R_{XX+YY}(\theta) = e^{-i \theta / 2 (X \otimes X + Y \otimes Y)} - \approx e^{-i \theta / 2 X \otimes X} e^{-i \theta /2 Y \otimes Y } - = R_{XX}(\theta) R_{YY}(\theta) + See :class:`~qiskit.circuit.library.RealAmplitudes` for more detail on the possible arguments + and options such as skipping unentanglement qubits, which apply here too. - where the approximation used comes from the Trotter expansion of the sum in the exponential. + The rotations of the ExcitationPreserving ansatz can be written as Examples: - >>> swaprz = SwapRZ(3, reps=1, insert_barriers=True, entanglement='linear') - >>> print(swaprz) # show the circuit + >>> ansatz = ExcitationPreserving(3, reps=1, insert_barriers=True, entanglement='linear') + >>> print(ansatz) # show the circuit ┌──────────┐ ░ ┌────────────┐┌────────────┐ ░ ┌──────────┐ q_0: ┤ RZ(θ[0]) ├─░─┤0 ├┤0 ├─────────────────────────────░─┤ RZ(θ[5]) ├ ├──────────┤ ░ │ RXX(θ[3]) ││ RYY(θ[3]) │┌────────────┐┌────────────┐ ░ ├──────────┤ @@ -66,10 +67,10 @@ class SwapRZ(TwoLocal): q_2: ┤ RZ(θ[2]) ├─░─────────────────────────────┤1 ├┤1 ├─░─┤ RZ(θ[7]) ├ └──────────┘ ░ └────────────┘└────────────┘ ░ └──────────┘ - >>> swaprz = SwapRZ(2, reps=1) + >>> ansatz = ExcitationPreserving(2, reps=1) >>> qc = QuantumCircuit(2) # create a circuit and append the RY variational form >>> qc.cry(0.2, 0, 1) # do some previous operation - >>> qc.compose(swaprz, inplace=True) # add the swaprz + >>> qc.compose(ansatz, inplace=True) # add the swaprz >>> qc.draw() ┌──────────┐┌────────────┐┌────────────┐┌──────────┐ q_0: ─────■─────┤ RZ(θ[0]) ├┤0 ├┤0 ├┤ RZ(θ[3]) ├ @@ -77,10 +78,21 @@ class SwapRZ(TwoLocal): q_1: ┤ RY(0.2) ├┤ RZ(θ[1]) ├┤1 ├┤1 ├┤ RZ(θ[4]) ├ └─────────┘└──────────┘└────────────┘└────────────┘└──────────┘ + >>> ansatz = ExcitationPreserving(3, reps=1, mode='fsim', entanglement=[[0,2]], + ... insert_barriers=True) + >>> print(ansatz) + ┌──────────┐ ░ ┌────────────┐┌────────────┐ ░ ┌──────────┐ + q_0: ┤ RZ(θ[0]) ├─░─┤0 ├┤0 ├─■──────░─┤ RZ(θ[5]) ├ + ├──────────┤ ░ │ ││ │ │ ░ ├──────────┤ + q_1: ┤ RZ(θ[1]) ├─░─┤ RXX(θ[3]) ├┤ RYY(θ[3]) ├─┼──────░─┤ RZ(θ[6]) ├ + ├──────────┤ ░ │ ││ │ │θ[4] ░ ├──────────┤ + q_2: ┤ RZ(θ[2]) ├─░─┤1 ├┤1 ├─■──────░─┤ RZ(θ[7]) ├ + └──────────┘ ░ └────────────┘└────────────┘ ░ └──────────┘ """ def __init__(self, num_qubits: Optional[int] = None, + mode: str = 'iswap', entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', reps: int = 3, skip_unentangled_qubits: bool = False, @@ -89,10 +101,11 @@ def __init__(self, insert_barriers: bool = False, initial_state: Optional[Any] = None, ) -> None: - """Create a new SwapRZ 2-local circuit. + """Create a new ExcitationPreserving 2-local circuit. Args: - num_qubits: The number of qubits of the SwapRZ circuit. + num_qubits: The number of qubits of the ExcitationPreserving circuit. + mode: aa reps: Specifies how often the structure of a rotation layer followed by an entanglement layer is repeated. entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear' @@ -115,16 +128,24 @@ def __init__(self, insert_barriers: If True, barriers are inserted in between each layer. If False, no barriers are inserted. + Raises: + ValueError: If the selected mode is not supported. """ + supported_modes = ['iswap', 'fsim'] + if mode not in supported_modes: + raise ValueError('Unsupported mode {}, choose one of {}'.format(mode, supported_modes)) theta = Parameter('θ') - rxxyy = QuantumCircuit(2, name='Rxx+yy') - rxxyy.rxx(theta, 0, 1) - rxxyy.ryy(theta, 0, 1) + swap = QuantumCircuit(2, name='Interaction') + swap.rxx(theta, 0, 1) + swap.ryy(theta, 0, 1) + if mode == 'fsim': + phi = Parameter('φ') + swap.cu1(phi, 0, 1) super().__init__(num_qubits=num_qubits, rotation_blocks=RZGate, - entanglement_blocks=rxxyy, + entanglement_blocks=swap, entanglement=entanglement, reps=reps, skip_unentangled_qubits=skip_unentangled_qubits, diff --git a/qiskit/circuit/library/n_local/real_amplitudes.py b/qiskit/circuit/library/n_local/real_amplitudes.py new file mode 100644 index 000000000000..c47405c6749f --- /dev/null +++ b/qiskit/circuit/library/n_local/real_amplitudes.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 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 real-amplitudes 2-local circuit.""" + +from typing import Union, Optional, List, Tuple, Callable, Any +import numpy as np + +from qiskit.circuit.library.standard_gates import RYGate, CXGate +from .two_local import TwoLocal + + +class RealAmplitudes(TwoLocal): + r"""The RealAmplitudes 2-local circuit. + + The ``RealAmplitudes`` circuit is a heuristic trial wave function used as Ansatz in chemistry + applications or classification circuits in machine learning. The circuit consists of + of alternating layers of :math:`Y` rotations and :math:`CX` entanglements. The entanglement + pattern can be user-defined or selected from a predefined set. + + For example a ``RealAmplitudes`` circuit with 2 repetitions on 3 qubits with ``'full'`` + entanglement is + + .. parsed-literal:: + + ┌──────────┐ ░ ░ ┌──────────┐ ░ ░ ┌──────────┐ + ┤ RY(θ[0]) ├─░───■────■────────░─┤ RY(θ[3]) ├─░───■────■────────░─┤ RY(θ[6]) ├ + ├──────────┤ ░ ┌─┴─┐ │ ░ ├──────────┤ ░ ┌─┴─┐ │ ░ ├──────────┤ + ┤ RY(θ[1]) ├─░─┤ X ├──┼────■───░─┤ RY(θ[4]) ├─░─┤ X ├──┼────■───░─┤ RY(θ[7]) ├ + ├──────────┤ ░ └───┘┌─┴─┐┌─┴─┐ ░ ├──────────┤ ░ └───┘┌─┴─┐┌─┴─┐ ░ ├──────────┤ + ┤ RY(θ[2]) ├─░──────┤ X ├┤ X ├─░─┤ RY(θ[5]) ├─░──────┤ X ├┤ X ├─░─┤ RY(θ[8]) ├ + └──────────┘ ░ └───┘└───┘ ░ └──────────┘ ░ └───┘└───┘ ░ └──────────┘ + + The entanglement can be set using the ``entanglement`` keyword as string or a list of + index-pairs. See the documentation of :class:`~qiskit.circuit.library.TwoLocal` and + :class:`~qiskit.circuit.NLocal` for more detail. Additional options that can be set include the + number of repetitions, skipping rotation gates on qubits that are not entangled, leaving out + the final rotation layer and inserting barriers in between the rotation and entanglement + layers. + + If some qubits are not entangled with other qubits it makes sense to not apply rotation gates + on these qubits, since a sequence of :math:`Y` rotations can be reduced to a single :math:`Y` + rotation with summed rotation angles. + + Examples: + + >>> ansatz = RealAmplitudes(3, reps=2) # create the circuit on 3 qubits + >>> print(ansatz) + ┌──────────┐ ┌──────────┐ ┌──────────┐ + q_0: ┤ RY(θ[0]) ├──■────■──┤ RY(θ[3]) ├──────────────■────■──┤ RY(θ[6]) ├──────────── + ├──────────┤┌─┴─┐ │ └──────────┘┌──────────┐┌─┴─┐ │ └──────────┘┌──────────┐ + q_1: ┤ RY(θ[1]) ├┤ X ├──┼───────■──────┤ RY(θ[4]) ├┤ X ├──┼───────■──────┤ RY(θ[7]) ├ + ├──────────┤└───┘┌─┴─┐ ┌─┴─┐ ├──────────┤└───┘┌─┴─┐ ┌─┴─┐ ├──────────┤ + q_2: ┤ RY(θ[2]) ├─────┤ X ├───┤ X ├────┤ RY(θ[5]) ├─────┤ X ├───┤ X ├────┤ RY(θ[8]) ├ + └──────────┘ └───┘ └───┘ └──────────┘ └───┘ └───┘ └──────────┘ + + >>> ansatz = RealAmplitudes(3, entanglement='linear', reps=2, insert_barriers=True) + >>> qc = QuantumCircuit(3) # create a circuit and append the RY variational form + >>> qc.compose(ansatz, inplace=True) + >>> qc.draw() + ┌──────────┐ ░ ░ ┌──────────┐ ░ ░ ┌──────────┐ + q_0: ┤ RY(θ[0]) ├─░───■────────░─┤ RY(θ[3]) ├─░───■────────░─┤ RY(θ[6]) ├ + ├──────────┤ ░ ┌─┴─┐ ░ ├──────────┤ ░ ┌─┴─┐ ░ ├──────────┤ + q_1: ┤ RY(θ[1]) ├─░─┤ X ├──■───░─┤ RY(θ[4]) ├─░─┤ X ├──■───░─┤ RY(θ[7]) ├ + ├──────────┤ ░ └───┘┌─┴─┐ ░ ├──────────┤ ░ └───┘┌─┴─┐ ░ ├──────────┤ + q_2: ┤ RY(θ[2]) ├─░──────┤ X ├─░─┤ RY(θ[5]) ├─░──────┤ X ├─░─┤ RY(θ[8]) ├ + └──────────┘ ░ └───┘ ░ └──────────┘ ░ └───┘ ░ └──────────┘ + + >>> ansatz = RealAmplitudes(4, reps=1, entanglement='circular', insert_barriers=True) + >>> print(ansatz) + ┌──────────┐ ░ ┌───┐ ░ ┌──────────┐ + q_0: ┤ RY(θ[0]) ├─░─┤ X ├──■─────────────░─┤ RY(θ[4]) ├ + ├──────────┤ ░ └─┬─┘┌─┴─┐ ░ ├──────────┤ + q_1: ┤ RY(θ[1]) ├─░───┼──┤ X ├──■────────░─┤ RY(θ[5]) ├ + ├──────────┤ ░ │ └───┘┌─┴─┐ ░ ├──────────┤ + q_2: ┤ RY(θ[2]) ├─░───┼───────┤ X ├──■───░─┤ RY(θ[6]) ├ + ├──────────┤ ░ │ └───┘┌─┴─┐ ░ ├──────────┤ + q_3: ┤ RY(θ[3]) ├─░───■────────────┤ X ├─░─┤ RY(θ[7]) ├ + └──────────┘ ░ └───┘ ░ └──────────┘ + + >>> ansatz = RealAmplitudes(4, reps=2, entanglement=[[0,3], [0,2]], + ... skip_unentangled_qubits=True) + >>> print(ansatz) + ┌──────────┐ ┌──────────┐ ┌──────────┐ + q_0: ┤ RY(θ[0]) ├──■───────■──────┤ RY(θ[3]) ├──■───────■──────┤ RY(θ[6]) ├ + └──────────┘ │ │ └──────────┘ │ │ └──────────┘ + q_1: ──────────────┼───────┼────────────────────┼───────┼────────────────── + ┌──────────┐ │ ┌─┴─┐ ┌──────────┐ │ ┌─┴─┐ ┌──────────┐ + q_2: ┤ RY(θ[1]) ├──┼─────┤ X ├────┤ RY(θ[4]) ├──┼─────┤ X ├────┤ RY(θ[7]) ├ + ├──────────┤┌─┴─┐┌──┴───┴───┐└──────────┘┌─┴─┐┌──┴───┴───┐└──────────┘ + q_3: ┤ RY(θ[2]) ├┤ X ├┤ RY(θ[5]) ├────────────┤ X ├┤ RY(θ[8]) ├──────────── + └──────────┘└───┘└──────────┘ └───┘└──────────┘ + + """ + + def __init__(self, + num_qubits: Optional[int] = None, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', + reps: int = 3, + skip_unentangled_qubits: bool = False, + skip_final_rotation_layer: bool = False, + parameter_prefix: str = 'θ', + insert_barriers: bool = False, + initial_state: Optional[Any] = None, + ) -> None: + """Create a new RealAmplitudes 2-local circuit. + + Args: + num_qubits: The number of qubits of the RealAmplitudes circuit. + reps: Specifies how often the structure of a rotation layer followed by an entanglement + layer is repeated. + entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear' + or 'sca'), a list of integer-pairs specifying the indices of qubits + entangled with one another, or a callable returning such a list provided with + the index of the entanglement layer. + See the Examples section of :class:`~qiskit.circuit.library.TwoLocal` for more + detail. + initial_state: An `InitialState` object to prepend to the circuit. + skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits + that are entangled with another qubit. If False, the single qubit gates are applied + to each qubit in the Ansatz. Defaults to False. + skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits + that are entangled with another qubit. If False, the single qubit gates are applied + to each qubit in the Ansatz. Defaults to False. + skip_final_rotation_layer: If False, a rotation layer is added at the end of the + ansatz. If True, no rotation layer is added. + parameter_prefix: The parameterized gates require a parameter to be defined, for which + we use :class:`~qiskit.circuit.ParameterVector`. + insert_barriers: If True, barriers are inserted in between each layer. If False, + no barriers are inserted. + + """ + super().__init__(num_qubits=num_qubits, + reps=reps, + rotation_blocks=RYGate, + entanglement_blocks=CXGate, + entanglement=entanglement, + initial_state=initial_state, + skip_unentangled_qubits=skip_unentangled_qubits, + skip_final_rotation_layer=skip_final_rotation_layer, + parameter_prefix=parameter_prefix, + insert_barriers=insert_barriers) + + @property + def parameter_bounds(self) -> List[Tuple[float, float]]: + """Return the parameter bounds. + + Returns: + The parameter bounds. + """ + return self.num_parameters * [(-np.pi, np.pi)] diff --git a/qiskit/circuit/library/n_local/ry.py b/qiskit/circuit/library/n_local/ry.py deleted file mode 100644 index e26ccc9c9cf9..000000000000 --- a/qiskit/circuit/library/n_local/ry.py +++ /dev/null @@ -1,178 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 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 RY 2-local circuit.""" - -from typing import Union, Optional, List, Tuple, Callable, Any -import numpy as np - -from qiskit.circuit import QuantumCircuit, Instruction -from qiskit.circuit.library.standard_gates import RYGate, CZGate -from .two_local import TwoLocal - - -class RY(TwoLocal): - r"""The RY 2-local circuit. - - The RY trial wave function consistes of alternating layers of :math:`Y` rotations - and entanglements. The entanglements are usually realized using CZ or CX gates. - - For example a RY circuit with 2 repetitions on 3 qubits with full CZ entanglement is - - .. parsed-literal:: - - ┌──────────┐ ░ ░ ┌──────────┐ ░ ░ ┌──────────┐ - ┤ RY(θ[0]) ├─░──■──■─────░─┤ RY(θ[3]) ├─░──■──■─────░─┤ RY(θ[6]) ├ - ├──────────┤ ░ │ │ ░ ├──────────┤ ░ │ │ ░ ├──────────┤ - ┤ RY(θ[1]) ├─░──■──┼──■──░─┤ RY(θ[4]) ├─░──■──┼──■──░─┤ RY(θ[7]) ├ - ├──────────┤ ░ │ │ ░ ├──────────┤ ░ │ │ ░ ├──────────┤ - ┤ RY(θ[2]) ├─░─────■──■──░─┤ RY(θ[5]) ├─░─────■──■──░─┤ RY(θ[8]) ├ - └──────────┘ ░ ░ └──────────┘ ░ ░ └──────────┘ - - - The qubits can be entangled using different structures. This can be set using the - ``entanglement`` keyword as string or a list of index-pairs, see the documentation of - :class:`~qiskit.circuit.library.TwoLocal` and :class:`~qiskit.circuit.NLocal` for more detail. - - Additional options that can be set include skipping rotation gates on qubits that are not - entangled, leaving out the final rotation layer and inserting barriers in between the - rotation and entanglement layers. - - When all qubits are entangled and the entanglement gates themselves have no additional - parameters, the number of parameters of this circuit is :math:`q \times (r + 1)`, - where :math:`q` is the total number of qubits and :math:`r` is the number of repetitions, - set using `reps`. It is sensible to remove the rotation gates on the unentangled qubits since - a sequence of RY gates can be reduced to a single RY with summed rotation angles and more - parameters introduce overhead in optimization routines. - - If the form uses entanglement gates with parameters (such as ``'crx'``) the number of parameters - increases by the number of entanglements. For instance with `'linear'` or `'sca'` entanglement - the total number of parameters is :math:`2q \times (d + 1/2)`. For `'full'` entanglement an - additional :math:`q \times (q - 1)/2 \times d` parameters, hence a total of - :math:`d \times q \times (q + 1) / 2 + q`. - - Examples: - - >>> ry = RY(3, reps=2) # create the variational form on 3 qubits - >>> print(ry) # show the circuit - ┌──────────┐ ┌──────────┐ ┌──────────┐ - q_0: ┤ RY(θ[0]) ├─■──■─┤ RY(θ[3]) ├─────────────■──■─┤ RY(θ[6]) ├──────────── - ├──────────┤ │ │ └──────────┘┌──────────┐ │ │ └──────────┘┌──────────┐ - q_1: ┤ RY(θ[1]) ├─■──┼──────■──────┤ RY(θ[4]) ├─■──┼──────■──────┤ RY(θ[7]) ├ - ├──────────┤ │ │ ├──────────┤ │ │ ├──────────┤ - q_2: ┤ RY(θ[2]) ├────■──────■──────┤ RY(θ[5]) ├────■──────■──────┤ RY(θ[8]) ├ - └──────────┘ └──────────┘ └──────────┘ - - >>> ry = RY(3, entanglement='linear', reps=2, insert_barriers=True) - >>> qc = QuantumCircuit(3) # create a circuit and append the RY variational form - >>> qc += ry.to_circuit() - >>> qc.decompose().draw() - ┌──────────┐ ░ ░ ┌──────────┐ ░ ░ ┌──────────┐ - q_0: ┤ RY(θ[0]) ├─░──■─────░─┤ RY(θ[3]) ├─░──■─────░─┤ RY(θ[6]) ├ - ├──────────┤ ░ │ ░ ├──────────┤ ░ │ ░ ├──────────┤ - q_1: ┤ RY(θ[1]) ├─░──■──■──░─┤ RY(θ[4]) ├─░──■──■──░─┤ RY(θ[7]) ├ - ├──────────┤ ░ │ ░ ├──────────┤ ░ │ ░ ├──────────┤ - q_2: ┤ RY(θ[2]) ├─░─────■──░─┤ RY(θ[5]) ├─░─────■──░─┤ RY(θ[8]) ├ - └──────────┘ ░ ░ └──────────┘ ░ ░ └──────────┘ - - >>> ry = RY(4, 'crx', entanglement='circular', reps=2, insert_barriers=True) - >>> print(ry) - ┌──────────┐ ░ ┌──────────┐ ░ ┌──────────┐ - q_0: ┤ RY(θ[0]) ├─░─┤ Rx(θ[4]) ├─────■───────────────────────────────░──┤ RY(θ[8]) ├ - ├──────────┤ ░ └────┬─────┘┌────┴─────┐ ░ ├──────────┤ - q_1: ┤ RY(θ[1]) ├─░──────┼──────┤ Rx(θ[5]) ├─────■───────────────────░──┤ RY(θ[9]) ├ - ├──────────┤ ░ │ └──────────┘┌────┴─────┐ ░ ┌┴──────────┤ - q_2: ┤ RY(θ[2]) ├─░──────┼──────────────────┤ Rx(θ[6]) ├─────■───────░─┤ RY(θ[10]) ├ - ├──────────┤ ░ │ └──────────┘┌────┴─────┐ ░ ├───────────┤ - q_3: ┤ RY(θ[3]) ├─░──────■──────────────────────────────┤ Rx(θ[7]) ├─░─┤ RY(θ[11]) ├ - └──────────┘ ░ └──────────┘ ░ └───────────┘ - - >>> entanglement = [[0, 1], [0, 2]] - >>> ry = RY(3, 'cx', entanglement, reps=2) - >>> print(ry) - ┌──────────┐ ┌──────────┐ ┌──────────┐ - q_0: ┤ RY(θ[0]) ├──■────────────────■──┤ RY(θ[3]) ├──■────────────────■──┤ RY(θ[6]) ├ - ├──────────┤┌─┴─┐┌──────────┐ │ └──────────┘┌─┴─┐┌──────────┐ │ └──────────┘ - q_1: ┤ RY(θ[1]) ├┤ X ├┤ RY(θ[4]) ├──┼──────────────┤ X ├┤ RY(θ[7]) ├──┼────────────── - ├──────────┤└───┘└──────────┘┌─┴─┐┌──────────┐└───┘└──────────┘┌─┴─┐┌──────────┐ - q_2: ┤ RY(θ[2]) ├─────────────────┤ X ├┤ RY(θ[5]) ├─────────────────┤ X ├┤ RY(θ[8]) ├ - └──────────┘ └───┘└──────────┘ └───┘└──────────┘ - """ - - def __init__(self, - num_qubits: Optional[int] = None, - entanglement_blocks: Union[ - str, type, Instruction, QuantumCircuit, - List[Union[str, type, Instruction, QuantumCircuit]] - ] = CZGate, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - reps: int = 3, - skip_unentangled_qubits: bool = False, - skip_final_rotation_layer: bool = False, - parameter_prefix: str = 'θ', - insert_barriers: bool = False, - initial_state: Optional[Any] = None, - ) -> None: - """Create a new RY 2-local circuit. - - Args: - num_qubits: The number of qubits of the RY circuit. - reps: Specifies how often the structure of a rotation layer followed by an entanglement - layer is repeated. - entanglement_blocks: The gates used in the entanglement layer. Can be specified via the - name of a gate (e.g. ``'cx'``), the gate type itself (e.g. ``CXGate``) or a - ``QuantumCircuit`` with two qubits. - If only one gate is provided, the gate same gate is applied to each qubit. - If a list of gates is provided, all gates are applied to each qubit in the provided - order. - entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear' - or 'sca'), a list of integer-pairs specifying the indices of qubits - entangled with one another, or a callable returning such a list provided with - the index of the entanglement layer. - See the Examples section of :class:`~qiskit.circuit.library.TwoLocal` for more - detail. - initial_state: An `InitialState` object to prepend to the circuit. - skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits - that are entangled with another qubit. If False, the single qubit gates are applied - to each qubit in the Ansatz. Defaults to False. - skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits - that are entangled with another qubit. If False, the single qubit gates are applied - to each qubit in the Ansatz. Defaults to False. - skip_final_rotation_layer: If False, a rotation layer is added at the end of the - ansatz. If True, no rotation layer is added. - parameter_prefix: The parameterized gates require a parameter to be defined, for which - we use :class:`~qiskit.circuit.ParameterVector`. - insert_barriers: If True, barriers are inserted in between each layer. If False, - no barriers are inserted. - - """ - super().__init__(num_qubits=num_qubits, - reps=reps, - rotation_blocks=RYGate, - entanglement_blocks=entanglement_blocks, - entanglement=entanglement, - initial_state=initial_state, - skip_unentangled_qubits=skip_unentangled_qubits, - skip_final_rotation_layer=skip_final_rotation_layer, - parameter_prefix=parameter_prefix, - insert_barriers=insert_barriers) - - @property - def parameter_bounds(self) -> List[Tuple[float, float]]: - """Return the parameter bounds. - - Returns: - The parameter bounds. - """ - return self.num_parameters * [(-np.pi, np.pi)] diff --git a/qiskit/circuit/library/n_local/ryrz.py b/qiskit/circuit/library/n_local/ryrz.py deleted file mode 100644 index a2c4da7dfb49..000000000000 --- a/qiskit/circuit/library/n_local/ryrz.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2017, 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 RYRZ 2-local circuit.""" - -from typing import Union, Optional, List, Tuple, Callable, Any -from numpy import pi - -from qiskit.circuit import QuantumCircuit, Instruction -from qiskit.circuit.library.standard_gates import RYGate, RZGate, CZGate -from .two_local import TwoLocal - - -class RYRZ(TwoLocal): - r"""The RYRZ 2-local circuit. - - The RYRZ circuit consists of layers of :math:`Y` plus :math:`Z` rotations with entanglements. - It can be used to prepare trial wave functions for variational quantum algorithms. - - .. parsed-literal:: - - ┌──────────┐┌──────────┐ ░ ░ ░ ┌───────────┐┌───────────┐ - ┤ RY(θ[0]) ├┤ RZ(θ[3]) ├─░──■─────░─ ... ─░─┤ RY(θ[12]) ├┤ RZ(θ[15]) ├ - ├──────────┤├──────────┤ ░ │ ░ ░ ├───────────┤├───────────┤ - ┤ RY(θ[1]) ├┤ RZ(θ[4]) ├─░──■──■──░─ ... ─░─┤ RY(θ[13]) ├┤ RZ(θ[16]) ├ - ├──────────┤├──────────┤ ░ │ ░ ░ ├───────────┤├───────────┤ - ┤ RY(θ[2]) ├┤ RZ(θ[5]) ├─░─────■──░─ ... ─░─┤ RY(θ[14]) ├┤ RZ(θ[17]) ├ - └──────────┘└──────────┘ ░ ░ ░ └───────────┘└───────────┘ - - See :class:`~qiskit.circuit.library.RY` for more detail on the possible arguments and options - such as skipping unentanglement qubits, which apply here too. - - Examples: - - >>> ryrz = RYRZ(3, reps=1) # create the variational form on 3 qubits - >>> print(ryrz) # show the circuit - ┌──────────┐┌──────────┐ ┌──────────┐┌──────────┐ - q_0: ┤ RY(θ[0]) ├┤ RZ(θ[3]) ├─■──■─┤ RY(θ[6]) ├┤ RZ(θ[9]) ├───────────── - ├──────────┤├──────────┤ │ │ └──────────┘├──────────┤┌───────────┐ - q_1: ┤ RY(θ[1]) ├┤ RZ(θ[4]) ├─■──┼──────■──────┤ RY(θ[7]) ├┤ RZ(θ[10]) ├ - ├──────────┤├──────────┤ │ │ ├──────────┤├───────────┤ - q_2: ┤ RY(θ[2]) ├┤ RZ(θ[5]) ├────■──────■──────┤ RY(θ[8]) ├┤ RZ(θ[11]) ├ - └──────────┘└──────────┘ └──────────┘└───────────┘ - - >>> ryrz = RYRZ(4, entanglement='circular', reps=1) - >>> qc = QuantumCircuit(3) # create a circuit and append the RY variational form - >>> qc += ryrz.to_circuit() - >>> qc.decompose().draw() - ┌──────────┐┌──────────┐ ┌──────────┐┌───────────┐ - q_0: ┤ RY(θ[0]) ├┤ RZ(θ[4]) ├─■──■─┤ RY(θ[8]) ├┤ RZ(θ[12]) ├────────────────────────── - ├──────────┤├──────────┤ │ │ └──────────┘└┬──────────┤┌───────────┐ - q_1: ┤ RY(θ[1]) ├┤ RZ(θ[5]) ├─┼──■──────■───────┤ RY(θ[9]) ├┤ RZ(θ[13]) ├───────────── - ├──────────┤├──────────┤ │ │ └──────────┘├───────────┤┌───────────┐ - q_2: ┤ RY(θ[2]) ├┤ RZ(θ[6]) ├─┼─────────■────────────■──────┤ RY(θ[10]) ├┤ RZ(θ[14]) ├ - ├──────────┤├──────────┤ │ │ ├───────────┤├───────────┤ - q_3: ┤ RY(θ[3]) ├┤ RZ(θ[7]) ├─■──────────────────────■──────┤ RY(θ[11]) ├┤ RZ(θ[15]) ├ - └──────────┘└──────────┘ └───────────┘└───────────┘ - """ - - def __init__(self, - num_qubits: Optional[int] = None, - entanglement_blocks: Union[ - str, type, Instruction, QuantumCircuit, - List[Union[str, type, Instruction, QuantumCircuit]] - ] = CZGate, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - reps: int = 3, - skip_unentangled_qubits: bool = False, - skip_final_rotation_layer: bool = False, - parameter_prefix: str = 'θ', - insert_barriers: bool = False, - initial_state: Optional[Any] = None, - ) -> None: - """Create a new RYRZ 2-local circuit. - - Args: - num_qubits: The number of qubits of the RYRZ circuit. - reps: Specifies how often the structure of a rotation layer followed by an entanglement - layer is repeated. - entanglement_blocks: The gates used in the entanglement layer. Can be specified via the - name of a gate (e.g. ``'cx'``), the gate type itself (e.g. ``CXGate``) or a - ``QuantumCircuit`` with two qubits. - If only one gate is provided, the same gate is applied to each qubit. - If a list of gates is provided, all gates are applied to each qubit in the provided - order. - entanglement: Specifies the entanglement structure. Can be a string ('full', 'linear' - , 'circular' or 'sca'), a list of integer-pairs specifying the indices of qubits - entangled with one another, or a callable returning such a list provided with - the index of the entanglement layer. - See the Examples section of :class:`~qiskit.circuit.library.TwoLocal` for more - detail. - initial_state: An `InitialState` object to prepend to the circuit. - skip_unentangled_qubits: If True, the single qubit gates are only applied to qubits - that are entangled with another qubit. If False, the single qubit gates are applied - to each qubit in the Ansatz. Defaults to False. - skip_final_rotation_layer: If False, a rotation layer is added at the end of the - ansatz. If True, no rotation layer is added. - parameter_prefix: The parameterized gates require a parameter to be defined, for which - we use :class:`~qiskit.circuit.ParameterVector`. - insert_barriers: If True, barriers are inserted in between each layer. If False, - no barriers are inserted. - - """ - super().__init__(num_qubits=num_qubits, - rotation_blocks=[RYGate, RZGate], - entanglement_blocks=entanglement_blocks, - entanglement=entanglement, - reps=reps, - skip_unentangled_qubits=skip_unentangled_qubits, - skip_final_rotation_layer=skip_final_rotation_layer, - parameter_prefix=parameter_prefix, - insert_barriers=insert_barriers, - initial_state=initial_state) - - @property - def parameter_bounds(self) -> List[Tuple[float, float]]: - """Return the parameter bounds. - - Returns: - The parameter bounds. - """ - return self.num_parameters * [(-pi, pi)] diff --git a/test/python/circuit/test_library.py b/test/python/circuit/test_library.py index 6003595deca1..4dfdfd974ce7 100644 --- a/test/python/circuit/test_library.py +++ b/test/python/circuit/test_library.py @@ -28,9 +28,9 @@ InnerProduct, OR, AND, QFT, IQP, LinearPauliRotations, PolynomialPauliRotations, IntegerComparator, PiecewiseLinearPauliRotations, - WeightedAdder, Diagonal, NLocal, TwoLocal, RY, RYRZ, - SwapRZ, PauliExpansion, FirstOrderExpansion, - SecondOrderExpansion, MCMT, MCMTVChain, GMS) + WeightedAdder, Diagonal, NLocal, TwoLocal, RealAmplitudes, + EfficientSU2, ExcitationPreserving, PauliFeatureMap, + ZFeatureMap, ZZFeatureMap, MCMT, MCMTVChain, GMS) from qiskit.circuit.random.utils import random_circuit from qiskit.converters.circuit_to_dag import circuit_to_dag from qiskit.exceptions import QiskitError @@ -1007,8 +1007,8 @@ def assertCircuitEqual(self, qc1, qc2, visual=False, transpiled=True): """An equality test specialized to circuits.""" if transpiled: basis_gates = ['id', 'u1', 'u3', 'cx'] - qc1_transpiled = transpile(qc1, basis_gates=basis_gates) - qc2_transpiled = transpile(qc2, basis_gates=basis_gates) + qc1_transpiled = transpile(qc1, basis_gates=basis_gates, optimization_level=0) + qc2_transpiled = transpile(qc2, basis_gates=basis_gates, optimization_level=0) qc1, qc2 = qc1_transpiled, qc2_transpiled if visual: @@ -1314,8 +1314,8 @@ def assertCircuitEqual(self, qc1, qc2, visual=False, transpiled=True): """An equality test specialized to circuits.""" if transpiled: basis_gates = ['id', 'u1', 'u3', 'cx'] - qc1_transpiled = transpile(qc1, basis_gates=basis_gates) - qc2_transpiled = transpile(qc2, basis_gates=basis_gates) + qc1_transpiled = transpile(qc1, basis_gates=basis_gates, optimization_level=0) + qc2_transpiled = transpile(qc2, basis_gates=basis_gates, optimization_level=0) qc1, qc2 = qc1_transpiled, qc2_transpiled if visual: @@ -1451,8 +1451,8 @@ def test_adding_two(self): self.assertCircuitEqual(reference, circuit) def test_ry_blocks(self): - """Test that the RY circuit is instantiated correctly.""" - two = RY(4) + """Test that the RealAmplitudes circuit is instantiated correctly.""" + two = RealAmplitudes(4) with self.subTest(msg='test rotation gate'): self.assertEqual(len(two.rotation_blocks), 1) self.assertIsInstance(two.rotation_blocks[0].data[0][0], RYGate) @@ -1462,11 +1462,10 @@ def test_ry_blocks(self): np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_ry_circuit(self): - """Test an RY circuit.""" + """Test an RealAmplitudes circuit.""" num_qubits = 3 reps = 2 entanglement = 'full' - entanglement_gate = 'cx' parameters = ParameterVector('theta', num_qubits * (reps + 1)) param_iter = iter(parameters) @@ -1480,14 +1479,14 @@ def test_ry_circuit(self): for i in range(num_qubits): expected.ry(next(param_iter), i) - library = RY(num_qubits, reps=reps, entanglement_blocks=entanglement_gate, - entanglement=entanglement).assign_parameters(parameters) + library = RealAmplitudes(num_qubits, reps=reps, + entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) def test_ryrz_blocks(self): - """Test that the RYRZ circuit is instantiated correctly.""" - two = RYRZ(3) + """Test that the EfficientSU2 circuit is instantiated correctly.""" + two = EfficientSU2(3) with self.subTest(msg='test rotation gate'): self.assertEqual(len(two.rotation_blocks), 2) self.assertIsInstance(two.rotation_blocks[0].data[0][0], RYGate) @@ -1498,11 +1497,10 @@ def test_ryrz_blocks(self): np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_ryrz_circuit(self): - """Test an RYRZ circuit.""" + """Test an EfficientSU2 circuit.""" num_qubits = 3 reps = 2 entanglement = 'circular' - entanglement_gate = 'cz' parameters = ParameterVector('theta', 2 * num_qubits * (reps + 1)) param_iter = iter(parameters) @@ -1512,22 +1510,23 @@ def test_ryrz_circuit(self): expected.ry(next(param_iter), i) for i in range(num_qubits): expected.rz(next(param_iter), i) - expected.cz(2, 0) - expected.cz(0, 1) - expected.cz(1, 2) + expected.cx(2, 0) + expected.cx(0, 1) + expected.cx(1, 2) for i in range(num_qubits): expected.ry(next(param_iter), i) for i in range(num_qubits): expected.rz(next(param_iter), i) - library = RYRZ(num_qubits, reps=reps, entanglement_blocks=entanglement_gate, - entanglement=entanglement).assign_parameters(parameters) + library = EfficientSU2(num_qubits, reps=reps, entanglement=entanglement).assign_parameters( + parameters + ) self.assertCircuitEqual(library, expected) def test_swaprz_blocks(self): - """Test that the SwapRZ circuit is instantiated correctly.""" - two = SwapRZ(5) + """Test that the ExcitationPreserving circuit is instantiated correctly.""" + two = ExcitationPreserving(5) with self.subTest(msg='test rotation gate'): self.assertEqual(len(two.rotation_blocks), 1) self.assertIsInstance(two.rotation_blocks[0].data[0][0], RZGate) @@ -1544,7 +1543,7 @@ def test_swaprz_blocks(self): np.testing.assert_almost_equal(two.parameter_bounds, expected) def test_swaprz_circuit(self): - """Test a SwapRZ circuit.""" + """Test a ExcitationPreserving circuit in iswap mode.""" num_qubits = 3 reps = 2 entanglement = 'linear' @@ -1564,8 +1563,38 @@ def test_swaprz_circuit(self): for i in range(num_qubits): expected.rz(next(param_iter), i) - library = SwapRZ(num_qubits, reps=reps, - entanglement=entanglement).assign_parameters(parameters) + library = ExcitationPreserving(num_qubits, reps=reps, + entanglement=entanglement).assign_parameters(parameters) + + self.assertCircuitEqual(library, expected) + + def test_fsim_circuit(self): + """Test a ExcitationPreserving circuit in fsim mode.""" + num_qubits = 3 + reps = 2 + entanglement = 'linear' + # need the parameters in the entanglement blocks to be the same because the order + # can get mixed up in ExcitationPreserving (since parameters are not ordered in circuits) + parameters = [1] * (num_qubits * (reps + 1) + reps * (1 + num_qubits)) + param_iter = iter(parameters) + + expected = QuantumCircuit(3) + for _ in range(reps): + for i in range(num_qubits): + expected.rz(next(param_iter), i) + shared_param = next(param_iter) + expected.rxx(shared_param, 0, 1) + expected.ryy(shared_param, 0, 1) + expected.cu1(next(param_iter), 0, 1) + shared_param = next(param_iter) + expected.rxx(shared_param, 1, 2) + expected.ryy(shared_param, 1, 2) + expected.cu1(next(param_iter), 1, 2) + for i in range(num_qubits): + expected.rz(next(param_iter), i) + + library = ExcitationPreserving(num_qubits, reps=reps, mode='fsim', + entanglement=entanglement).assign_parameters(parameters) self.assertCircuitEqual(library, expected) @@ -1576,7 +1605,7 @@ class TestDataEncoding(QiskitTestCase): def test_pauli_empty(self): """Test instantiating an empty Pauli expansion.""" - encoding = PauliExpansion() + encoding = PauliFeatureMap() with self.subTest(msg='equal to empty circuit'): self.assertTrue(Operator(encoding).equiv(QuantumCircuit())) @@ -1589,13 +1618,13 @@ def test_pauli_empty(self): @unpack def test_num_parameters(self, num_qubits, reps, pauli_strings): """Test the number of parameters equals the number of qubits, independent of reps.""" - encoding = PauliExpansion(num_qubits, paulis=pauli_strings, reps=reps) + encoding = PauliFeatureMap(num_qubits, paulis=pauli_strings, reps=reps) self.assertEqual(encoding.num_parameters, num_qubits) self.assertEqual(encoding.num_parameters_settable, num_qubits) def test_pauli_evolution(self): """Test the generation of Pauli blocks.""" - encoding = PauliExpansion() + encoding = PauliFeatureMap() time = 1.4 with self.subTest(pauli_string='ZZ'): evo = QuantumCircuit(2) @@ -1630,7 +1659,7 @@ def test_pauli_evolution(self): def test_first_order_circuit(self): """Test a first order expansion circuit.""" times = [0.2, 1, np.pi, -1.2] - encoding = FirstOrderExpansion(4, reps=3).assign_parameters(times) + encoding = ZFeatureMap(4, reps=3).assign_parameters(times) ref = QuantumCircuit(4) for _ in range(3): @@ -1643,7 +1672,7 @@ def test_first_order_circuit(self): def test_second_order_circuit(self): """Test a second order expansion circuit.""" times = [0.2, 1, np.pi] - encoding = SecondOrderExpansion(3, reps=2).assign_parameters(times) + encoding = ZZFeatureMap(3, reps=2).assign_parameters(times) def zz_evolution(circuit, qubit1, qubit2): time = (np.pi - times[qubit1]) * (np.pi - times[qubit2])