Skip to content

Commit

Permalink
Merge 6484be2 into 23f79eb
Browse files Browse the repository at this point in the history
  • Loading branch information
purva-thakre committed Jul 15, 2021
2 parents 23f79eb + 6484be2 commit d489b2e
Show file tree
Hide file tree
Showing 9 changed files with 570 additions and 2 deletions.
16 changes: 16 additions & 0 deletions doc/source/_apidoc/qutip_qip.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ qutip\_qip.circuit\_latex module
:undoc-members:
:show-inheritance:

qutip\_qip.decompose module
---------------------------

.. automodule:: qutip_qip.decompose
:members:
:undoc-members:
:show-inheritance:

qutip\_qip.gates module
-----------------------

Expand Down Expand Up @@ -72,6 +80,14 @@ qutip\_qip.qubits module
:undoc-members:
:show-inheritance:

qutip\_qip.version module
-------------------------

.. automodule:: qutip_qip.version
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

Expand Down
1 change: 1 addition & 0 deletions src/qutip_qip/_decomposition_functions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._single_qubit_gate import *
198 changes: 198 additions & 0 deletions src/qutip_qip/_decomposition_functions/_single_qubit_gate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import numpy as np
import cmath

from qutip import Qobj
from qutip_qip._decomposition_functions._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
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.
"""
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)

# 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, target, num_qubits=1):
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.
"""
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]

# 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=[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
: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.
"""
angle_list = _angles_for_ZYZ(input_gate, num_qubits)
alpha = angle_list[0]
alpha = alpha - np.pi / 2
beta = angle_list[2]
beta = beta + np.pi / 2
theta = angle_list[1]
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=[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)


# 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."""
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]

# 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),
)

return (Rz_A, Ry_A, Pauli_X, Ry_B, Rz_B, Pauli_X, Rz_C, Phase_gate)
42 changes: 42 additions & 0 deletions src/qutip_qip/_decomposition_functions/_utility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import numpy as np
import cmath

from qutip import Qobj


class MethodError(Exception):
"""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


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:
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.")
29 changes: 28 additions & 1 deletion src/qutip_qip/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
)
from .operations.gates import _gate_label
from qutip import basis, ket2dm, qeye
from qutip.qobj import Qobj
from qutip import Qobj
from qutip.measurement import measurement_statistics


Expand Down Expand Up @@ -2130,3 +2130,30 @@ def _apply_measurement(self, operation):
else:
raise NotImplementedError(
"mode {} is not available.".format(self.mode))

# For Decomposition functions
def decomposed_gates_to_circuit(decomposed_gate,num_qubits):
"""This function takes the input from a decomposition function and returns
a quantum circuit.
"""
# as decomposed_gate contains information about targets/control, there's no
# additional input of target here.
# In addition, there's no check if the gates are valid for number of qubits
# because this is done in a decomposition function before output.
if isinstance(decomposed_gate,tuple) == True:
q_circuit = QubitCircuit(num_qubits, reverse_states=False)
for i in decomposed_gate:
q_circuit.add_gate(i)
return(q_circuit)
else:
raise TypeError("Input is not a tuple of gates.")

def compute_unitary(quantum_circuit):
"""Evaluates all the gates in the quantum circuit.
"""
if isinstance(quantum_circuit, QubitCircuit) == True:
gate_list = quantum_circuit.propagators()
matrix_of_all_gates = gate_sequence_product(gate_list)
return(matrix_of_all_gates)
else:
raise TypeError("Input is not of type QubitCircuit.")
Loading

0 comments on commit d489b2e

Please sign in to comment.