Skip to content

Commit

Permalink
style
Browse files Browse the repository at this point in the history
  • Loading branch information
purva-thakre committed Jul 15, 2021
1 parent 9daf3e4 commit b554278
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 149 deletions.
7 changes: 5 additions & 2 deletions src/qutip_qip/decompose/_utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@

from qutip import Qobj


class MethodError(Exception):
"""When invalid method is chosen, this error is raised.
"""
"""When invalid method is chosen, this error is raised."""

pass


class GateError(Exception):
"""When chosen method cannot be applied to the input gate, this error
is raised.
"""

pass


Expand Down
213 changes: 145 additions & 68 deletions src/qutip_qip/decompose/single_qubit_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
import cmath

from qutip import Qobj
from qutip_qip.decompose._utility import (check_gate, MethodError, GateError)
from qutip_qip.decompose._utility import check_gate, MethodError, GateError

from qutip_qip.circuit import QubitCircuit, Gate


# Functions for decompose_to_rotation_matrices
def _angles_for_ZYZ(input_gate, num_qubits=1):
""" Finds and returns the angles for ZYZ rotation matrix. These are
"""Finds and returns the angles for ZYZ rotation matrix. These are
used to change ZYZ to other combinations.
Parameters
Expand All @@ -20,25 +20,24 @@ def _angles_for_ZYZ(input_gate, num_qubits=1):
check_gate(input_gate, num_qubits)
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)
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])
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)
theta = 2 * np.arctan2(np.absolute(b_negative), np.absolute(a_negative))

return (alpha, -theta, beta, global_phase_angle)


def _ZYZ_rotation(input_gate, target, num_qubits=1):
r""" An input 1-qubit gate is expressed as a product of rotation matrices
r"""An input 1-qubit gate is expressed as a product of rotation matrices
:math:`\textrm{R}_z` and :math:`\textrm{R}_y`.
Parameters
Expand All @@ -50,23 +49,44 @@ def _ZYZ_rotation(input_gate, target, num_qubits=1):
alpha = angle_list[0]
beta = angle_list[2]
theta = angle_list[1]
global_phase_angle=angle_list[3]
global_phase_angle = angle_list[3]

# for string in circuit diagram
alpha_string = alpha/np.pi
beta_string = beta/np.pi
theta_string = theta/np.pi
global_phase_angle_string = global_phase_angle/np.pi
alpha_string = alpha / np.pi
beta_string = beta / np.pi
theta_string = theta / np.pi
global_phase_angle_string = global_phase_angle / np.pi

Phase_gate = Gate(
"GLOBALPHASE",
targets=[target],
arg_value=global_phase_angle,
arg_label=r"{:0.2f} \times \pi".format(global_phase_angle_string),
)
Rz_beta = Gate(
"RZ",
targets=[target],
arg_value=beta,
arg_label=r"{:0.2f} \times \pi".format(beta_string),
)
Ry_theta = Gate(
"RY",
targets=[target],
arg_value=theta,
arg_label=r"{:0.2f} \times \pi".format(theta_string),
)
Rz_alpha = Gate(
"RZ",
targets=[target],
arg_value=alpha,
arg_label=r"{:0.2f} \times \pi".format(alpha_string),
)

return (Rz_alpha, Ry_theta, Rz_beta, Phase_gate)

Phase_gate = Gate("GLOBALPHASE",targets=[target], arg_value=global_phase_angle, arg_label=r'{:0.2f} \times \pi'.format(global_phase_angle_string))
Rz_beta = Gate("RZ",targets=[target], arg_value=beta, arg_label=r'{:0.2f} \times \pi'.format(beta_string))
Ry_theta = Gate("RY",targets=[target], arg_value=theta, arg_label=r'{:0.2f} \times \pi'.format(theta_string))
Rz_alpha = Gate("RZ",targets=[target], arg_value=alpha, arg_label=r'{:0.2f} \times \pi'.format(alpha_string))

return(Rz_alpha, Ry_theta, Rz_beta, Phase_gate)

