pyqch package

pyqch.base_change_unitaries module

base_change_unitaries

This module implements functions that return unitaries for transforming density matrices between different bases. It currently includes transformations related to the generalized Gell-Mann basis and the element-wise (computational or canonical) basis, with potential for future extensions to other transformations.

pyqch.base_change_unitaries.el_gm(dim: int) ndarray

Returns the unitary matrix that transforms from the element-wise (computational) basis to the coefficients in the generalized Gell-Mann normalized basis.

Parameters:

dim (int) – The dimension of the Hilbert space.

Returns:

The unitary matrix for the basis transformation.

Return type:

np.array

See also

gm_el

Returns the inverse transformation.

Examples

>>> from base_change_unitaries import el_gm
>>> dim = 3
>>> U_el_gm = gm_el(dim)
>>> # Define a density matrix in the computational basis
>>> rho = np.diag([0.6, 0.3, 0.1])
>>> # Transform the density matrix to the Gell-Mann basis
>>> rho_gm = U_el_gm @ rho.reshape(dim**2)
>>> print("rho in Gell-Mann basis:")
>>> print(rho_gm)
[0.38723499+0.j 0.        +0.j 0.21213203+0.j 0.        +0.j
 0.38723499+0.j 0.        +0.j 0.21213203+0.j 0.        +0.j
 0.2647605 +0.j]
pyqch.base_change_unitaries.gm_el(dim: int) ndarray

Returns the unitary matrix that transforms from the coefficients in the generalized Gell-Mann normalized basis to the element-wise (computational) basis.

Parameters:

dim (int) – The dimension of the Hilbert space.

Returns:

The unitary matrix for the basis transformation.

Return type:

np.array

See also

el_gm

Returns the inverse transformation.

Notes

Gell-Mann matrices are a generalization of the Pauli matrices used to describe higher-dimensional systems in quantum mechanics [1]. They form a complete, orthogonal basis for the space of Hermitian matrices.

The d>3 generalization is taken as in [2] (Eq. 8).

References

Examples

>>> from base_change_unitaries import gm_el
>>> U_el_gm = el_gm(2)  # Transforms from the normalized Pauli basis
>>> # Define a density matrix in the Pauli basis
>>> rho_pauli = np.array([1/2, 1/2, 0, 0])
>>> # Transform the density matrix to the element-wise basis
>>> rho = (U_el_gm @ rho_pauli).reshape((2,2))
>>> print("rho in element-wise basis:")
>>> print(rho)
[[ 0.5  0.5 ]
 [ 0.5  0.5 ]]

pyqch.channel_families module

channel_families

This module implements functions that return the transition matrices or Kraus representation describing different families of quantum channels.

These functions cover a variety of channels including classical permutations, dephasing, depolarizing, embedding classical channels, initializers, POVMs, probabilistic damping, and probabilistic unitaries.

pyqch.channel_families.amplitude_damping(dim: int, lamb: float, x: ndarray | int = 1, y: ndarray | int = 0, kraus_representation: bool = False) ndarray

Amplitude damping in arbitrary dimension.

It describes the coherent damping from state |x> to |y>.

Reduces to the usual qubit amplitude damping for dim=0, x=1, y=0.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • lamb (float) – The damping parameter ranging from 0 to 1.

  • x (np.ndarray | int, optional) – The origin state for the damping, by default 1. If int, it is interpreted as a basis state.

  • y (np.ndarray | int, optional) – The target state for the damping, by default 0. If int, it is interpreted as a basis state.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False.

Returns:

The transition matrix or Kraus operators representing the amplitude damping channel.

Return type:

np.ndarray

Notes

For the general d-dimensional formulation we considered Definition 1 in [3].

References

Raises:

ValueError – If lambda not in [0, 1]; x or y are int and are not in [0, dim), or are np.ndarray and shape not compatible with (dim,1).

pyqch.channel_families.classical_permutation(dim: int, perm: list, kraus_representation: bool = False) ndarray

Returns the transition matrix representing a classical permutation channel.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • perm (list) – The array defining the permutation that maps i to perm[i].

  • kraus_representation (bool, optional) – Defaults to False. WARNING: This map does not admit a Kraus representation, if it is set to True it will rise a ValueError.

Returns:

The transition matrix representing the classical permutation channel.

Return type:

np.ndarray

Examples

>>> from channel_families import classical_permutation
>>> perm = [2, 0, 1]
>>> T = classical_permutation(3, perm)
>>> print(T)
[[0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]]
pyqch.channel_families.dephasing(dim: int, g: float | ndarray, u: ndarray = None, kraus_representation: bool = False) ndarray

