mimiqcircuits.operations.noisechannel.kraus

Functions

is_valid_power_of_2(n)

Check if a number is a power of 2.

Classes

Kraus(E[, rtol])

Kraus(kmatrices).

class mimiqcircuits.operations.noisechannel.kraus.Kraus(E, rtol=1e-12)[source]

Bases: krauschannel

Kraus(kmatrices).

Custom N-qubit Kraus channel specified by a list of 2^N x 2^N Kraus matrices.

A Kraus channel is defined by:

\[\mathcal{E}(\rho) = \sum_k E_k \rho E_k^\dagger,\]

where \(E_k\) are Kraus matrices that need to fulfill \(\sum_k E_k^\dagger E_k = I\).

If the Kraus matrices are all proportional to unitaries, use MixedUnitary() instead.

The Kraus matrices are defined in the computational basis in the usual textbook order (the first qubit corresponds to the left-most qubit). For 1 qubit, we have \(|0\rangle\), \(|1\rangle\). For 2 qubits, we have \(|00\rangle\), \(|01\rangle\), \(|10\rangle\), \(|11\rangle\).

Note

Currently, only 1 and 2-qubit custom Kraus channels are supported.

A Kraus channel becomes loss-aware simply by including one or more LossyOperator branches in E. In that case, hasloss() returns True and lossoperators(), survivaloperators(), and losseffect() can be used to inspect the leakage structure.

The entries of a LossyOperator are amplitudes, not probabilities. The corresponding loss probabilities appear in losseffect(), which sums \(L_k^\dagger L_k\) over all lossy branches \(L_k\).

See also

MixedUnitary GateCustom LossyOperator Operator

Parameters:

kmatrices (list) – List of \(2^N \times 2^N\) complex matrices. The number of qubits is equal to \(N\).

Examples

>>> from mimiqcircuits import *
>>> from symengine import *
>>> c = Circuit()
>>> c.push(Kraus([Matrix([[1, 0], [0, sqrt(0.9)]]), Matrix([[0, sqrt(0.1)], [0, 0]])]), 0)
1-qubit circuit with 1 instruction:
└── Kraus(Operator([[1, 0], [0, 0.948683298050514]]), Operator([[0, 0.316227766016838], [0, 0]])) @ q[0]

Kraus Matrices

>>> g=Kraus([Matrix([[1, 0], [0, sqrt(0.9)]]), Matrix([[0, sqrt(0.1)], [0, 0]])])
>>> g.krausmatrices()
[[1.0, 0]
[0, 0.948683298050514]
, [0, 0.316227766016838]
[0, 0]
]

# Example 2: Kraus with Projector0 and Projector1

>>> c = Circuit()
>>> c.push(Kraus([Projector0(), Projector1()]), 0)
1-qubit circuit with 1 instruction:
└── Kraus(P₀(1), P₁(1)) @ q[0]

# Example 3: Kraus with a matrix and Projector1

>>> c = Circuit()
>>> c.push(Kraus([Matrix([[1, 0], [0, 0]]), Projector1()]), 0)
1-qubit circuit with 1 instruction:
└── Kraus(Operator([[1, 0], [0, 0]]), P₁(1)) @ q[0]

# Example 4: Loss-aware Kraus with a LossyOperator branch

>>> lossy = LossyOperator(Matrix([[0, 0], [0, sqrt(0.2)]]))
>>> survival = Operator(Matrix([[1, 0], [0, sqrt(0.8)]]))
>>> lk = Kraus([survival, lossy])
>>> lk.hasloss()
True
>>> lk.lossoperators()
[1-qubit LossyOperator (lossy=(1,)):
├── 0 0
└── 0 0.447213595499958]
>>> lk.survivaloperators()
[1-qubit Operator:
├── 1 0
└── 0 0.894427190999916]
>>> lk.losseffect()
1-qubit Operator:
├── 0.0 + 0.0*I 0.0 + 0.0*I
└── 0.0 + 0.0*I 0.2 + 0.0*I

# Example 5: Symbolic Kraus evaluation

>>> x = Symbol("x")
>>> g = Kraus([Projector0(), Projector1(x)])
>>> g
Kraus(P₀(1), P₁(x))
>>> g.evaluate({x: 1})
Kraus(P₀(1), P₁(1))
__init__(E, rtol=1e-12)[source]
evaluate(values)[source]

Evaluates symbolic parameters in Kraus matrices using a dictionary of values.

Parameters:

values (dict) – A dictionary where keys are symbolic variables and values are the corresponding numerical values.

Returns:

A new Kraus instance with evaluated matrices.

Return type:

Kraus

krausoperators()[source]

Returns the Kraus operators.

hasloss()[source]

Return True when the channel contains any LossyOperator branch.

lossoperators()[source]

Return the LossyOperator branches of the channel.

survivaloperators()[source]

Return the non-lossy branches of the channel.

losseffect(tol=1e-12)[source]

Compute the loss-probability operator over all lossy branches.

This returns the operator

\[\Lambda = \sum_k L_k^\dagger L_k,\]

where \(L_k\) runs over the LossyOperator branches of the channel.

\(\Lambda\) is a positive semidefinite operator whose diagonal entries give the loss probabilities for each computational basis state. For a basis state \(|i\rangle\),

\[\langle i|\Lambda|i\rangle\]

is the probability that \(|i\rangle\) leaks out of the computational subspace. For a general state \(|\psi\rangle\), the total loss probability is \(\langle\psi|\Lambda|\psi\rangle\).

Note

The entries of LossyOperator are amplitudes, not probabilities. losseffect() converts them into probabilities by summing \(L_k^\dagger L_k\).

The survival and lossy operators together satisfy

\[\sum_k S_k^\dagger S_k + \Lambda = I,\]

where \(S_k\) are the non-lossy branches.

If the channel has no lossy operators, the zero matrix is returned.

Examples

>>> from mimiqcircuits import *
>>> from symengine import Matrix, sqrt
>>> g = Kraus([Matrix([[1, 0], [0, sqrt(0.9)]]),
...            LossyOperator(Matrix([[0, 0], [0, sqrt(0.1)]]))])
>>> g.losseffect()
1-qubit Operator:
├── 0.0 + 0.0*I 0.0 + 0.0*I
└── 0.0 + 0.0*I 0.1 + 0.0*I
__str__()[source]

String representation, listing either AbstractOperators or matrix slices.

unwrappedkrausmatrices()[source]

Returns the unwrapped Kraus matrices.