def _ZXZ_rotation(input_gate, target, num_qubits=1):
r""" An input 1-qubit gate is expressed as a product of rotation matrices
r"""An input 1-qubit gate is expressed as a product of rotation matrices
:math:`\textrm{R}_z` and :math:`\textrm{R}_x`.
Parameters
Expand All @@ -76,29 +96,51 @@ def _ZXZ_rotation(input_gate, target, num_qubits=1):
"""
angle_list = _angles_for_ZYZ(input_gate, num_qubits)
alpha = angle_list[0]
alpha = alpha - np.pi/2
alpha = alpha - np.pi / 2
beta = angle_list[2]
beta = beta + np.pi/2
beta = beta + np.pi / 2
theta = angle_list[1]
global_phase_angle=angle_list[3]
global_phase_angle = angle_list[3]

# for string in circuit diagram
alpha_string = alpha/np.pi
beta_string = beta/np.pi
theta_string = theta/np.pi
global_phase_angle_string = global_phase_angle/np.pi
alpha_string = alpha / np.pi
beta_string = beta / np.pi
theta_string = theta / np.pi
global_phase_angle_string = global_phase_angle / np.pi

Phase_gate = Gate(
"GLOBALPHASE",
targets=[target],
arg_value=global_phase_angle,
arg_label=r"{:0.2f} \times \pi".format(global_phase_angle_string),
)
Rz_alpha = Gate(
"RZ",
targets=[target],
arg_value=alpha,
arg_label=r"{:0.2f} \times \pi".format(alpha_string),
)
Rx_theta = Gate(
"RX",
targets=[target],
arg_value=theta,
arg_label=r"{:0.2f} \times \pi".format(theta_string),
)
Rz_beta = Gate(
"RZ",
targets=[target],
arg_value=beta,
arg_label=r"{:0.2f} \times \pi".format(beta_string),
)

return (Rz_alpha, Rx_theta, Rz_beta, Phase_gate)


_rotation_matrices_dictionary = {
"ZYZ": _ZYZ_rotation,
"ZXZ": _ZXZ_rotation,
} # other combinations to add here

Phase_gate = Gate("GLOBALPHASE",targets=[target], arg_value=global_phase_angle, arg_label=r'{:0.2f} \times \pi'.format(global_phase_angle_string))
Rz_alpha = Gate("RZ",targets=[target], arg_value=alpha, arg_label=r'{:0.2f} \times \pi'.format(alpha_string))
Rx_theta = Gate("RX",targets=[target], arg_value=theta, arg_label=r'{:0.2f} \times \pi'.format(theta_string))
Rz_beta = Gate("RZ",targets=[target], arg_value=beta, arg_label=r'{:0.2f} \times \pi'.format(beta_string))

return(Rz_alpha, Rx_theta, Rz_beta, Phase_gate)


_rotation_matrices_dictionary ={"ZYZ": _ZYZ_rotation,
"ZXZ": _ZXZ_rotation,
} # other combinations to add here

def decompose_to_rotation_matrices(input_gate, method, num_qubits, target=0):
r""" An input 1-qubit gate is expressed as a product of rotation matrices
Expand Down Expand Up @@ -152,48 +194,82 @@ def decompose_to_rotation_matrices(input_gate, method, num_qubits, target=0):
try:
assert num_qubits == 1
except AssertionError:
if target is None and num_qubits >1:
raise GateError("This method is valid for single qubit gates only. Provide a target qubit for single qubit gate.")

if target is None and num_qubits > 1:
raise GateError(
"This method is valid for single qubit gates only. Provide a target qubit for single qubit gate."
)

key = _rotation_matrices_dictionary.keys()
if str(method) in key:
method = _rotation_matrices_dictionary[str(method)]
return(method(input_gate, target, 1))
return method(input_gate, target, 1)

else:
raise MethodError("Invalid method chosen.")


# Functions for ABC_decomposition


def _ZYZ_pauli_X(input_gate, target, num_qubits=1):
"""Returns a 1 qubit unitary as a product of ZYZ rotation matrices and Pauli X.
"""
"""Returns a 1 qubit unitary as a product of ZYZ rotation matrices and Pauli X."""
angle_list = _angles_for_ZYZ(input_gate, num_qubits)
alpha = angle_list[0]
beta = angle_list[2]
theta = angle_list[1]
global_phase_angle=angle_list[3]
global_phase_angle = angle_list[3]