Returns the transition matrix for a dephasing channel.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • g (float | np.ndarray) – Dephasing strength. If float, it is used as an uniform damping for all off-diagonal terms; if np.ndarray, g[i,j] describes the damping of the element (i,j). The validity of g to define an appropriate dephasing channel is not verified.

  • u (np.ndarray, optional) – The unitary matrix defining the basis in which dephasing occurs. Defaults to the identity matrix.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False.

Returns:

The transition matrix or Kraus operators for the dephasing channel.

Return type:

np.ndarray

Notes

This function implements dephasing by dampening the off-diagonal terms in a given basis. If g is a matrix, it should be real, symmetric, positive semi-definite and all diagonal terms equal to one.

For other generalized dephasing, one could define the corresponding PVMs and include an identity with some dampening, then pass them to the povm function.

Examples

>>> from channel_families import dephasing
>>> dim = 3
>>> g = 0.4
>>> T = dephasing(dim, g)
>>> print(T)
[[1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ],
   [0. , 0.6, 0. , 0. , 0. , 0. , 0. , 0. , 0. ],
   [0. , 0. , 0.6, 0. , 0. , 0. , 0. , 0. , 0. ],
   [0. , 0. , 0. , 0.6, 0. , 0. , 0. , 0. , 0. ],
   [0. , 0. , 0. , 0. , 1. , 0. , 0. , 0. , 0. ],
   [0. , 0. , 0. , 0. , 0. , 0.6, 0. , 0. , 0. ],
   [0. , 0. , 0. , 0. , 0. , 0. , 0.6, 0. , 0. ],
   [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0.6, 0. ],
   [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 1. ]])
pyqch.channel_families.depolarizing(dim: int, p: float, r: ndarray = None, kraus_representation: bool = False, kraus_atol: float = 1e-07) ndarray

Returns the transition matrix for a depolarizing channel.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • p (float) – Depolarizing probability.

  • r (np.ndarray, optional) – The stationary state of the depolarizing channel. Defaults to the completely mixed state.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False.

  • kraus_atol (float, optional) – The tolerance to neglect a Kraus operator, defaults to 1e-7.

Returns:

The transition matrix or Kraus operators for the depolarizing channel.

Return type:

np.ndarray

Examples

>>> from channel_families import depolarizing
>>> dim = 3
>>> p = 0.2
>>> T = depolarizing(dim, p)
>>> rho_in = np.diag([1, 0, 0])
>>> rho_out = (T @ rho_in.reshape(dim**2)).reshape((dim, dim))
>>> print(rho_out)
[[0.86666667+0.j 0.        +0.j 0.        +0.j]
 [0.        +0.j 0.06666667+0.j 0.        +0.j]
 [0.        +0.j 0.        +0.j 0.06666667+0.j]]
pyqch.channel_families.embed_classical(dim: int, stoch_mat: ndarray, kraus_representation: bool = False, kraus_atol: float = 1e-07) ndarray

Embeds a classical stochastic matrix into a quantum transition matrix.

The resulting process sets off-diagonal terms to zero and acts on the diagonal terms following the classical stochastic process.

Parameters:
  • dim (int) – The dimension of the input Hilbert space.

  • stoch_mat (np.ndarray) – The classical column-stochastic matrix to be embedded.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False.

  • kraus_atol (float, optional) – The tolerance to neglect a Kraus operator, defaults to 1e-7.

Returns:

The transition matrix or Kraus operators representing the embedded classical channel.

Return type:

np.ndarray

Examples

>>> from channel_families import embed_classical
>>> stoch_mat = np.array([[0.8, 0.3], [0.2, 0.7]])
>>> T = embed_classical(2, stoch_mat)
>>> print(T)
[[0.8 0.  0.  0.3]
 [0.  0.  0.  0. ]
 [0.  0.  0.  0. ]
 [0.2 0.  0.  0.7]]
pyqch.channel_families.initializer(dim: int, states: ndarray, mode='c-q', kraus_representation: bool = False, kraus_atol: float = 1e-07) ndarray

Returns a transition matrix for initializing a quantum state.

The initializer channel takes a classical probability distribution as input and produces a density matrix.

The mode argument defines the format of the input and output states. The initial state can be a classical array (c) or a density matrix whose diagonal elements are used as the input probability distribution (q), discarding off-diagonal terms. The output state can store only the initialized states (q) or also the input probability distribution (qc)

