def test_cache_usage(self, depth): """Test that the right number of cachings have been executed after clearing the cache""" pu.pauli_eigs.cache_clear() pu.pauli_eigs(depth) total_runs = sum([2**x for x in range(depth)]) assert functools._CacheInfo(depth - 1, depth, 128, depth) == pu.pauli_eigs.cache_info()
class PauliZ(Observable, Operation): r"""PauliZ(wires) The Pauli Z operator .. math:: \sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1) @staticmethod def _matrix(*params): return np.array([[1, 0], [0, -1]]) def diagonalizing_gates(self): return []
def test_correct_eigenvalues_pauli_kronecker_products_three_qubits( self, pauli_product): """Test the paulieigs function for three qubits""" assert np.array_equal( pu.pauli_eigs(3), np.diag(np.kron(self.pauliz, np.kron(self.pauliz, self.pauliz))), )
def generator(self): if self._generator is None: pauli_word = self.parameters[1] # Simplest case is if the Pauli is the identity matrix if pauli_word == "I" * len(pauli_word): self._generator = [np.eye(2 ** len(pauli_word)), -1 / 2] return self._generator # We first generate the matrix excluding the identity parts and expand it afterwards. # To this end, we have to store on which wires the non-identity parts act non_identity_wires, non_identity_gates = zip( *[(wire, gate) for wire, gate in enumerate(pauli_word) if gate != "I"] ) # get MultiRZ's generator multi_Z_rot_generator = np.diag(pauli_eigs(len(non_identity_gates))) # now we conjugate with Hadamard and RX to create the Pauli string conjugation_matrix = functools.reduce( np.kron, [PauliRot._PAULI_CONJUGATION_MATRICES[gate] for gate in non_identity_gates], ) self._generator = [ expand( conjugation_matrix.T.conj() @ multi_Z_rot_generator @ conjugation_matrix, non_identity_wires, list(range(len(pauli_word))), ), -1 / 2, ] return self._generator
class PauliZ(Observable, DiagonalOperation): r"""PauliZ(wires) The Pauli Z operator .. math:: \sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1) matrix = np.array([[1, 0], [0, -1]]) @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): return [] @staticmethod def decomposition(wires): decomp_ops = [PhaseShift(np.pi, wires=wires)] return decomp_ops
class Hadamard(Observable, Operation): r"""Hadamard(wires) The Hadamard operator .. math:: H = \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1\\ 1 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None is_self_inverse = True eigvals = pauli_eigs(1) matrix = np.array([[INV_SQRT2, INV_SQRT2], [INV_SQRT2, -INV_SQRT2]]) @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): r"""Rotates the specified wires such that they are in the eigenbasis of the Hadamard operator. For the Hadamard operator, .. math:: H = U^\dagger Z U where :math:`U = R_y(-\pi/4)`. Returns: list(~.Operation): A list of gates that diagonalize Hadamard in the computational basis. """ return [qml.RY(-np.pi / 4, wires=self.wires)] @staticmethod def decomposition(wires): decomp_ops = [ qml.PhaseShift(np.pi / 2, wires=wires), qml.RX(np.pi / 2, wires=wires), qml.PhaseShift(np.pi / 2, wires=wires), ] return decomp_ops def adjoint(self): return Hadamard(wires=self.wires) def single_qubit_rot_angles(self): # H = RZ(\pi) RY(\pi/2) RZ(0) return [np.pi, np.pi / 2, 0.0]
def _eigvals(cls, theta, n): eigs = qml.math.convert_like(pauli_eigs(n), theta) if qml.math.get_interface(theta) == "tensorflow": theta = qml.math.cast_like(theta, 1j) eigs = qml.math.cast_like(eigs, 1j) return qml.math.exp(-1j * theta / 2 * eigs)
class PauliY(Observable, Operation): r"""PauliY(wires) The Pauli Y operator .. math:: \sigma_y = \begin{bmatrix} 0 & -i \\ i & 0\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1) matrix = np.array([[0, -1j], [1j, 0]]) @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): r"""Rotates the specified wires such that they are in the eigenbasis of PauliY. For the Pauli-Y observable, .. math:: Y = U^\dagger Z U where :math:`U=HSZ`. Returns: list(~.Operation): A list of gates that diagonalize PauliY in the computational basis. """ return [ PauliZ(wires=self.wires), S(wires=self.wires), Hadamard(wires=self.wires) ] @staticmethod def decomposition(wires): decomp_ops = [ PhaseShift(np.pi / 2, wires=wires), RY(np.pi, wires=wires), PhaseShift(np.pi / 2, wires=wires), ] return decomp_ops
def MultiRZ(theta, n): r"""Arbitrary multi Z rotation. Args: theta (float): rotation angle :math:`\theta` wires (Sequence[int] or int): the wires the operation acts on Returns: array[complex]: diagonal part of the multi-qubit rotation matrix """ return jnp.exp(-1j * theta / 2 * pauli_eigs(n))
def _eigvals(theta, n): """Return the eigenvalues corresponding to a specific rotation angle. Args: theta (float): Rotation angle Returns: (array[float]): Eigenvalues of the transformation """ return np.exp(-1j * theta / 2 * pauli_eigs(n))
def MultiRZ(theta, n): r"""Arbitrary multi Z rotation. Args: theta (float): rotation angle n (int): number of wires the rotation acts on Returns: tf.Tensor[complex]: diagonal part of the MultiRZ matrix """ theta = tf.cast(theta, dtype=C_DTYPE) multi_Z_rot_eigs = tf.exp(-1j * theta / 2 * pauli_eigs(n)) return tf.convert_to_tensor(multi_Z_rot_eigs)
class PauliZ(Observable, Operation): r"""PauliZ(wires) The Pauli Z operator .. math:: \sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_wires = 1 basis = "Z" eigvals = pauli_eigs(1) matrix = np.array([[1, 0], [0, -1]]) @property def num_params(self): return 0 def label(self, decimals=None, base_label=None): return base_label or "Z" @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): return [] @staticmethod def decomposition(wires): decomp_ops = [qml.PhaseShift(np.pi, wires=wires)] return decomp_ops def adjoint(self): return PauliZ(wires=self.wires) def _controlled(self, wire): CZ(wires=Wires(wire) + self.wires) def single_qubit_rot_angles(self): # Z = RZ(\pi) RY(0) RZ(0) return [np.pi, 0.0, 0.0]
class PauliZ(Observable, Operation): r"""PauliZ(wires) The Pauli Z operator .. math:: \sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1)
class Hadamard(Observable, Operation): r"""Hadamard(wires) The Hadamard operator .. math:: H = \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1\\ 1 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1)
class Hadamard(Observable, Operation): r"""Hadamard(wires) The Hadamard operator .. math:: H = \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1\\ 1 & -1\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1) matrix = np.array([[INV_SQRT2, INV_SQRT2], [INV_SQRT2, -INV_SQRT2]]) @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): r"""Rotates the specified wires such that they are in the eigenbasis of the Hadamard operator. For the Hadamard operator, .. math:: H = U^\dagger Z U where :math:`U = R_y(-\pi/4)`. Returns: list(~.Operation): A list of gates that diagonalize Hadamard in the computational basis. """ return [RY(-np.pi / 4, wires=self.wires)]
class PauliX(Observable, Operation): r"""PauliX(wires) The Pauli X operator .. math:: \sigma_x = \begin{bmatrix} 0 & 1 \\ 1 & 0\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_params = 0 num_wires = 1 par_domain = None eigvals = pauli_eigs(1) matrix = np.array([[0, 1], [1, 0]]) @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): r"""Rotates the specified wires such that they are in the eigenbasis of the Pauli-X operator. For the Pauli-X operator, .. math:: X = H^\dagger Z H. Returns: list(qml.Operation): A list of gates that diagonalize PauliY in the computational basis. """ return [Hadamard(wires=self.wires)]
def generator(self): if self._generator is None: self._generator = [np.diag(pauli_eigs(len(self.wires))), -1 / 2] return self._generator
class PauliY(Observable, Operation): r"""PauliY(wires) The Pauli Y operator .. math:: \sigma_y = \begin{bmatrix} 0 & -i \\ i & 0\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_wires = 1 basis = "Y" eigvals = pauli_eigs(1) matrix = np.array([[0, -1j], [1j, 0]]) @property def num_params(self): return 0 def label(self, decimals=None, base_label=None): return base_label or "Y" @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): r"""Rotates the specified wires such that they are in the eigenbasis of PauliY. For the Pauli-Y observable, .. math:: Y = U^\dagger Z U where :math:`U=HSZ`. Returns: list(~.Operation): A list of gates that diagonalize PauliY in the computational basis. """ return [ PauliZ(wires=self.wires), S(wires=self.wires), Hadamard(wires=self.wires), ] @staticmethod def decomposition(wires): decomp_ops = [ qml.PhaseShift(np.pi / 2, wires=wires), qml.RY(np.pi, wires=wires), qml.PhaseShift(np.pi / 2, wires=wires), ] return decomp_ops def adjoint(self): return PauliY(wires=self.wires) def _controlled(self, wire): CY(wires=Wires(wire) + self.wires) def single_qubit_rot_angles(self): # Y = RZ(0) RY(\pi) RZ(0) return [0.0, np.pi, 0.0]
def test_correct_eigenvalues_paulis(self, pauli): """Test the paulieigs function for one qubit""" assert np.array_equal(pu.pauli_eigs(1), np.diag(self.pauliz))
class PauliX(Observable, Operation): r"""PauliX(wires) The Pauli X operator .. math:: \sigma_x = \begin{bmatrix} 0 & 1 \\ 1 & 0\end{bmatrix}. **Details:** * Number of wires: 1 * Number of parameters: 0 Args: wires (Sequence[int] or int): the wire the operation acts on """ num_wires = 1 basis = "X" eigvals = pauli_eigs(1) matrix = np.array([[0, 1], [1, 0]]) @property def num_params(self): return 0 def label(self, decimals=None, base_label=None): return base_label or "X" @classmethod def _matrix(cls, *params): return cls.matrix @classmethod def _eigvals(cls, *params): return cls.eigvals def diagonalizing_gates(self): r"""Rotates the specified wires such that they are in the eigenbasis of the Pauli-X operator. For the Pauli-X operator, .. math:: X = H^\dagger Z H. Returns: list(qml.Operation): A list of gates that diagonalize PauliY in the computational basis. """ return [Hadamard(wires=self.wires)] @staticmethod def decomposition(wires): decomp_ops = [ qml.PhaseShift(np.pi / 2, wires=wires), qml.RX(np.pi, wires=wires), qml.PhaseShift(np.pi / 2, wires=wires), ] return decomp_ops def adjoint(self): return PauliX(wires=self.wires) def _controlled(self, wire): CNOT(wires=Wires(wire) + self.wires) def single_qubit_rot_angles(self): # X = RZ(-\pi/2) RY(\pi) RZ(\pi/2) return [np.pi / 2, np.pi, -np.pi / 2]
def _eigvals(cls, theta, n): return np.exp(-1j * theta / 2 * pauli_eigs(n))