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..8053f689f --- /dev/null +++ b/src/qutip_qip/decompose/_utility.py @@ -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.") 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..c0b983810 --- /dev/null +++ b/src/qutip_qip/decompose/single_qubit_gate.py @@ -0,0 +1,352 @@ +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_qubit_gate_decompositions.py b/tests/decompose/test_single_qubit_gate_decompositions.py new file mode 100644 index 000000000..0d21c5c31 --- /dev/null +++ b/tests/decompose/test_single_qubit_gate_decompositions.py @@ -0,0 +1,88 @@ +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..0ecc9c7f2 --- /dev/null +++ b/tests/decompose/test_utility.py @@ -0,0 +1,48 @@ +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))