Parameters:
  • dim (int) – The dimension of the output Hilbert space.

  • states (np.ndarray) – Array of states defining the choices for initialization. The expected shape is either (number of states, dim, dim) or (number of states, dim) , such that states[i] represents the state prepared when the classical variable takes the value i (described as a density matrix or as a vector).

  • mode (str, optional) – Mode of initialization (‘c-q’ for classical to quantum, ‘c-qc’ for classical to quantum and classical, ‘q-q’ for quantum to quantum, ‘q-qc’ for quantum to quantum and classical). Defaults to ‘c-q’.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False. Some modes do not admit a Kraus representation.

  • kraus_atol (float, optional) – The tolerance to neglect a Kraus operator, defaults to 1e-7.

Returns:

The transition matrix or Kraus operators for the initializer.

Return type:

np.ndarray

Examples

>>> from channel_families import initializer
>>> states = np.array([[[1, 0], [0, 0]],
                        [[.5, .5], [.5, .5]]])
>>> T = initializer(2, states)
>>> print(T)
[[1.  0.5]
 [0.  0.5]
 [0.  0.5]
 [0.  0.5]]
pyqch.channel_families.povm(dim: int, pos: ndarray, mode='q-q', kraus_representation: bool = False) ndarray

Returns a transition matrix for a quantum channel defined by a positive operator-valued measure (POVM).

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • pos (np.ndarray) – The POVM elements defining the channel. The expected shape is (number of positive operators, dim, dim), such that pos[i] is the ith positive operator.

  • mode (str, optional) – Mode of the channel (‘q-q’ for quantum to quantum, ‘q-c’ for quantum to classical, ‘q-qc’ for quantum to quantum and classical). Defaults to ‘q-q’.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False. Some modes do not admit a Kraus representation.

Returns:

The transition matrix or Kraus operators for the POVM-based quantum channel.

Return type:

np.ndarray

Examples

>>> from channel_families import povm
>>> pos = np.array([np.diag([1,0]), np.diag([0,1])])
>>> T = povm(2, pos)
>>> print(T)
[[1 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 1]]
pyqch.channel_families.probabilistic_damping(dim: int, p: float, kraus_representation: bool = False) ndarray

Returns a transition matrix for a probabilistic damping channel.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • p (float) – Damping probability.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False.

Returns:

The transition matrix or Kraus operators for the probabilistic damping channel.

Return type:

np.ndarray

Examples

>>> from channel_families import probabilistic_damping
>>> dim = 2
>>> p = 0.1
>>> T = probabilistic_damping(dim, p)
>>> print(T)
[[1.  0.  0.  0.1]
 [0.  0.9 0.  0. ]
 [0.  0.  0.9 0. ]
 [0.  0.  0.  0.9]]
pyqch.channel_families.probabilistic_unitaries(dim: int, p_arr: ndarray, u_arr: ndarray, kraus_representation: bool = False) ndarray

Returns a transition matrix for a channel that applies unitaries probabilistically.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • p_arr (np.ndarray) – Probabilities for each unitary operation.

  • u_arr (np.ndarray) – Array of unitary matrices. The expected shape is (number of unitaries, dim, dim), such that u_arr[i] is the ith possible unitary.

  • kraus_representation (bool, optional) – If true, the function returns the Kraus representation of the channel with shape (Kraus operator index, dim2, dim1), defaults to False.

Returns:

The transition matrix or Kraus operators for the probabilistic unitary channel.

Return type:

np.ndarray

Examples

This example demonstrates the implementation of a bit-flip channel.

>>> from channel_families import probabilistic_unitaries
>>> p_arr = np.array([0.5, 0.5])
>>> u_arr = np.array([np.eye(2), np.array([[0, 1], [1, 0]])])
>>> T = probabilistic_unitaries(2, p_arr, u_arr)
>>> print(T)
[[0.5 0.  0.  0.5]
 [0.  0.5 0.5 0. ]
 [0.  0.5 0.5 0. ]
 [0.5 0.  0.  0.5]]
pyqch.channel_families.transposition(dim: int, u: ndarray = None, kraus_representation: bool = False) ndarray

Returns a transition matrix for the transposition operator. This is positive but not completely positive, thus it is not a quantum channel.

Parameters:
  • dim (int) – The dimension of the Hilbert space.

  • u (np.ndarray or None) – Basis in which the transposition is performed, if None the canonical basis is used. Defaults to None.

  • kraus_representation (bool, optional) – Defaults to False. WARNING: This map does not admit a Kraus representation, if it is set to True it will rise a ValueError.

