Skip to content

Commit

Permalink
update docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
Cryoris committed Oct 17, 2024
1 parent 5501bc5 commit 2eea43e
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 116 deletions.
53 changes: 24 additions & 29 deletions qiskit/circuit/library/n_local/efficient_su2.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ def efficient_su2(
insert_barriers: bool = False,
name: str = "EfficientSU2",
):
r"""The hardware efficient SU(2) 2-local circuit.
r"""The hardware-efficient :math:`SU(2)` 2-local circuit.
The ``efficient_su2`` circuit consists of layers of single qubit operations spanned by SU(2)
and 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.
The ``efficient_su2`` circuit consists of layers of single qubit operations spanned by
:math:`SU(2)` and 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`
:math:`SU(2)` is the 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:
On 3 qubits and using the Pauli :math:`Y` and :math:`Z` rotations as single qubit gates, the
this circuit is represented by:
.. parsed-literal::
Expand All @@ -67,29 +67,24 @@ def efficient_su2(
Examples:
>>> circuit = efficient_su2(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]) ├
└──────────┘└──────────┘ └───┘ └───┘ └──────────┘└───────────┘
Per default, the ``"reverse_linear"`` entanglement is used, which, in the case of
CX gates, is equivalent to an all-to-all entanglement:
>>> ansatz = efficient_su2(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 ├
└──────────┘└───┘ └───┘ └──────────┘└───┘
.. plot::
:include-source:
circuit = efficient_su2(3, reps=1)
circuit.draw("mpl")
To specify which SU(2) gates should be used in the rotation layer, we can set the
``su2_gates`` argument. In addition, we can change the entanglement structure.
For example::
.. plot::
:include-source:
circuit = efficient_su2(4, su2_gates=["rx", "y"], entanglement="circular", reps=1)
circuit.draw("mpl")
Args:
num_qubits: The number of qubits.
Expand Down
44 changes: 15 additions & 29 deletions qiskit/circuit/library/n_local/excitation_preserving.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,37 +67,23 @@ def excitation_preserving(
Examples:
>>> ansatz = excitation_preserving(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]) │┌────────────┐┌────────────┐ ░ ├──────────┤
q_1: ┤ RZ(θ[1]) ├─░─┤1 ├┤1 ├┤0 ├┤0 ├─░─┤ RZ(θ[6]) ├
├──────────┤ ░ └────────────┘└────────────┘│ RXX(θ[4]) ││ RYY(θ[4]) │ ░ ├──────────┤
q_2: ┤ RZ(θ[2]) ├─░─────────────────────────────┤1 ├┤1 ├─░─┤ RZ(θ[7]) ├
└──────────┘ ░ └────────────┘└────────────┘ ░ └──────────┘
With linear entanglement, this circuit is given by:
>>> ansatz = excitation_preserving(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(ansatz, inplace=True)
>>> qc.draw()
┌──────────┐┌────────────┐┌────────────┐┌──────────┐
q_0: ─────■─────┤ RZ(θ[0]) ├┤0 ├┤0 ├┤ RZ(θ[3]) ├
┌────┴────┐├──────────┤│ RXX(θ[2]) ││ RYY(θ[2]) │├──────────┤
q_1: ┤ RY(0.2) ├┤ RZ(θ[1]) ├┤1 ├┤1 ├┤ RZ(θ[4]) ├
└─────────┘└──────────┘└────────────┘└────────────┘└──────────┘
.. plot::
:include-source:
>>> ansatz = excitation_preserving(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]) ├
└──────────┘ ░ └────────────┘└────────────┘ ░ └──────────┘
ansatz = excitation_preserving(3, reps=1, insert_barriers=True, entanglement="linear")
ansatz.draw("mpl")
The entanglement structure can be explicitly specified with the ``entanglement``
argument. The ``"fsim"`` mode includes an additional parameterized :class:`.CPhaseGate`
in each block:
.. plot::
:include-source:
ansatz = excitation_preserving(3, reps=1, mode="fsim", entanglement=[[0, 2]])
ansatz.draw("mpl")
Args:
num_qubits: The number of qubits.
Expand Down
96 changes: 94 additions & 2 deletions qiskit/circuit/library/n_local/n_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def n_local(
skip_unentangled_qubits: bool = False,
name: str | None = "nlocal",
) -> QuantumCircuit:
"""Construct an n-local variational circuit.
r"""Construct an n-local variational circuit.
The structure of the n-local circuit are alternating rotation and entanglement layers.
In both layers, parameterized circuit-blocks act on the circuit in a defined way.
Expand Down Expand Up @@ -99,7 +99,99 @@ def n_local(
+---------------------------------+
repeated reps times
If specified, barriers can be inserted in between every layer.
Entanglement:
The entanglement describes the connections of the gates in the entanglement layer.
For a two-qubit gate for example, the entanglement contains pairs of qubits on which the
gate should acts, e.g. ``[[ctrl0, target0], [ctrl1, target1], ...]``.
A set of default entanglement strategies is provided and can be selected by name:
* ``"full"`` entanglement is each qubit is entangled with all the others.
* ``"linear"`` entanglement is qubit :math:`i` entangled with qubit :math:`i + 1`,
for all :math:`i \in \{0, 1, ... , n - 2\}`, where :math:`n` is the total number of qubits.
* ``"reverse_linear"`` entanglement is qubit :math:`i` entangled with qubit :math:`i + 1`,
for all :math:`i \in \{n-2, n-3, ... , 1, 0\}`, where :math:`n` is the total number of qubits.
Note that if ``entanglement_blocks=="cx"`` then this option provides the same unitary as
``"full"`` with fewer entangling gates.
* ``"pairwise"`` entanglement is one layer where qubit :math:`i` is entangled with qubit
:math:`i + 1`, for all even values of :math:`i`, and then a second layer where qubit :math:`i`
is entangled with qubit :math:`i + 1`, for all odd values of :math:`i`.
* ``"circular"`` entanglement is linear entanglement but with an additional entanglement of the
first and last qubit before the linear part.
* ``"sca"`` (shifted-circular-alternating) entanglement is a generalized and modified version
of the proposed circuit 14 in `Sim et al. <https://arxiv.org/abs/1905.10876>`__.
It consists of circular entanglement where the "long" entanglement connecting the first with
the last qubit is shifted by one each block. Furthermore the role of control and target
qubits are swapped every block (therefore alternating).
If an entanglement layer contains multiple blocks, then the entanglement should be
given as list of entanglements for each block. For example::
entanglement_blocks = ["rxx", "ryy"]
entanglement = ["full", "linear"] # full for rxx and linear for ryy
or::
structure_rxx = [[0, 1], [2, 3]]
structure_ryy = [[0, 2]]
entanglement = [structure_rxx, structure_ryy]
Finally, the entanglement can vary in each repetition of the circuit. For this, we
support passing a callable that takes as input the layer index and returns the entanglement
for the layer in the above format. See the examples below for a concrete example.
Examples:
The rotation and entanglement gates can be specified via single strings, if they
are made up of a single block per layer:
.. plot::
:include-source:
circuit = n_local(3, "ry", "cx", "linear", reps=2, insert_barriers=True)
circuit.draw("mpl")
Multiple gates per layer can be set by passing a list. Here, for example, we use
Pauli-Y and Pauli-Z rotations in the rotation layer:
.. plot::
:include-source:
circuit = n_local(3, ["ry", "rz"], "cz", 'full', reps=1, insert_barriers=True)
circuit.draw("mpl")
To omit rotation or entanglement layers, the block can be set to an empty list:
.. plot::
:include-source:
circuit = n_local(4, [], "cry", reps=2)
circuit.draw("mpl")
The entanglement can be set explicitly via the ``entanglement`` argument:
.. plot::
:include-source:
entangler_map = [[0, 1], [2, 0]]
circuit = n_local(3, "x", "crx", entangler_map, reps=2)
circuit.draw("mpl")
We can set different entanglements per layer, by specifing a callable that takes
as input the current layer index, and returns the entanglement structure. For example,
the following uses different entanglements for odd and even layers:
.. plot:
:include-source:
def entanglement(layer_index: int) -> list[list[int]]:
if layer_index % 2 == 0:
return [[0, 1], [0, 2]]
return [[1, 2]]
circuit = n_local(3, "x", "cx", entanglement, reps=3, insert_barriers=True)
circuit.draw("mpl")
Args:
num_qubits: The number of qubits of the circuit.
Expand Down
8 changes: 4 additions & 4 deletions qiskit/circuit/library/n_local/pauli_two_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ def pauli_two_design(
Examples:
.. plot::
:include-source:
:include-source:
from qiskit.circuit.library import pauli_two_design
circuit = pauli_two_design(4, reps=2, seed=5, insert_barriers=True)
circuit.draw("mpl")
from qiskit.circuit.library import pauli_two_design
circuit = pauli_two_design(4, reps=2, seed=5, insert_barriers=True)
circuit.draw("mpl")
Args:
num_qubits: The number of qubits of the Pauli Two-Design circuit.
Expand Down
Loading

0 comments on commit 2eea43e

Please sign in to comment.