-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
509 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
qutip\_qip.decompose package | ||
============================ | ||
|
||
Submodules | ||
---------- | ||
|
||
qutip\_qip.decompose.decompose\_single\_qubit\_gate module | ||
---------------------------------------------------------- | ||
|
||
.. automodule:: qutip_qip.decompose.decompose_single_qubit_gate | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: | ||
|
||
Module contents | ||
--------------- | ||
|
||
.. automodule:: qutip_qip.decompose | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .decompose_single_qubit_gate import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from qutip import Qobj | ||
|
||
|
||
class MethodError(Exception): | ||
"""When invalid method is chosen, this error is raised.""" | ||
|
||
pass | ||
|
||
|
||
def check_gate(gate, num_qubits): | ||
"""Verifies input is a valid quantum gate. | ||
Parameters | ||
---------- | ||
gate : :class:`qutip.Qobj` | ||
The matrix that's supposed to be decomposed should be a Qobj. | ||
num_qubits: | ||
Total number of qubits in the circuit. | ||
Raises | ||
------ | ||
TypeError | ||
If the gate is not a Qobj. | ||
ValueError | ||
If the gate is not a unitary operator on qubits. | ||
""" | ||
if not isinstance(gate, Qobj): | ||
raise TypeError("The input matrix is not a Qobj.") | ||
if not gate.check_isunitary(): | ||
raise ValueError("Input is not unitary.") | ||
if gate.dims != [[2] * num_qubits] * 2: | ||
raise ValueError(f"Input is not a unitary on {num_qubits} qubits.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
import numpy as np | ||
import cmath | ||
|
||
|
||
from qutip_qip.decompose._utility import ( | ||
check_gate, | ||
MethodError, | ||
) | ||
|
||
from qutip_qip.circuit import Gate | ||
|
||
|
||
def _angles_for_ZYZ(input_gate): | ||
"""Finds and returns the angles for ZYZ rotation matrix. These are | ||
used to change ZYZ to other combinations. | ||
Parameters | ||
---------- | ||
input_gate : :class:`qutip.Qobj` | ||
The gate matrix that's supposed to be decomposed should be a Qobj. | ||
""" | ||
input_array = input_gate.full() | ||
normalization_constant = np.sqrt(np.linalg.det(input_array)) | ||
global_phase_angle = -cmath.phase(1 / normalization_constant) | ||
input_array = input_array * (1 / normalization_constant) | ||
|
||
# U = np.array([[a,b],[-b*,a*]]) | ||
# If a = x+iy and b = p+iq, alpha = inv_tan(-y/x) - inv_tan(-q/p) | ||
a_negative = np.real(input_array[0][0]) - 1j * np.imag(input_array[0][0]) | ||
b_negative = np.real(input_array[0][1]) - 1j * np.imag(input_array[0][1]) | ||
|
||
# find alpha, beta and theta | ||
alpha = cmath.phase(a_negative) - cmath.phase(b_negative) | ||
beta = cmath.phase(a_negative) + cmath.phase(b_negative) | ||
theta = 2 * np.arctan2(np.absolute(b_negative), np.absolute(a_negative)) | ||
|
||
return (alpha, -theta, beta, global_phase_angle) | ||
|
||
|
||
def _ZYZ_rotation(input_gate): | ||
r"""An input 1-qubit gate is expressed as a product of rotation matrices | ||
:math:`\textrm{R}_z` and :math:`\textrm{R}_y`. | ||
Parameters | ||
---------- | ||
input_gate : :class:`qutip.Qobj` | ||
The matrix that's supposed to be decomposed should be a Qobj. | ||
""" | ||
check_gate(input_gate, num_qubits=1) | ||
alpha, theta, beta, global_phase_angle = _angles_for_ZYZ(input_gate) | ||
|
||
Phase_gate = Gate( | ||
"GLOBALPHASE", | ||
targets=[0], | ||
arg_value=global_phase_angle, | ||
arg_label=r"{:0.2f} \times \pi".format(global_phase_angle / np.pi), | ||
) | ||
Rz_beta = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=beta, | ||
arg_label=r"{:0.2f} \times \pi".format(beta / np.pi), | ||
) | ||
Ry_theta = Gate( | ||
"RY", | ||
targets=[0], | ||
arg_value=theta, | ||
arg_label=r"{:0.2f} \times \pi".format(theta / np.pi), | ||
) | ||
Rz_alpha = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=alpha, | ||
arg_label=r"{:0.2f} \times \pi".format(alpha / np.pi), | ||
) | ||
|
||
return (Rz_alpha, Ry_theta, Rz_beta, Phase_gate) | ||
|
||
|
||
def _ZXZ_rotation(input_gate): | ||
r"""An input 1-qubit gate is expressed as a product of rotation matrices | ||
:math:`\textrm{R}_z` and :math:`\textrm{R}_x`. | ||
Parameters | ||
---------- | ||
input_gate : :class:`qutip.Qobj` | ||
The matrix that's supposed to be decomposed should be a Qobj. | ||
""" | ||
check_gate(input_gate, num_qubits=1) | ||
alpha, theta, beta, global_phase_angle = _angles_for_ZYZ(input_gate) | ||
alpha = alpha - np.pi / 2 | ||
beta = beta + np.pi / 2 | ||
# theta and global phase are same as ZYZ values | ||
|
||
Phase_gate = Gate( | ||
"GLOBALPHASE", | ||
targets=[0], | ||
arg_value=global_phase_angle, | ||
arg_label=r"{:0.2f} \times \pi".format(global_phase_angle / np.pi), | ||
) | ||
Rz_alpha = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=alpha, | ||
arg_label=r"{:0.2f} \times \pi".format(alpha / np.pi), | ||
) | ||
Rx_theta = Gate( | ||
"RX", | ||
targets=[0], | ||
arg_value=theta, | ||
arg_label=r"{:0.2f} \times \pi".format(theta / np.pi), | ||
) | ||
Rz_beta = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=beta, | ||
arg_label=r"{:0.2f} \times \pi".format(beta / np.pi), | ||
) | ||
|
||
return (Rz_alpha, Rx_theta, Rz_beta, Phase_gate) | ||
|
||
|
||
# Functions for ABC_decomposition | ||
|
||
|
||
def _ZYZ_pauli_X(input_gate): | ||
"""Returns a 1 qubit unitary as a product of ZYZ rotation matrices and | ||
Pauli X.""" | ||
check_gate(input_gate, num_qubits=1) | ||
alpha, theta, beta, global_phase_angle = _angles_for_ZYZ(input_gate) | ||
|
||
Phase_gate = Gate( | ||
"GLOBALPHASE", | ||
targets=[0], | ||
arg_value=global_phase_angle, | ||
arg_label=r"{:0.2f} \times \pi".format(global_phase_angle / np.pi), | ||
) | ||
Rz_A = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=alpha, | ||
arg_label=r"{:0.2f} \times \pi".format(alpha / np.pi), | ||
) | ||
Ry_A = Gate( | ||
"RY", | ||
targets=[0], | ||
arg_value=theta / 2, | ||
arg_label=r"{:0.2f} \times \pi".format(theta / np.pi), | ||
) | ||
Pauli_X = Gate("X", targets=[0]) | ||
Ry_B = Gate( | ||
"RY", | ||
targets=[0], | ||
arg_value=-theta / 2, | ||
arg_label=r"{:0.2f} \times \pi".format(-theta / np.pi), | ||
) | ||
Rz_B = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=-(alpha + beta) / 2, | ||
arg_label=r"{:0.2f} \times \pi".format(-(alpha + beta) / (2*np.pi)), | ||
) | ||
Rz_C = Gate( | ||
"RZ", | ||
targets=[0], | ||
arg_value=(-alpha + beta) / 2, | ||
arg_label=r"{:0.2f} \times \pi".format((-alpha + beta) / (2*np.pi)), | ||
) | ||
|
||
return (Rz_A, Ry_A, Pauli_X, Ry_B, Rz_B, Pauli_X, Rz_C, Phase_gate) | ||
|
||
|
||
_single_decompositions_dictionary = { | ||
"ZYZ": _ZYZ_rotation, | ||
"ZXZ": _ZXZ_rotation, | ||
"ZYZ_PauliX": _ZYZ_pauli_X, | ||
} # other combinations to add here | ||
|
||
|
||
def decompose_one_qubit_gate(input_gate, method): | ||
r""" An input 1-qubit gate is expressed as a product of rotation matrices | ||
:math:`\textrm{R}_i` and :math:`\textrm{R}_j` or as a product of rotation | ||
matrices :math:`\textrm{R}_i` and :math:`\textrm{R}_j` and a | ||
Pauli :math:`\sigma_k`. | ||
Here, :math:`i \neq j` and :math:`i, j, k \in {x, y, z}`. | ||
Based on Lemma 4.1 and Lemma 4.3 of | ||
https://arxiv.org/abs/quant-ph/9503016v1 respectively. | ||
.. math:: | ||
U = \begin{bmatrix} | ||
a & b \\ | ||
-b^* & a^* \\ | ||
\end{bmatrix} = \textrm{R}_i(\alpha) \textrm{R}_j(\theta) \textrm{R}_i(\beta) = \textrm{A} \sigma_k \textrm{B} \sigma_k \textrm{C} | ||
Here, | ||
* :math:`\textrm{A} = \textrm{R}_i(\alpha) \textrm{R}_j\left(\frac{\theta}{2} \right)` | ||
* :math:`\textrm{B} = \textrm{R}_j \left(\frac{-\theta}{2} \right)\textrm{R}_i \left(\frac{- \left(\alpha + \beta \right)}{2} \right)` | ||
* :math:`\textrm{C} = \textrm{R}_i \left(\frac{\left(-\alpha + \beta\right)}{2} \right)` | ||
Parameters | ||
---------- | ||
input_gate : :class:`qutip.Qobj` | ||
The matrix that's supposed to be decomposed should be a Qobj. | ||
method : string | ||
Name of the preferred decomposition method | ||
.. list-table:: | ||
:widths: auto | ||
:header-rows: 1 | ||
* - Method Key | ||
- Method | ||
* - ZYZ | ||
- :math:`\textrm{R}_z(\alpha) \textrm{R}_y(\theta) \textrm{R}_z(\beta)` | ||
* - ZXZ | ||
- :math:`\textrm{R}_z(\alpha) \textrm{R}_x(\theta) \textrm{R}_z(\beta)` | ||
* - ZYZ_PauliX | ||
- :math:`\textrm{A} \sigma_k \textrm{B} \sigma_k \textrm{C}` :math:`\forall k =x, i =z, j=y` | ||
.. note:: | ||
This function is under construction. As more combinations are | ||
added, above table will be updated with their respective keys. | ||
Returns | ||
------- | ||
tuple | ||
The gates in the decomposition are returned as a tuple of :class:`Gate` | ||
objects. | ||
When the input gate is decomposed to product of rotation matrices - | ||
tuple will contain 4 elements per each :math:`1 \times 1` | ||
qubit gate - :math:`\textrm{R}_i(\alpha)`, :math:`\textrm{R}_j(\theta)` | ||
, :math:`\textrm{R}_i(\beta)`, and some global phase gate. | ||
When the input gate is decomposed to product of rotation matrices and | ||
Pauli - tuple will contain 6 elements per each :math:`1 \times 1` | ||
qubit gate - 2 gates forming :math:`\textrm{A}`, 2 gates forming | ||
:math:`\textrm{B}`, 1 gates forming :math:`\textrm{C}`, and some global | ||
phase gate. | ||
""" | ||
check_gate(input_gate, num_qubits=1) | ||
f = _single_decompositions_dictionary.get(method, None) | ||
if f is None: | ||
raise MethodError(f"Invalid decomposition method: {method!r}") | ||
return f(input_gate) |
Oops, something went wrong.