Skip to content

Commit

Permalink
all tests
Browse files Browse the repository at this point in the history
  • Loading branch information
purva-thakre committed Jul 5, 2021
1 parent a5e01da commit 196be45
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 19 deletions.
11 changes: 10 additions & 1 deletion src/qutip_qip/decompositions/decompositions_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,24 @@
import numpy as np

def decomposed_gates_to_circuit(decomposed_gate,num_of_qubits):
"""This function takes the input from a decomposition function and creates
a circuit diagram.
"""
# 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_of_qubits, reverse_states=False)
for i in decomposed_gate:
q_circuit.add_gate(i)
return(q_circuit)
else:
raise TypeError("Input is not a list of gates.")
raise TypeError("Input is not a tuple of gates.")

def matrix_of_decomposed_gates(quantum_circuit):
"""Evaluates all the gates in the circuit.
"""
if isinstance(quantum_circuit, QubitCircuit) == True:
gate_list = quantum_circuit.propagators()
matrix_of_all_gates = gate_sequence_product(gate_list)
Expand Down
12 changes: 8 additions & 4 deletions src/qutip_qip/decompositions/general_decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
from qutip.qobj 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_input(input_gate):
Expand Down Expand Up @@ -89,7 +94,8 @@ def convert_qobj_gate_to_array(input_gate):
input_to_array = Qobj.full(input_gate)
try:
isinstance(input_to_array, np.ndarray)
except:
except: # Not sure if this error has to be included. If an error occurs,
# then it ought to covered in the main qutip module.
raise ConversionError("Input Qobj could not be converted to a numpy array.")
return(input_to_array)
else:
Expand All @@ -109,9 +115,7 @@ def extract_global_phase(input_gate, num_of_qubits):
if check_input_shape(input_gate, num_of_qubits) == True:
input_array = convert_qobj_gate_to_array(input_gate)
determinant_of_input = np.linalg.det(input_array)
y = np.imag(determinant_of_input)
x = np.real(determinant_of_input)
global_phase_angle = np.arctan2(y,x)
global_phase_angle = cmath.phase(determinant_of_input)
global_phase_angle = global_phase_angle/(2**num_of_qubits)
return(global_phase_angle)
else:
Expand Down
26 changes: 14 additions & 12 deletions src/qutip_qip/decompositions/single_decompositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
import cmath

