Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions documentation/source/general/changelog/changelog-dev.rst
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,19 @@ Bug Fixes
* Fixed ``reset`` on a single qubit, such as ``reset(qv[0])``, failing to
compile with :ref:`qjit <qjit>`. Resetting a whole QuantumVariable was not
affected (`PR #941 <https://github.com/eclipse-qrisp/Qrisp/pull/941>`_).
* Fixed the matrices of several standard gates and their conversions:

- ``RGate(theta, phi)`` now implements ``RZ(phi) RX(theta) RZ(-phi)``,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In IQM language this gate is called PRX. It would probably make sense to not have this duplicated (i.e. remove RGate, move PRXGate into the standard operations).

matching the standard R gate. Its PennyLane conversion is now
``qml.Rot(pi/2 - phi, theta, phi - pi/2)``.
- ``SXGate`` and ``SXDGGate`` now carry the global phase ``±pi/4``, so they
are the exact square roots of ``X`` instead of ``RX(±pi/2)``. This phase is
also kept when they are controlled. They are now converted to ``qml.SX``
in PennyLane.
- ``U1Gate`` now converts to Qiskit's ``RZGate`` instead of ``PhaseGate``,
since Qrisp's ``U1`` is an RZ rotation.

(`PR #949 <https://github.com/eclipse-qrisp/Qrisp/pull/949>`_).

Compatibility
-------------
Expand All @@ -629,6 +642,14 @@ Compatibility
integer
(`PR #932 <https://github.com/eclipse-qrisp/Qrisp/pull/932>`_).

* ``RGate`` and ``QuantumCircuit.r`` are deprecated in favor of the identical
``PRXGate`` and the new ``QuantumCircuit.prx``. Both still work but emit a
``QrispDeprecationWarning``. ``RGate(theta, phi)`` now builds a
``PRXGate(theta, phi)``, so the resulting operation is named ``"prx"`` instead
of ``"r"`` and stores its angles as ``alpha`` and ``beta``. ``PRXGate`` can now
also be imported from ``qrisp.circuit.standard_operations``
(`PR #949 <https://github.com/eclipse-qrisp/Qrisp/pull/949>`_).

.. Add compatibility notes above this line

New Tutorials/ Updated Documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ Operation application methods
QuantumCircuit.id
QuantumCircuit.crx
QuantumCircuit.barrier
QuantumCircuit.prx
QuantumCircuit.r


Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
qrisp.QuantumCircuit.prx
========================

.. currentmodule:: qrisp

.. automethod:: QuantumCircuit.prx
33 changes: 13 additions & 20 deletions src/qrisp/circuit/controlled_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,27 +64,20 @@ def multi_controlled_u3_circ(u3_gate, control_amount, ctrl_state, method=None):
if ctrl_state[i] == "0":
qc.x(qc.qubits[i])

# Once controlled, the global phase becomes a phase on the states where all controls
# are on. The branches for diagonal unitaries add it to both target phases, the others synthesize
# it on the control qubits only.
gp = u3_gate.global_phase

# If the U3 gate is an rx , ry, rz or p gate, we can use gray phase synthesis on the
# target qubit and wrap this in the corresponding gates
# (for instance H for rx because RX = H RZ H)
if u3_gate.name in ["p", "t", "s", "t_dg", "s_dg"]:
# Synthesize phases using gray synthesis
gray_phase_synth_qb_list(
qc,
qc.qubits,
(2 ** (control_amount + 1) - 1) * [0] + [u3_gate.lam + u3_gate.phi],
phase_tolerant=method in ["gray_pt", "gray_pt_inv"],
)

if method == "gray_pt_inv":
qc = qc.inverse()

elif u3_gate.name == "rz":
# Make sure to account for the possible global phase of rz
if u3_gate.name in ["p", "t", "s", "t_dg", "s_dg", "rz"]:
# Since theta = 0, U3 is diag(e^(i*gp), e^(i*(gp + phi + lam)))
gray_phase_synth_qb_list(
qc,
qc.qubits,
(2 ** (control_amount + 1) - 2) * [0] + [-u3_gate.params[0] / 2, u3_gate.params[0] / 2],
(2 ** (control_amount + 1) - 2) * [0] + [gp, gp + u3_gate.phi + u3_gate.lam],
phase_tolerant=method in ["gray_pt", "gray_pt_inv"],
)