# for string in circuit diagram
alpha_string = alpha/np.pi
beta_string = beta/np.pi
theta_string = theta/np.pi
global_phase_angle_string = global_phase_angle/np.pi

Phase_gate = Gate("GLOBALPHASE",targets=[0], arg_value=global_phase_angle, arg_label=r'{:0.2f} \times \pi'.format(global_phase_angle_string))
Rz_A = Gate("RZ",targets=[target], arg_value=alpha, arg_label=r'{:0.2f} \times \pi'.format(alpha_string))
Ry_A = Gate("RY",targets=[target], arg_value=theta/2, arg_label=r'{:0.2f} \times \pi'.format(theta_string/2))
Pauli_X = Gate("X",targets=[target])
Ry_B = Gate("RY",targets=[target], arg_value=-theta/2, arg_label=r'{:0.2f} \times \pi'.format(-theta_string/2))
Rz_B = Gate("RZ",targets=[target], arg_value=-(alpha+beta)/2, arg_label=r'{:0.2f} \times \pi'.format(-(alpha_string+beta_string)/2))
Rz_C = Gate("RZ",targets=[target], arg_value=(-alpha+beta)/2, arg_label=r'{:0.2f} \times \pi'.format((-alpha_string+beta_string)/2))
alpha_string = alpha / np.pi
beta_string = beta / np.pi
theta_string = theta / np.pi
global_phase_angle_string = global_phase_angle / np.pi

Phase_gate = Gate(
"GLOBALPHASE",
targets=[0],
arg_value=global_phase_angle,
arg_label=r"{:0.2f} \times \pi".format(global_phase_angle_string),
)
Rz_A = Gate(
"RZ",
targets=[target],
arg_value=alpha,
arg_label=r"{:0.2f} \times \pi".format(alpha_string),
)
Ry_A = Gate(
"RY",
targets=[target],
arg_value=theta / 2,
arg_label=r"{:0.2f} \times \pi".format(theta_string / 2),
)
Pauli_X = Gate("X", targets=[target])
Ry_B = Gate(
"RY",
targets=[target],
arg_value=-theta / 2,
arg_label=r"{:0.2f} \times \pi".format(-theta_string / 2),
)
Rz_B = Gate(
"RZ",
targets=[target],
arg_value=-(alpha + beta) / 2,
arg_label=r"{:0.2f} \times \pi".format(-(alpha_string + beta_string) / 2),
)
Rz_C = Gate(
"RZ",
targets=[target],
arg_value=(-alpha + beta) / 2,
arg_label=r"{:0.2f} \times \pi".format((-alpha_string + beta_string) / 2),
)

return (Rz_A, Ry_A, Pauli_X, Ry_B, Rz_B, Pauli_X, Rz_C, Phase_gate)


_rotation_pauli_matrices_dictionary = {
"ZYZ_PauliX": _ZYZ_pauli_X,
} # other combinations to add here

return(Rz_A, Ry_A, Pauli_X, Ry_B, Rz_B, Pauli_X, Rz_C, Phase_gate)


_rotation_pauli_matrices_dictionary ={"ZYZ_PauliX":_ZYZ_pauli_X,
} # other combinations to add here

def ABC_decomposition(input_gate, method, num_qubits, target=0):
r""" An input 1-qubit gate is expressed as a product of rotation matrices
Expand Down Expand Up @@ -262,14 +338,15 @@ def ABC_decomposition(input_gate, method, num_qubits, target=0):
try:
assert num_qubits == 1
except AssertionError:
if target is None and num_qubits >1:
raise GateError("This method is valid for single qubit gates only. Provide a target qubit for single qubit gate.")

if target is None and num_qubits > 1:
raise GateError(
"This method is valid for single qubit gates only. Provide a target qubit for single qubit gate."
)

key = _rotation_pauli_matrices_dictionary.keys()
if str(method) in key:
method = _rotation_pauli_matrices_dictionary[str(method)]
return(method(input_gate, target, 1))
return method(input_gate, target, 1)

else:
raise MethodError("Invalid method chosen.")
65 changes: 0 additions & 65 deletions tests/decompose/test_single_decompositions.py

This file was deleted.

Loading

0 comments on commit b554278

Please sign in to comment.