diff --git a/doc/source/_apidoc/qutip_qip.decompose.rst b/doc/source/_apidoc/qutip_qip.decompose.rst new file mode 100644 index 000000000..8b59f0d71 --- /dev/null +++ b/doc/source/_apidoc/qutip_qip.decompose.rst @@ -0,0 +1,21 @@ +qutip\_qip.decompose package +============================ + +Submodules +---------- + +qutip\_qip.decompose.single\_qubit\_gate module +----------------------------------------------- + +.. automodule:: qutip_qip.decompose.single_qubit_gate + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: qutip_qip.decompose + :members: + :undoc-members: + :show-inheritance: diff --git a/doc/source/_apidoc/qutip_qip.rst b/doc/source/_apidoc/qutip_qip.rst index a438e8c89..1c3bf8d5b 100644 --- a/doc/source/_apidoc/qutip_qip.rst +++ b/doc/source/_apidoc/qutip_qip.rst @@ -9,6 +9,7 @@ Subpackages qutip_qip.algorithms qutip_qip.compiler + qutip_qip.decompose qutip_qip.device qutip_qip.operations qutip_qip.transpiler @@ -72,6 +73,14 @@ qutip\_qip.qubits module :undoc-members: :show-inheritance: +qutip\_qip.version module +------------------------- + +.. automodule:: qutip_qip.version + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/src/qutip_qip/circuit.py b/src/qutip_qip/circuit.py index 5a3f33c66..0a6c4e04a 100644 --- a/src/qutip_qip/circuit.py +++ b/src/qutip_qip/circuit.py @@ -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 @@ -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.") diff --git a/src/qutip_qip/decompose/__init__.py b/src/qutip_qip/decompose/__init__.py new file mode 100644 index 000000000..590687920 --- /dev/null +++ b/src/qutip_qip/decompose/__init__.py @@ -0,0 +1 @@ +from .single_qubit_gate import * diff --git a/src/qutip_qip/decompose/_utility.py b/src/qutip_qip/decompose/_utility.py new file mode 100644 index 000000000..4b6b5175f --- /dev/null +++ b/src/qutip_qip/decompose/_utility.py @@ -0,0 +1,39 @@ +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.") diff --git a/src/qutip_qip/decompose/single_qubit_gate.py b/src/qutip_qip/decompose/single_qubit_gate.py new file mode 100644 index 000000000..c89f43836 --- /dev/null +++ b/src/qutip_qip/decompose/single_qubit_gate.py @@ -0,0 +1,275 @@ +import numpy as np +import cmath + +from qutip import Qobj +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 + 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) + + +_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 + :math:`\textrm{R}_i` and :math:`\textrm{R}_j`. + + Here, :math:`i \neq j` and :math:`i, j \in {x, y, z}`. + + Based on Lemma 4.1 of https://arxiv.org/abs/quant-ph/9503016v1 + + .. math:: + + U = \begin{bmatrix} + a & b \\ + -b^* & a^* \\ + \end{bmatrix} = \textrm{R}_i(\alpha) \textrm{R}_j(\theta) \textrm{R}_i(\beta) + + Parameters + ---------- + input_gate : :class:`.Qobj` + The matrix that's supposed to be decomposed should be a Qobj. + + num_qubits : int + Number of qubits being acted upon by input gate + + target : int + If the circuit contains more than 1 qubits then provide target for + single qubit gate. + + 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)` + + Returns + ------- + tuple + The gates in the decomposition are returned as a tuple of :class:`Gate` + objects. This 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. + """ + 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.") + + + key = _rotation_matrices_dictionary.keys() + if str(method) in key: + method = _rotation_matrices_dictionary[str(method)] + 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. + """ + 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) + + +_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 + :math:`\textrm{R}_i` and :math:`\textrm{R}_j` and Pauli :math:`\sigma_k`. + + Here, :math:`i \neq j` and :math:`i, j, k \in {x, y, z}`. + + Based on Lemma 4.3 of https://arxiv.org/abs/quant-ph/9503016v1 + + .. math:: + + U = \begin{bmatrix} + a & b \\ + -b^* & a^* \\ + \end{bmatrix} = \textrm{A} \sigma_k \textrm{B} \sigma_k \textrm{C} + + Here, + + .. list-table:: + :widths: auto + :header-rows: 1 + + * - Gate Label + - Gate Composition + * - :math:`\textrm{A}` + - :math:`\textrm{R}_i(\alpha) \textrm{R}_j \left(\frac{\theta}{2} \right)` + * - :math:`\textrm{B}` + - :math:`\textrm{R}_j \left(\frac{-\theta}{2} \right) \textrm{R}_i \left(\frac{- \left(\alpha + \beta \right)}{2} \right)` + * - :math:`\textrm{C}` + - :math:`\textrm{R}_i \left(\frac{\left(-\alpha + \beta \right)}{2} \right)` + + Parameters + ---------- + input_gate : :class:`.Qobj` + The matrix that's supposed to be decomposed should be a Qobj. + + num_qubits : int + Number of qubits being acted upon by input gate + + target : int + If the circuit contains more than 1 qubits then provide target for + single qubit gate. + + method : string + Name of the preferred decomposition method + + .. list-table:: + :widths: auto + :header-rows: 1 + + * - Method Key + - Method + - :math:`(i,j,k)` + * - ZYZ_PauliX + - :math:`\textrm{A} \textrm{X} \textrm{B} \textrm{X} \textrm{C}` + - :math:`(z,y,x)` + + Returns + ------- + tuple + The gates in the decomposition are returned as a tuple of :class:`Gate` + objects. This 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. + """ + 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.") + + + key = _rotation_pauli_matrices_dictionary.keys() + if str(method) in key: + method = _rotation_pauli_matrices_dictionary[str(method)] + return(method(input_gate, target, 1)) + + else: + raise MethodError("Invalid method chosen.") diff --git a/tests/decompose/test_single_decompositions.py b/tests/decompose/test_single_decompositions.py new file mode 100644 index 000000000..1a430c31d --- /dev/null +++ b/tests/decompose/test_single_decompositions.py @@ -0,0 +1,65 @@ +import numpy as np +import cmath +import pytest + +from qutip import Qobj, average_gate_fidelity, rand_unitary, sigmax, sigmay, sigmaz + +from qutip_qip.decompose.single_qubit_gate import (_ZYZ_rotation, _ZXZ_rotation, + ABC_decomposition, _ZYZ_pauli_X, decompose_to_rotation_matrices) + +from qutip_qip.circuit import (decomposed_gates_to_circuit, compute_unitary) +from qutip_qip.operations.gates import snot, sqrtnot + +# Fidelity closer to 1 means the two states are similar to each other +H = snot(1,0) +sigmax = sigmax() +sigmay = sigmay() +sigmaz = sigmaz() +SQRTNOT = sqrtnot(N=1, target=0) +T = Qobj([[1,0],[0,cmath.rect(1, np.pi/4)]]) +S = Qobj([[1,0],[0,1j]]) +target = 0 +num_qubits = 1 + +# Tests for private functions +@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T, rand_unitary(2)]) +@pytest.mark.parametrize("method",[_ZYZ_rotation, _ZXZ_rotation, _ZYZ_pauli_X]) +def test_single_qubit_to_rotations(gate, method): + """Initial matrix and product of final decompositions are same within some phase.""" + gate_list = method(gate,target,num_qubits) + decomposed_gates_circuit = decomposed_gates_to_circuit(gate_list,num_qubits) + decomposed_gates_final_matrix = compute_unitary(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,rand_unitary(2)]) +@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.""" + gate_list = decompose_to_rotation_matrices(gate,method,target,num_qubits) + decomposed_gates_circuit = decomposed_gates_to_circuit(gate_list,num_qubits) + decomposed_gates_final_matrix = compute_unitary(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, rand_unitary(2)]) +@pytest.mark.parametrize("method",[_ZYZ_rotation, _ZXZ_rotation, _ZYZ_pauli_X]) +def test_output_is_tuple(gate, method): + """Initial matrix and product of final decompositions are same within some phase.""" + gate_list = method(gate,target,num_qubits) + assert(isinstance(gate_list, tuple)) + +# Tests for public functions +@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T, rand_unitary(2)]) +@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.""" + gate_list = decompose_to_rotation_matrices(gate,method,num_qubits,target) + assert(isinstance(gate_list, tuple)) + +@pytest.mark.parametrize("gate",[H, sigmax, sigmay, sigmaz, SQRTNOT, S, T, rand_unitary(2)]) +@pytest.mark.parametrize("method",['ZYZ_PauliX']) +def test_check_single_qubit_to_decompose_to_rotations(gate, method): + """Initial matrix and product of final decompositions are same within some phase.""" + gate_list = ABC_decomposition(gate,method, num_qubits,target) + assert(isinstance(gate_list, tuple)) diff --git a/tests/decompose/test_utility.py b/tests/decompose/test_utility.py new file mode 100644 index 000000000..cd7c29db9 --- /dev/null +++ b/tests/decompose/test_utility.py @@ -0,0 +1,37 @@ +import numpy as np +import cmath +import pytest + +from qutip import Qobj, qeye +from qutip_qip.decompose._utility import (check_gate, MethodError, GateError) +from qutip_qip.operations import rx, z_gate, t_gate +from qutip_qip.circuit import QubitCircuit, Gate + +# Tests for check_gate +@pytest.mark.parametrize("invalid_input",[np.array([[1,1],[1,1]]),([[1,1],[1,1]]),1.5,3,(1,2,3,4),np.array([[],[]]),([[],[]]),()]) +def test_check_gate_non_qobj(invalid_input): + """Checks if correct value is returned or not when the input is not a Qobj. + """ + with pytest.raises(TypeError,match="The input matrix is not a Qobj."): + check_gate(invalid_input, num_qubits=1) + +@pytest.mark.parametrize("non_unitary",[Qobj([[1,1],[0,1]])]) +def test_check_gate_non_unitary(non_unitary): + """Checks if non-unitary input is correctly identified. + """ + with pytest.raises(ValueError, match="Input is not unitary."): + check_gate(non_unitary, num_qubits=1) + +@pytest.mark.parametrize("non_qubit_unitary",[qeye(4)]) +def test_check_gate_non_unitary(non_qubit_unitary): + """Checks if non-unitary input is correctly identified. + """ + with pytest.raises(ValueError, match="Input is not a unitary on 2 qubits."): + check_gate(non_qubit_unitary, num_qubits=2) + +@pytest.mark.parametrize("unitary",[Qobj([[1,0],[0,-1]])]) +def test_check_gate_unitary_input(unitary): + """Checks if shape of input is correctly identified. + """ + # No error raised if it passes. + check_gate(unitary, num_qubits=1) diff --git a/tests/test_circuit.py b/tests/test_circuit.py index 1ede8f2d5..6aea0d79a 100644 --- a/tests/test_circuit.py +++ b/tests/test_circuit.py @@ -38,7 +38,7 @@ from qutip_qip.circuit import ( - QubitCircuit, CircuitSimulator, Measurement) + QubitCircuit, CircuitSimulator, Measurement, compute_unitary, decomposed_gates_to_circuit) from qutip import (tensor, Qobj, ptrace, rand_ket, fock_dm, basis, rand_dm, bell_state, ket2dm, identity) from qutip_qip.qasm import read_qasm @@ -48,6 +48,8 @@ _para_gates ) +from qutip_qip.decompose.single_qubit_gate import _ZYZ_rotation + import qutip as qp @@ -674,3 +676,42 @@ def test_latex_code_teleportation_circuit(self): r" \gate{{\rm H}} & \meter & \qw & \qw & \qw & \qw \\ ", "", ]) + +# 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, 0, 1) +sigmax_zyz_gates = _ZYZ_rotation(sigmax, 0, 1) + +@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."): + compute_unitary(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=compute_unitary(valid_input) + assert(isinstance(final_output, Qobj))