def test_check_gate_non_1qubit(non_1qubit_unitary): """Checks if non-unitary input is correctly identified.""" num_qubits = 1 with pytest.raises( ValueError, match=f"Input is not a unitary on {num_qubits} qubits."): check_gate(non_1qubit_unitary, num_qubits)
def _ZYZ_pauli_X(input_gate): """Returns a 1 qubit unitary as a product of ZYZ rotation matrices and Pauli X.""" check_gate(input_gate, num_qubits=1) alpha, theta, beta, global_phase_angle = _angles_for_ZYZ(input_gate) Phase_gate = Gate( "GLOBALPHASE", targets=[0], arg_value=global_phase_angle, arg_label=r"{:0.2f} \times \pi".format(global_phase_angle / np.pi), ) Rz_A = Gate( "RZ", targets=[0], arg_value=alpha, arg_label=r"{:0.2f} \times \pi".format(alpha / np.pi), ) Ry_A = Gate( "RY", targets=[0], arg_value=theta / 2, arg_label=r"{:0.2f} \times \pi".format(theta / np.pi), ) Pauli_X = Gate("X", targets=[0]) Ry_B = Gate( "RY", targets=[0], arg_value=-theta / 2, arg_label=r"{:0.2f} \times \pi".format(-theta / np.pi), ) Rz_B = Gate( "RZ", targets=[0], arg_value=-(alpha + beta) / 2, arg_label=r"{:0.2f} \times \pi".format(-(alpha + beta) / (2 * np.pi)), ) Rz_C = Gate( "RZ", targets=[0], arg_value=(-alpha + beta) / 2, arg_label=r"{:0.2f} \times \pi".format((-alpha + beta) / (2 * np.pi)), ) return (Rz_A, Ry_A, Pauli_X, Ry_B, Rz_B, Pauli_X, Rz_C, Phase_gate)
def _ZXZ_rotation(input_gate): 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. """ check_gate(input_gate, num_qubits=1) alpha, theta, beta, global_phase_angle = _angles_for_ZYZ(input_gate) alpha = alpha - np.pi / 2 beta = beta + np.pi / 2 # theta and global phase are same as ZYZ values Phase_gate = Gate( "GLOBALPHASE", targets=[0], arg_value=global_phase_angle, arg_label=r"{:0.2f} \times \pi".format(global_phase_angle / np.pi), ) Rz_alpha = Gate( "RZ", targets=[0], arg_value=alpha, arg_label=r"{:0.2f} \times \pi".format(alpha / np.pi), ) Rx_theta = Gate( "RX", targets=[0], arg_value=theta, arg_label=r"{:0.2f} \times \pi".format(theta / np.pi), ) Rz_beta = Gate( "RZ", targets=[0], arg_value=beta, arg_label=r"{:0.2f} \times \pi".format(beta / np.pi), ) return (Rz_alpha, Rx_theta, Rz_beta, Phase_gate)
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)
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)
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)
def decompose_one_qubit_gate(input_gate, method): r""" An input 1-qubit gate is expressed as a product of rotation matrices :math:`\textrm{R}_i` and :math:`\textrm{R}_j` or as a product of rotation matrices :math:`\textrm{R}_i` and :math:`\textrm{R}_j` and a Pauli :math:`\sigma_k`. Here, :math:`i \neq j` and :math:`i, j, k \in {x, y, z}`. Based on Lemma 4.1 and Lemma 4.3 of https://arxiv.org/abs/quant-ph/9503016v1 respectively. .. math:: U = \begin{bmatrix} a & b \\ -b^* & a^* \\ \end{bmatrix} = \textrm{R}_i(\alpha) \textrm{R}_j(\theta) \textrm{R}_i(\beta) = \textrm{A} \sigma_k \textrm{B} \sigma_k \textrm{C} Here, * :math:`\textrm{A} = \textrm{R}_i(\alpha) \textrm{R}_j\left(\frac{\theta}{2} \right)` * :math:`\textrm{B} = \textrm{R}_j \left(\frac{-\theta}{2} \right)\textrm{R}_i \left(\frac{- \left(\alpha + \beta \right)}{2} \right)` * :math:`\textrm{C} = \textrm{R}_i \left(\frac{\left(-\alpha + \beta\right)}{2} \right)` Parameters ---------- input_gate : :class:`qutip.Qobj` The matrix to be decomposed. 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)` * - ZYZ_PauliX - :math:`\textrm{A} \sigma_k \textrm{B} \sigma_k \textrm{C}` :math:`\forall k =x, i =z, j=y` .. note:: This function is under construction. As more combinations are added, above table will be updated with their respective keys. Returns ------- tuple The gates in the decomposition are returned as a tuple of :class:`Gate` objects. When the input gate is decomposed to product of rotation matrices - 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. When the input gate is decomposed to product of rotation matrices and Pauli - 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. """ check_gate(input_gate, num_qubits=1) f = _single_decompositions_dictionary.get(method, None) if f is None: raise MethodError(f"Invalid decomposition method: {method!r}") return f(input_gate)