Source code for qepsilon.operator_basis.tls

"""
This module deals with Pauli operators and related operations.
"""

import torch as th
from qepsilon.utilities import compose

[docs] class Pauli(th.nn.Module): """ This class deals with Pauli operators. """ def _X(self): """ This function returns the Pauli X operator as a 2x2 complex tensor. """ return th.tensor([[0, 1], [1, 0]], dtype=th.cfloat) def _Y(self): """ This function returns the Pauli Y operator as a 2x2 complex tensor. """ return th.tensor([[0, -1j], [1j, 0]], dtype=th.cfloat) def _Z(self): """ This function returns the Pauli Z operator as a 2x2 complex tensor. """ return th.tensor([[1, 0], [0, -1]], dtype=th.cfloat) def _I(self): """ This function returns the identity operator as a 2x2 complex tensor. """ return th.eye(2, dtype=th.cfloat) def _U(self): """ This function returns the raising operator as a 2x2 complex tensor. """ return th.tensor([[0, 1], [0, 0]], dtype=th.cfloat) def _D(self): """ This function returns the lowering operator as a 2x2 complex tensor. """ return th.tensor([[0, 0], [1, 0]], dtype=th.cfloat) def _N(self): """ This function returns the number operator as a 2x2 complex tensor. """ return th.tensor([[1, 0], [0, 0]], dtype=th.cfloat)
[docs] def __init__(self, n_qubits: int): super().__init__() """ Initialize the Pauli operator class. One-body Pauli operators are registered as buffers. Args: n_qubits: the number of qubits. """ self.nq = n_qubits self.register_buffer("X", self._X()) self.register_buffer("Y", self._Y()) self.register_buffer("Z", self._Z()) self.register_buffer("I", self._I()) self.register_buffer("U", self._U()) self.register_buffer("D", self._D()) self.register_buffer("N", self._N())
[docs] def along_u(self, u: th.FloatTensor): """ This function returns (Pauli vector dot u). u is a direction vector. Args: u: the direction vector. Should be a real-valued tensor. Shape: (3) or (nbatch, 3) Returns: M: the (batched) Pauli operator. Shape: (2,2) or (nbatch, 2, 2) """ if u.dim() == 1: if u.shape != (3,): raise ValueError("u must be a 3-dimensional vector.") u = u / th.norm(u) u = u.to(self.X.device) return self.X * u[0] + self.Y * u[1] + self.Z * u[2] elif u.dim() == 2: if u.shape[1] != 3: raise ValueError("u must be a 2D tensor with the last dimension being 3. ") u = u / th.norm(u, dim=1, keepdim=True) u = u.to(self.X.device) M = self.X[None, :, :] * u[:, 0, None, None] + self.Y[None, :, :] * u[:, 1, None, None] + self.Z[None, :, :] * u[:, 2, None, None] return M else: raise ValueError("u must be a 1D or 2D tensor.")
[docs] def SU2_rotation(self, u: th.Tensor, theta: th.Tensor): """ This function returns the SU(2) rotation operator about the direction u by angle theta. Args: u: the (batched) direction of the rotation. Shape: (3) or (nbatch, 3) theta: the (batched) angle of the rotation. Shape: (1) or (nbatch) Returns: M: the (batched) rotation operator. Shape: (2,2) or (nbatch, 2, 2) """ if u.dim() == 1: return th.cos(theta / 2) * self.I - 1j * th.sin(theta / 2) * self.along_u(u) elif u.dim() == 2: if theta.shape != u[:,0].shape: raise ValueError("The first dimension of u and theta must be the same.") theta = theta.reshape(-1, 1, 1) return th.cos(theta / 2) * self.I.unsqueeze(0) - 1j * th.sin(theta / 2) * self.along_u(u) else: raise ValueError("u must be a 1D or 2D tensor.")
[docs] def get_sequence_ops(self, name_sequence: str): """ This function returns a sequence of Pauli operators. Args: name_sequence: a string of Pauli operator names. Example: "XYZI" """ op = [] for s in name_sequence: op.append(getattr(self, s)) return op
[docs] def get_composite_ops(self, name_sequence: str): """ This function returns a composite Pauli operator. """ return compose(self.get_sequence_ops(name_sequence))
[docs] class Kraus(Pauli): """ This class deals with Kraus operators. """
[docs] def __init__(self): super().__init__()
[docs] def depolarizing_operators(self, p: float): """ This function returns the Kraus operators of the depolarizing channel. This channel represents a situation where the system is subject to a random unitary error with equal probability between X, Y, and Z. """ ops = [] ops.append(th.sqrt(1 - p) * self.I) ops.append(th.sqrt(p / 3) * self.X) ops.append(th.sqrt(p / 3) * self.Y) ops.append(th.sqrt(p / 3) * self.Z) return ops
[docs] def dephasing_operators(self, gamma: float): """ This function returns the Kraus operators of the dephasing channel. This channel represents a situation where the system is subject to a random phase error. """ ops = [] ops.append(th.sqrt(1 - gamma) * self.I) ops.append(th.sqrt(gamma) * self.Z) return ops
[docs] def amplitude_damping_operators(self, gamma: float): """ This function returns the Kraus operators of the amplitude damping channel. This channel represents a situation where the system experience energy dissipation, such as spontaneous emission. """ raise NotImplementedError("Amplitude damping channel is not implemented yet.")