Returns:

The transition matrix for the transposition.

Return type:

np.ndarray

pyqch.channel_operations module

channel_operations

This module implements various operations on quantum channels. It includes functions for obtaining the Choi state of a channel, performing tensor products of channel representations, and finding fixed points of a channel.

pyqch.channel_operations.choi_state(t: ndarray) ndarray

Returns the Choi state representation of a given quantum channel.

Parameters:

t (np.ndarray) – The transition matrix representing the quantum channel.

Returns:

The Choi state corresponding to the quantum channel.

Return type:

np.ndarray

Examples

>>> from channel_operations import choi_state
>>> from channel_families import depolarizing
>>> dim = 2
>>> p = .5
>>> t = depolarizing(dim, p)
>>> choi = choi_state(t)
>>> print(choi)
[[0.375+0.j 0.   +0.j 0.   +0.j 0.25 +0.j]
 [0.   +0.j 0.125+0.j 0.   +0.j 0.   +0.j]
 [0.   +0.j 0.   +0.j 0.125+0.j 0.   +0.j]
 [0.25 +0.j 0.   +0.j 0.   +0.j 0.375+0.j]]
pyqch.channel_operations.doeblin_coefficient(channel: ndarray, transpose: bool = False, subspace_projection: ndarray = None)

Computes the Doeblin coefficient of a quantum channel.

The Doeblin coefficient is the maximum erasure probability for which an erasure channel can be degraded into the given channel. This implementation uses the SDP construction described in reference [1].

The function dependends on the optional package cvxpy. Please install it with pip install cvxpy before using the function.

Parameters:
  • channel (np.ndarray) – The transition matrix representing the quantum channel.

  • transpose (bool, optional) – Whether to check transpose-degradability instead of the usual degradability. Defaults to False.

  • subspace_projection (np.ndarray, optional) – An orthogonal projector into a subspace of dimension at least 2. If provided, it restricts the search for the coefficient to that subspace. Defaults to None.

Returns:

The Doeblin coefficient of the quantum channel. Returns None if the problem is infeasible or unbounded.

Return type:

float

Notes

This function implements the SDP (Semidefinite Programming) construction given in reference [1] to find the Doeblin coefficient.

The subspace restriciton is not detailed in [1].

See also

choi_state

Function to compute the Choi state of the channel.

channel_families.transposition

Function to obtain the transition matrix of matrix transposition.

References

[1] : C. Hirche (2024), “Quantum Doeblin coefficients: A simple upper bound on contraction coefficients” (arXiv: 2405.00105)

pyqch.channel_operations.fixed_points(t: ndarray, tol: float = 1e-06) ndarray

Returns the fixed points of a given quantum channel.

Parameters:
  • t (np.ndarray) – The transition matrix representing the quantum channel.

  • tol (float, optional) – Tolerance for determining fixed points. Defaults to 1e-6.

Returns:

The fixed points of the quantum channel.

Return type:

np.ndarray

Raises:

NotImplementedError – If more than one fixed point is detected.

Examples

>>> from channel_operations import fixed_points
>>> from channel_families import depolarizing
>>> dim = 3
>>> p = 0.5
>>> rho_ref = np.diag([.5, .3, .2])
>>> t = depolarizing(dim, p, rho_ref)
>>> fixed_pt = fixed_points(t)
>>> np.allclose(fixed_pt, rho_ref)
True
pyqch.channel_operations.kraus_operators(t: ndarray, atol: float = 1e-06) ndarray

Returns the Kraus operators of a given quantum channel.

Parameters:
  • t (np.ndarray) – The transition matrix representing the quantum channel, with shape (dim2**2, dim1**2)

  • atol (float, optional) – The absolute tolerance for the norm of a Kraus operator to be considered as a non-trivial Kraus operator, defaults to 1e-6.

Returns:

The Kraus operators corresponding to the quantum channel, with shape (kraus_rank, dim2, dim1).

Return type:

np.ndarray

Notes

Currently, the method uses the spectral decomposition of the Choi matrix and discards eigenvalues that are smaller than atol. This is equivalent to discarding Kraus operators with np.trace(K.T @ K) <= atol * dim1.

Using the matrix square root of the Choi matrix is a valid alternative if the canonical form is not needed. It seems more efficient in cases where the matrix is sparse.