Expand All @@ -97,7 +90,7 @@ def multi_controlled_u3_circ(u3_gate, control_amount, ctrl_state, method=None):
gray_phase_synth_qb_list(
qc,
qc.qubits,
(2 ** (control_amount + 1) - 2) * [0] + [-u3_gate.theta / 2, u3_gate.theta / 2],
(2 ** (control_amount + 1) - 2) * [0] + [gp - u3_gate.theta / 2, gp + u3_gate.theta / 2],
phase_tolerant=method in ["gray_pt", "gray_pt_inv"],
)
qc.h(target_qubit)
Expand All @@ -112,7 +105,7 @@ def multi_controlled_u3_circ(u3_gate, control_amount, ctrl_state, method=None):
gray_phase_synth_qb_list(
qc,
qc.qubits,
(2 ** (control_amount + 1) - 2) * [0] + [u3_gate.theta / 2, -u3_gate.theta / 2],
(2 ** (control_amount + 1) - 2) * [0] + [gp + u3_gate.theta / 2, gp - u3_gate.theta / 2],
phase_tolerant=method in ["gray_pt", "gray_pt_inv"],
)
qc.h(target_qubit)
Expand All @@ -121,7 +114,7 @@ def multi_controlled_u3_circ(u3_gate, control_amount, ctrl_state, method=None):
if method == "gray_pt_inv":
qc = qc.inverse()

elif u3_gate.phi == 0 and u3_gate.lam == 0 and u3_gate.theta == 0 and u3_gate.global_phase == 0:
elif u3_gate.phi == 0 and u3_gate.lam == 0 and u3_gate.theta == 0 and gp == 0:
pass

elif u3_gate.phi == 0 and u3_gate.lam == 0 and u3_gate.theta == 0:
Expand All @@ -130,7 +123,7 @@ def multi_controlled_u3_circ(u3_gate, control_amount, ctrl_state, method=None):
gray_phase_synth_qb_list(
qc,
qc.qubits[:-1],
(2 ** (control_amount) - 1) * [0] + [u3_gate.global_phase],
(2 ** (control_amount) - 1) * [0] + [gp],
)

# Treat pauli gates
Expand Down Expand Up @@ -182,7 +175,7 @@ def multi_controlled_u3_circ(u3_gate, control_amount, ctrl_state, method=None):
gray_phase_synth_qb_list(
qc,
qc.qubits[:-1],
(2 ** (control_amount) - 1) * [0] + [u3_gate.global_phase + (alpha + beta) / 2],
(2 ** (control_amount) - 1) * [0] + [gp + (alpha + beta) / 2],
)

qc.append(A.to_gate("A"), [qc.qubits[-1]])
Expand Down
50 changes: 1 addition & 49 deletions src/qrisp/circuit/pass_management/passes/convert_to_prx.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,7 @@
from qrisp.circuit.operation import ClControlledOperation, U3Gate
from qrisp.circuit.pass_management.circuit_pass import CircuitPass
from qrisp.circuit.quantum_circuit import QuantumCircuit
from qrisp.circuit.standard_operations import GPhaseGate


class PRXGate(U3Gate):
r"""PRX (Phased-RX) gate.

The PRX gate is a single-qubit gate of the form:

.. math::

\text{PRX}(\alpha, \beta) = R_Z(\beta) \cdot R_X(\alpha) \cdot R_Z(-\beta)

Parameters
----------
alpha : float
The rotation angle.
beta : float
The phase parameter.

"""

def __init__(self, alpha: float, beta: float) -> None:
self.alpha = alpha
self.beta = beta

# PRX(alpha, beta) = RZ(beta) * RX(alpha) * RZ(-beta)
#
# Using RX(alpha) = RZ(-pi/2) * RY(alpha) * RZ(pi/2) and the U3
# decomposition U3(theta, phi, lam) = RZ(phi) * RY(theta) * RZ(lam):
#
# PRX(alpha, beta) = RZ(beta) * RZ(-pi/2) * RY(alpha) * RZ(pi/2) * RZ(-beta)
# = RZ(beta - pi/2) * RY(alpha) * RZ(pi/2 - beta)
# = U3(alpha, beta - pi/2, pi/2 - beta)
#
super().__init__(alpha, beta - np.pi / 2, np.pi / 2 - beta, name="prx")