from qutip.qobj import Qobj
from .general_decompositions import (check_input, check_input_shape,
from qutip_qip.decompositions.general_decompositions import (check_input, check_input_shape,
convert_qobj_gate_to_array, extract_global_phase, MethodError, GateError)

from qutip_qip.operations import *
from qutip_qip.circuit import QubitCircuit, Gate


# Functions for decompose_to_rotation_matrices
def _angles_for_ZYZ(input_gate, 1):
single_qubit = 1
def _angles_for_ZYZ(input_gate, single_qubit):
""" Finds and returns the angles for ZYZ rotation matrix. These are
used to change ZYZ to other combinations.
"""
global_phase_angle = extract_global_phase(input_gate,1)
input_array = cmath.rect(1,global_phase_angle)*convert_qobj_gate_to_array(input_gate)

global_phase_angle = extract_global_phase(input_gate,single_qubit)
input_array = convert_qobj_gate_to_array(input_gate)
input_array = input_array/cmath.rect(1,global_phase_angle)
# separate all the elements
a = input_array[0][0]
b = input_array[0][1]
Expand All @@ -27,7 +28,7 @@ def _angles_for_ZYZ(input_gate, 1):
alpha = cmath.phase(-a_star) + cmath.phase(b_star)
beta = cmath.phase(-a_star) - cmath.phase(b_star)
theta = 2*np.arctan2(np.absolute(b_star), np.absolute(a))
return(alpha, theta, beta, global_phase_angle)
return(alpha, theta, beta, np.pi+global_phase_angle)



Expand All @@ -40,7 +41,7 @@ def _ZYZ_rotation(input_gate, num_of_qubits, target):
input_gate : :class:`qutip.Qobj`
The matrix that's supposed to be decomposed should be a Qobj.
"""
angle_list = _angles_for_ZYZ(input_gate, 1)
angle_list = _angles_for_ZYZ(input_gate, single_qubit)
alpha = angle_list[0]
beta = angle_list[2]
theta = angle_list[1]
Expand Down Expand Up @@ -68,7 +69,7 @@ def _ZXZ_rotation(input_gate, num_of_qubits, target):
input_gate : :class:`qutip.Qobj`
The matrix that's supposed to be decomposed should be a Qobj.
"""
angle_list = _angles_for_ZYZ(input_gate, 1)
angle_list = _angles_for_ZYZ(input_gate, single_qubit)
alpha = angle_list[0]
alpha = alpha - np.pi/2
beta = angle_list[2]
Expand Down Expand Up @@ -194,16 +195,17 @@ def ABC_decomposition(input_gate, num_of_qubits, target):
raise GateError("This method is valid for single qubit gates only. Provide a target qubit for larger circuits. ")


global_phase_angle = extract_global_phase(input_gate,1)
global_phase_angle_string = global_phase_angle/np.pi
input_array = cmath.rect(1,global_phase_angle)*convert_qobj_gate_to_array(input_gate)

global_phase_angle = extract_global_phase(input_gate,single_qubit)
input_array = convert_qobj_gate_to_array(input_gate)
input_array = input_array/cmath.rect(1,global_phase_angle)
# separate all the elements
a = input_array[0][0]
b = input_array[0][1]
a_star = input_array[1][1]
b_star = input_array[1][0]

global_phase_angle=np.pi+global_phase_angle
global_phase_angle_string = global_phase_angle/np.pi
# find alpha, beta and theta
alpha = cmath.phase(-a_star) + cmath.phase(b_star)
alpha_string = alpha/np.pi # for string in circuit diagram
Expand Down
55 changes: 55 additions & 0 deletions tests/gate_decompositions/test_decomposition_extras.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import numpy as np
import cmath
import pytest

from qutip.qobj import Qobj
from qutip_qip.operations import *
from qutip_qip.circuit import QubitCircuit, Gate

from qutip_qip.decompositions.general_decompositions import (check_input,
check_input_shape, convert_qobj_gate_to_array, extract_global_phase)

from qutip_qip.decompositions.single_decompositions import (_ZYZ_rotation, _ZXZ_rotation, _rotation_matrices_dictionary,
ABC_decomposition, decompose_to_rotation_matrices)

from qutip_qip.decompositions.decompositions_extras import (decomposed_gates_to_circuit, matrix_of_decomposed_gates)


# Tests for decomposed_gates_to_circuit
@pytest.mark.parametrize("invalid_input",[np.array([[1,1],[1,1]]),([[1,1],[1,1]]),1.5,3])
def test_decomposed_gates_to_circuit(invalid_input):
"""Checks if correct error is raised when input is anything but a tuple of gates.
"""
with pytest.raises(TypeError,match="Input is not a tuple of gates."):
decomposed_gates_to_circuit(invalid_input,1)

H = Qobj([[1/np.sqrt(2),1/np.sqrt(2)],[1/np.sqrt(2),-1/np.sqrt(2)]])
sigmax = Qobj([[0,1],[1,0]])
H_zyz_gates = _ZYZ_rotation(H, 1, 0)
sigmax_zyz_gates = _ZYZ_rotation(sigmax, 1, 0)

@pytest.mark.parametrize("valid_input",[H_zyz_gates,sigmax_zyz_gates])
def test_decomposed_gates_to_circuit(valid_input):
"""Checks if output is of type QubitCircuit.
"""
assert(isinstance(decomposed_gates_to_circuit(valid_input,1),QubitCircuit))


H_zyz_quantum_circuit = decomposed_gates_to_circuit(H_zyz_gates, 1)
sigmax_zyz_quantum_circuit = decomposed_gates_to_circuit(sigmax_zyz_gates, 1)
sigmax_zyz_output = (sigmax_zyz_quantum_circuit)
# Tests for matrix_of_decomposed_gates
@pytest.mark.parametrize("invalid_input",[np.array([[1,1],[1,1]]),([[1,1],[1,1]]),1.5,3])
def test_matrix_of_decomposed_gates(invalid_input):
"""Checks if correct error is raised when input is anything but a quantum circuit.
"""
with pytest.raises(TypeError,match="Input is not of type QubitCircuit."):
matrix_of_decomposed_gates(invalid_input)


@pytest.mark.parametrize("valid_input",[H_zyz_quantum_circuit,sigmax_zyz_quantum_circuit])
def test_matrix_of_decomposed_gates(valid_input):
"""Checks if final output is a Qobj.
"""
final_output=matrix_of_decomposed_gates(valid_input)
assert(isinstance(final_output, Qobj))
37 changes: 35 additions & 2 deletions tests/gate_decompositions/test_general_decompositions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@

import numpy as np
import cmath
import pytest

from qutip.qobj import Qobj
from .general_decompositions import (check_input, check_input_shape,
convert_qobj_gate_to_array, extract_global_phase)
from qutip_qip.decompositions.general_decompositions import (check_input, check_input_shape,
convert_qobj_gate_to_array, extract_global_phase, MethodError, GateError)

from qutip_qip.operations import *
from qutip_qip.circuit import QubitCircuit, Gate
Expand Down Expand Up @@ -45,6 +46,18 @@ def test_check_input_non_qobj(non_unitary):
assert(check_input(non_unitary)==False)

# Tests for check_input_shape
@pytest.mark.parametrize("unitary",[Qobj([[1,0],[0,-1]])])
def test_check_input_shape_unitary_input(unitary):
"""Checks if shape of input is correctly identified.
"""
assert(check_input_shape(unitary,1)==True)

@pytest.mark.parametrize("non_unitary",[Qobj([[1,1],[0,1]])])
def test_check_input_non_qobj(non_unitary):
"""Checks if non-unitary input is correctly identified.
"""
with pytest.raises(ValueError, match="Input is not unitary."):
check_input_shape(non_unitary,1)

# Tests for convert_qobj_gate_to_array
@pytest.mark.parametrize("valid_input",[Qobj([[1,0,0],[0,1,0],[0,0,1]]),rx(np.pi/2,3),z_gate(3),t_gate(3)])
Expand All @@ -53,4 +66,24 @@ def test_one_qutrit_gates(valid_input):
"""
assert(isinstance(convert_qobj_gate_to_array(valid_input),np.ndarray))

@pytest.mark.parametrize("non_unitary",[Qobj([[1,1],[0,1]])])
def test_convert_qobj_gate_to_array(non_unitary):
"""Checks if non-unitary input is correctly identified.
"""
with pytest.raises(ValueError, match="Input is not unitary."):
convert_qobj_gate_to_array(non_unitary)

# Tests for extract_global_phase
def test_extract_global_phase_valid_input():
"""Checks if global phase is correctly identified for multiplication.
"""
H = Qobj([[1/np.sqrt(2),1/np.sqrt(2)],[1/np.sqrt(2),-1/np.sqrt(2)]])
H_global_phase = extract_global_phase(H,1)
assert(H_global_phase == np.pi/2)

def test_extract_global_phase_valid_input_incorrect_number_of_qubits():
"""Checks if global phase is correctly identified for multiplication.
"""
H = Qobj([[1/np.sqrt(2),1/np.sqrt(2)],[1/np.sqrt(2),-1/np.sqrt(2)]])
with pytest.raises(GateError, match="Gate shape does not match to the number of qubits in the circuit. "):
extract_global_phase(H,2)
59 changes: 59 additions & 0 deletions tests/gate_decompositions/test_single_decompositions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import numpy as np
import cmath
import pytest

from qutip.qobj import Qobj
from qutip.metrics import average_gate_fidelity


from qutip_qip.decompositions.single_decompositions import (_ZYZ_rotation, _ZXZ_rotation,
ABC_decomposition, decompose_to_rotation_matrices)

from qutip_qip.decompositions.decompositions_extras import (decomposed_gates_to_circuit, matrix_of_decomposed_gates)

# Fidelity closer to 1 means the two states are similar to each other
H = Qobj([[1/np.sqrt(2),1/np.sqrt(2)],[1/np.sqrt(2),-1/np.sqrt(2)]])
sigmax = Qobj([[0,1],[1,0]])
sigmay = Qobj([[0,-1j],[1j,0]])
sigmaz = Qobj([[1,0],[0,-1]])
SQRTNOT = Qobj([[1/np.sqrt(2),-1j/np.sqrt(2)],[-1j/np.sqrt(2),1/np.sqrt(2)]])
T = Qobj([[1,0],[0,cmath.rect(1, np.pi/4)]])
S = Qobj([[1,0],[0,1j]])

@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T])
@pytest.mark.parametrize("method",[_ZYZ_rotation, _ZXZ_rotation, ABC_decomposition])
def test_single_qubit_to_rotations(gate, method):
"""Initial matrix and product of final decompositions are same within some phase."""
target = 0
gate_list = method(gate,1,target)
decomposed_gates_circuit = decomposed_gates_to_circuit(gate_list,1)
decomposed_gates_final_matrix = matrix_of_decomposed_gates(decomposed_gates_circuit)
fidelity_of_input_output = average_gate_fidelity(gate, decomposed_gates_final_matrix)
assert(np.isclose(fidelity_of_input_output,1.0))

@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T])
@pytest.mark.parametrize("method",['ZXZ','ZYZ'])
def test_check_single_qubit_to_decompose_to_rotations(gate, method):
"""Initial matrix and product of final decompositions are same within some phase."""
target = 0
gate_list = decompose_to_rotation_matrices(gate,1,target,method)
decomposed_gates_circuit = decomposed_gates_to_circuit(gate_list,1)
decomposed_gates_final_matrix = matrix_of_decomposed_gates(decomposed_gates_circuit)
fidelity_of_input_output = average_gate_fidelity(gate, decomposed_gates_final_matrix)
assert(np.isclose(fidelity_of_input_output,1.0))

@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T])
@pytest.mark.parametrize("method",[_ZYZ_rotation, _ZXZ_rotation, ABC_decomposition])
def test_output_is_tuple(gate, method):
"""Initial matrix and product of final decompositions are same within some phase."""
target = 0
gate_list = method(gate,1,target)
assert(isinstance(gate_list, tuple))

@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T])
@pytest.mark.parametrize("method",['ZXZ','ZYZ'])
def test_check_single_qubit_to_decompose_to_rotations(gate, method):
"""Initial matrix and product of final decompositions are same within some phase."""
target = 0
gate_list = decompose_to_rotation_matrices(gate,1,target,method)
assert(isinstance(gate_list, tuple))

0 comments on commit 196be45

Please sign in to comment.