pyqch.channel_operations.tensor(t_arr: ndarray | list[ndarray], n: int = 1) ndarray

Returns the tensor product of quantum channels represented by their transition matrices.

This function can be used in two ways:

  1. Provide a list of transition matrices to compute their tensor product.

  2. Provide a single transition matrix and a value for n to compute the self-tensor product applied n times.

Parameters:
  • t_arr (np.ndarray or list of np.ndarray) – A single transition matrix or a list of transition matrices.

  • n (int, optional) – Number of times to apply the tensor product to a single transition matrix. Ignored if t_arr is a list of matrices. Defaults to 1.

Returns:

The resulting tensor product of the transition matrices.

Return type:

np.ndarray

Examples

Define a qubit-depolarizing and a qubit identity channel.

>>> from channel_operations import tensor
>>> from channel_families import depolarizing
>>> dim = 2
>>> p = 0.5
>>> tdepol = depolarizing(dim, p)
>>> tid = np.identity(dim**2)

Example 1: Tensor product of a list of matrices Demonstrates how to construct an inhomogeneous local depolarizer that acts on two qubits, but only adds noise to the state of the first qubit.

>>> tensor_product = tensor([tdepol, tid])
>>> tensor_product.shape
(16, 16)

Example 2: Self-tensor product of a single matrix applied n times Demostrates how to build a multi-qubit homogeneous depolarizing channel.

>>> n = 3
>>> local_depol = tensor(tdepol, n)
>>> local_depol.shape
(64, 64)
pyqch.channel_operations.twirling(t: ndarray, r_in: list[ndarray], r_out: list[ndarray])

Applies a twirling operation to a quantum channel.

The input is two representations of the finite group G, given as arrays of unitary matrices.

Parameters:
  • t (np.ndarray) – The transition matrix representing the quantum channel.

  • r_in (list of np.ndarray) – Input representation of the finite group with each element labeled by an integer.

  • r_out (list of np.ndarray) – Output representation of the finite group with each element labeled by an integer.

Returns:

The transition matrix of the twirled quantum channel.

Return type:

np.ndarray

Raises:

ValueError – If r_in and r_out do not have the same size.

Examples

>>> from channel_operations import twirling
>>> from random_generators import channel
>>> t = channel(2, k_rank = 1)
>>> r_in = [np.eye(2), np.array([[0, 1], [1, 0]])]
>>> r_out = [np.eye(2), np.array([[0, -1j], [1j, 0]])]
>>> t_twirl = twirling(t, r_in, r_out)
>>> print(t_twirl)

Notes

This function applies a twirling operation, which averages the quantum channel over a group of unitary transformations.

\[T_G(\mathcal{E})(\cdot) = \frac{1}{|G|} \sum_{g \in G} R_{out}(g^{-1}) \mathcal{E}(R_{in}(g) \cdot R_{in}(g)^\dagger) R_{out}(g^{-1})^\dagger\]

pyqch.divergences module

divergences

This module implements various quantum divergences, which are bivariate functions that assign a dissimilarity measure to quantum states.

pyqch.divergences.hockey_stick(rho: ndarray, sigma: ndarray, gamma: float) float64

Computes the hockey-stick divergence between two quantum states.

Parameters:
  • rho (np.ndarray) – The first quantum state (density matrix).

  • sigma (np.ndarray) – The second quantum state (density matrix).

  • gamma (float) – The parameter gamma used in the divergence calculation.

Returns:

The hockey-stick divergence between the two states.

Return type:

np.float64

Examples

>>> rho = np.array([[0.5, 0], [0, 0.5]])
>>> sigma = np.array([[0.4, 0.1], [0.1, 0.6]])
>>> gamma = 1.25
>>> divergence = hockey_stick(rho, sigma, gamma)
0.051776695296636865
pyqch.divergences.hs_dist(rho: ndarray, sigma: ndarray) float64

Computes the Hilbert-Schmidt distance between two quantum states.

Parameters:
  • rho (np.ndarray) – The first quantum state (density matrix).

  • sigma (np.ndarray) – The second quantum state (density matrix).

Returns:

The Hilbert-Schmidt distance between the two states.

Return type:

np.float64

Examples

>>> rho = np.array([[0.5, 0], [0, 0.5]])
>>> sigma = np.array([[0.4, 0.1], [0.1, 0.6]])
>>> hs_dist(rho, sigma)
0.19999999999999998
pyqch.divergences.max_relative_entropy(rho: ndarray, sigma: ndarray, basis: int = 2) float64