def inverse(self):
r"""Returns the inverse of the PRX gate.

The inverse of :math:`R_Z(\beta) R_X(\alpha) R_Z(-\beta)` is
:math:`R_Z(\beta) R_X(-\alpha) R_Z(-\beta)`.

Returns
-------
PRXGate
The inverted PRX gate with parameters ``(-alpha, beta)``.

"""
return PRXGate(-self.alpha, self.beta)
from qrisp.circuit.standard_operations import GPhaseGate, PRXGate


def _get_phase_diff(U_a: np.ndarray, U_b: np.ndarray) -> float:
Expand Down
41 changes: 35 additions & 6 deletions src/qrisp/circuit/quantum_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from __future__ import annotations

import warnings
from typing import TYPE_CHECKING, Any, cast

import numpy as np
Expand All @@ -33,6 +34,7 @@
import qrisp.circuit.standard_operations as ops
from qrisp.circuit import Clbit, Instruction, Operation, Qubit, U3Gate
from qrisp.misc import (
QrispDeprecationWarning,
cnot_count,
cnot_depth_indicator,
get_depth_dic,
Expand Down Expand Up @@ -2821,19 +2823,39 @@ def u3(self, theta: FloatLike, phi: FloatLike, lam: FloatLike, qubits: QubitLike
"""
self.append(ops.u3Gate(theta, phi, lam), [qubits])

def r(self, theta: FloatLike, phi: FloatLike, qubits: QubitLike):
r"""Instruct an R-gate.
def prx(self, alpha: FloatLike, beta: FloatLike, qubits: QubitLike):
r"""Instruct a PRX (Phased-RX) gate.

The unitary is

.. math::

R(\theta, \phi) =
\text{PRX}(\alpha, \beta) = R_Z(\beta) R_X(\alpha) R_Z(-\beta) =
\begin{pmatrix}
\cos\!\tfrac{\theta}{2} & e^{-i\phi}\sin\!\tfrac{\theta}{2} \\
-e^{i\phi}\sin\!\tfrac{\theta}{2} & \cos\!\tfrac{\theta}{2}
\cos\!\tfrac{\alpha}{2} & -i e^{-i\beta}\sin\!\tfrac{\alpha}{2} \\
-i e^{i\beta}\sin\!\tfrac{\alpha}{2} & \cos\!\tfrac{\alpha}{2}
\end{pmatrix}

Parameters
----------
alpha : FloatLike
Rotation angle in radians. Controls the amount of rotation.
beta : FloatLike
Phase angle in radians. Selects the rotation axis in the XY plane.
qubits : QubitLike
The qubit to apply the gate on.

"""
self.append(ops.PRXGate(alpha, beta), [qubits])

def r(self, theta: FloatLike, phi: FloatLike, qubits: QubitLike):
"""Instruct an R-gate.

.. deprecated:: 0.10

``r`` is deprecated, use :meth:`prx` instead.
``qc.r(theta, phi, qubits)`` is identical to ``qc.prx(theta, phi, qubits)``.

Parameters
----------
theta : FloatLike
Expand All @@ -2844,7 +2866,14 @@ def r(self, theta: FloatLike, phi: FloatLike, qubits: QubitLike):
The qubit to apply the gate on.

"""
self.append(ops.RGate(theta, phi), [qubits])
warnings.warn(
"DeprecationWarning: QuantumCircuit.r is deprecated and will be removed in a "
"later release of Qrisp. Use QuantumCircuit.prx instead, "
"qc.r(theta, phi, qubits) is identical to qc.prx(theta, phi, qubits).",
QrispDeprecationWarning,
stacklevel=2,
)
self.prx(theta, phi, qubits)

def unitary(self, unitary_array, qubits: QubitLike):
"""Instruct a gate from a given unitary matrix.
Expand Down
Loading
Loading