Computes the max-relative entropy between two quantum states.

Parameters:
  • rho (np.ndarray) – The first quantum state (density matrix).

  • sigma (np.ndarray) – The second quantum state (density matrix).

  • basis (int, optional) – The logarithmic base for the entropy calculation. Defaults to 2.

Returns:

The max-relative entropy between the two states.

Return type:

np.float64

Examples

>>> rho = np.array([[0.5, 0], [0, 0.5]])
>>> sigma = np.array([[0.4, 0.1], [0.1, 0.6]])
>>> max_relative_entropy(rho, sigma)
0.4796385281957086
pyqch.divergences.relative_entropy(rho: ndarray, sigma: ndarray, basis: int = 2) float64

Computes the relative entropy (Kullback-Leibler divergence) between two quantum states.

Parameters:
  • rho (np.ndarray) – The first quantum state (density matrix).

  • sigma (np.ndarray) – The second quantum state (density matrix).

  • basis (int, optional) – The logarithmic base for the entropy calculation. Defaults to 2.

Returns:

The relative entropy between the two states.

Return type:

np.float64

Examples

>>> rho = np.array([[0.5, 0], [0, 0.5]])
>>> sigma = np.array([[0.4, 0.1], [0.1, 0.6]])
>>> entropy = relative_entropy(rho, sigma)
0.060147116858855876
pyqch.divergences.tr_dist(rho: ndarray, sigma: ndarray) float64

Computes the trace distance between two quantum states.

Parameters:
  • rho (np.ndarray) – The first quantum state (density matrix).

  • sigma (np.ndarray) – The second quantum state (density matrix).

Returns:

The trace distance between the two states.

Return type:

np.float64

Examples

>>> rho = np.array([[0.5, 0], [0, 0.5]])
>>> sigma = np.array([[0.4, 0.1], [0.1, 0.6]])
>>> tr_dist(rho, sigma)
0.1414213562373095

pyqch.predicates module

predicates

This module contains functions to check whether a given matrix is a density matrix or a quantum channel.

pyqch.predicates.is_channel(t: ndarray, tol: float = 1e-06, show: bool = False) bool

Checks if the given matrix is a valid quantum channel.

Parameters:
  • t (np.ndarray) – The transition matrix representing the quantum channel.

  • tol (float, optional) – Tolerance for the numerical checks. Defaults to 1e-6.

  • show (bool, optional) – If True, prints the results of the various tests performed. Defaults to False.

Returns:

True if the matrix is a valid quantum channel, False otherwise.

Return type:

bool

Examples

>>> from predicates import is_channel
>>> from channel_families import depolarizing
>>> dim = 3
>>> p = 0.5
>>> tdepol = depolarizing(dim, p)
>>> is_channel(tdepol)
True
>>> t = 2.5 * tdepol
>>> result = is_channel(t, show = True)
For the Choi matrix of the channel:
Trace diff:  1.4999999999999996
Hermiticity diff:  0.0
Minimum eigval:  0.1388888888888888
Channel is not trace preserving.
>>> result
False
pyqch.predicates.is_density_matrix(dm: ndarray, tol: float = 1e-06, show: bool = False) bool

Checks if the given matrix is a valid density matrix.

Parameters:
  • dm (np.ndarray) – The matrix to be checked.

  • tol (float, optional) – Tolerance for the numerical checks. Defaults to 1e-6.

  • show (bool, optional) – If True, prints the results of the various tests performed. Defaults to False.

Returns:

True if the matrix is a valid density matrix, False otherwise.

Return type:

bool

Examples

>>> from predicates import is_density_matrix
>>> rho = np.array([[0.7, 0], [0, 0.3]])
>>> result = is_density_matrix(dm, show=True)
Trace diff:  0.0
Hermiticity diff:  0.0
Minimum eigval:  0.3
>>> result
True
>>> m = np.array([[1.3, 0], [0, -0.3]])
>>> result = is_density_matrix(m, show=True)
Trace diff:  0.0
Hermiticity diff:  0.0
Minimum eigval:  -0.3
>>> result
False
pyqch.predicates.is_system_compatible(state: ndarray, system: tuple[int], show: bool = False) bool

Checks if the given state and system structure are compatible.

Parameters:
  • state (np.ndarray) – The density matrix of the state.

  • system (tuple[int]) – The system structure represented by a tupe with the local dimension of the constituent subsystems.

  • show (bool, optional) – If True, prints the results of the various tests performed. by default False

Returns:

True if the state and system structure are compatible, False otherwise.

Return type:

bool

pyqch.random_generators module

random_generators

This module contains functions to generate random quantum channels and states by sampling from various distributions.

pyqch.random_generators.channel(dim_in: int, dim_out: int = None, kraus_rank: int = None, random_state: int | Generator = None) ndarray

Generates a random quantum channel by generating a random isometry (truncated Haar-random unitary) and tracing out the environment.

Parameters:
  • dim_in (int) – Input dimension of the quantum channel.

  • dim_out (int, optional) – Output dimension of the quantum channel. If None, defaults to dim_in.

  • kraus_rank (int, optional) – Rank of the Kraus operators representing the quantum channel. If None, defaults to dim_in * dim_out.

  • random_state (int | np.random.Generator, optional) – Used for drawing random variates. If seed is None, the RandomState singleton is used. If seed is an int, a new RandomState instance is used, seeded with seed. If seed is already a RandomState or Generator instance, then that object is used. Default is None.

Returns:

The transition matrix representing the random quantum channel.

Return type:

np.ndarray

Examples

>>> from random_generators import channel
>>> dim_in, dim_out = 2, 3
>>> T = channel(dim_in, dim_out)
>>> T.shape
(9, 4)
pyqch.random_generators.state(dim: int, rank: int = None, random_state: int | Generator = None) ndarray

Generates a random quantum state by sampling Haar random pure states on dimension dim * rank and computing the partiual trace on rank degrees of freedom.

Parameters:
  • dim (int) – Dimension of the quantum state.

  • rank (int, optional) – Rank of the state. If None, defaults to dim.

  • random_state (int | np.random.Generator, optional) – Used for drawing random variates. If seed is None, the RandomState singleton is used. If seed is an int, a new RandomState instance is used, seeded with seed. If seed is already a RandomState or Generator instance, then that object is used. Default is None.

Returns:

The random quantum state.

Return type:

np.ndarray

Examples

>>> from random_generators import state
>>> dim = 2
>>> rank = 1
>>> rho = state(dim, rank)
>>> print(rho)
[[ 0.7984795 +3.77722651e-19j -0.21614672+3.37920970e-01j]
 [-0.21614672-3.37920970e-01j  0.2015205 -3.08140272e-18j]]
pyqch.random_generators.state_dirichlet(dim: int, alpha: float, random_state: int | Generator = None) ndarray

Generates random quantum states by sampling their spectrum from the homogeneous Dirichlet distribution with concentration parameter alpha. The basis are rotated with Haar random unitary matrices.

Parameters:
  • dim (int) – Dimension of the quantum state.

  • alpha (float) – Concentration parameter of the Dirichlet distribution.

  • random_state (int | np.random.Generator, optional) – Used for drawing random variates. If seed is None, the RandomState singleton is used. If seed is an int, a new RandomState instance is used, seeded with seed. If seed is already a RandomState or Generator instance, then that object is used. Default is None.

Returns:

The random quantum state.

Return type:

np.ndarray

Note

As alpha -> 0 generated states are more pure, alpha = 1 is equivalent to a Hilbert-Schmidt uniform measure, and as alpha -> infty it concentrates around the maximally mixed state.

Examples

>>> from random_generators import state_dirichlet
>>> dim = 2
>>> alpha = 0.5
>>> rho = state_dirichlet(dim, alpha)
>>> print(rho)
[[ 0.33869694+0.j        -0.24593237-0.4043495j]
 [-0.24593237+0.4043495j  0.66130306+0.j       ]]
pyqch.random_generators.unitary_channel(dim: int, random_state: int | Generator = None) ndarray

Generates a random unitary quantum channel.

Parameters:
  • dim (int) – Dimension of the Holbert space.

  • random_state (int | np.random.Generator, optional) – Used for drawing random variates. If seed is None, the RandomState singleton is used. If seed is an int, a new RandomState instance is used, seeded with seed. If seed is already a RandomState or Generator instance, then that object is used. Default is None.

Returns:

The transition matrix representing the random unitary quantum channel.

Return type:

np.ndarray

Examples

>>> from random_generators import unitary_channel
>>> dim = 2
>>> U = unitary_channel(dim)
>>> U.shape
(4, 4)
>>> np.allclose(U @ U.T.conj(), np.identity(dim**2))
True

pyqch.state_families module

state_families

Provides functions to generate quantum states belonging to specific families. Currently under development.

pyqch.state_families.computational_basis(dim: int, index: int, as_density_matrix: bool = False) ndarray

Generates a quantum state in the computational basis.

Parameters:
  • dim (int) – Dimension of the Hilbert space.

  • index (int) – Index of the desired basis state.

  • as_density_matrix (bool) – Whether the state is returned as a density matrix, by default False.

Returns:

The quantum state in the computational basis.

Return type:

np.ndarray

pyqch.state_families.maximally_entangled(dim: int, as_density_matrix: bool = False) ndarray

Generates a maximally entangled state in the computational basis.

Parameters:
  • dim (int) – Dimension of the Hilbert space.

  • as_density_matrix (bool, optional) – Whether the state is returned as a density matrix, by default False.

Returns:

The maximally entangled state.

Return type:

np.ndarray

pyqch.state_transformations module

state_transformations

This module contains functions to directly transform density matrices. Such as, subsystem permutations or applying a quantum channel in a subsystem without having to build the transfer matrix for the whole system.

pyqch.state_transformations.local_channel(state: ndarray, system: tuple[int], active_sites: tuple[int] | int, channel: ndarray) ndarray

Applies the channel to the state in the chosen sites.

Parameters:
  • state (np.ndarray) – Density matrix of the state.

  • system (tuple[int]) – The system structure represented by a tuple with the local dimension of the constituent subsystems.

  • active_sites (tuple[int] | int) – The sites in which the channel acts on. If the channel’s input and output dimensions are equal, the order of the sites is used to arrange the sites before applying the channel and recover them back. If the channel’s output dimension differs from the input active_sites collapse to a single site in the relative position corresponding to active_sites[0].

  • channel (np.ndarray) – The transition matrix of the quantum channel.

Returns:

Density matrix of the state resulting from applying the channel.

Return type:

np.ndarray

pyqch.state_transformations.low_rank_representation(state: ndarray, rank_atol: float = 1e-07, output_rank: int = None, reverse: bool = False) ndarray

Generates a low rank representation of a density matrix with shape (effective rank, dim), or the reverse transformation.

Parameters:
  • state (np.ndarray) – Density matrix representation of the input state, or a low rank representation if reverse is True.

  • rank_atol (float, optional) – Absolute tolerance of accumulated probability in the discarded eigenvalues, by default 1e-7. It is overwritten by output_rank.

  • output_rank (int, optional) – Set the rank of the representation to a fixed value, by default None and rank_atol is used.

  • reverse (bool, optional) – If true, implements the reverse transformation, by default False.

Returns:

Low rank representation of the state, or the density matrix representation if reverse is True.

Return type:

np.ndarray

pyqch.state_transformations.partial_trace(state: ndarray, system: tuple[int], traced_sites: tuple[int] | int, keep_sites: bool = False) ndarray

Computes the partial trace on the state.

Parameters:
  • state (np.ndarray) – Density matrix of the state to be traced.

  • system (tuple[int]) – The system structure represented by a tuple with the local dimension of the constituent subsystems.

  • traced_sites (tuple[int] | int) – Site(s) in the system structure that will be traced out.

  • keep_sites (bool (optional)) – If True, traced_sites specifies the subsystems to keep rather than trace out. Defaults to False.

Returns:

The density matrix of the state resulting from the partial trace.

Return type:

np.ndarray

pyqch.state_transformations.subsystem_permutation(state: ndarray, system: tuple[int], permutation: tuple[int], inverse: bool = False) ndarray

Performs the permutation of the subsystems on the state.

Parameters:
  • state (np.ndarray) – The density matrix of the state.

  • system (tuple[int]) – The system structure represented by a tuple with the local dimension of the constituent subsystems.

  • permutation (tuple[int]) – The permutation that maps i to permutation[i].

  • inverse (bool) – Whether to perform the inverse of the permutation.

Returns:

The density matrix of the state after applying the permutation.

Return type:

np.ndarray

Raises:

ValueError – If the system and permutation are incompatible.

pyqch.state_transformations.subsystem_reshape(state: ndarray, system: tuple[int]) ndarray

Reshapes the density matrix to the given system structure.

Parameters:
  • state (np.ndarray) – The density matrix to be reshaped.

  • system (tuple[int]) – The system structure represented by a tuple with the local dimension of the constituent subsystems.

Returns:

The reshaped density matrix.

Return type:

np.ndarray

Raises:

ValueError – If state and system are not compatible.

pyqch.state_transformations.twirling(state: ndarray, r: list[ndarray] = None, decomposition: list[ndarray] = None)