Note
Go to the end to download the full example code.
Introductory Tutorial¶
This tutorial will illustrate the basics of how to use |toqito⟩
. This
will cover how to instantiate and use the fundamental objects that
|toqito⟩
provides; namely quantum states, channels, and measurements.
This is an introduction to the functionality in |toqito⟩
and is not meant to serve as an
introduction to quantum information. For more information, please consult the book [1] or the freely available lecture notes [2].
This tutorial assumes you have |toqito⟩
installed on your machine. If you
do not, please consult the installation instructions in Getting started.
States¶
A quantum state is a density operator
where \(\mathcal{X}\) is a complex Euclidean space and where \(\text{D}(\cdot)\) represents the set of density matrices, that is, the set of matrices that are positive semidefinite with trace equal to \(1\).
Quantum States¶
A complete overview of the scope of quantum states can be found here
The standard basis ket vectors given as \(|0\rangle\) and \(|1\rangle\) where
can be defined in |toqito⟩
as such
41 from toqito.matrices import standard_basis
42
43 # |0>
44 standard_basis(2)[0]
array([[1.],
[0.]])
To get the other ket
49 # |1>
50 standard_basis(2)[1]
array([[0.],
[1.]])
One may define one of the four Bell states written as
using |toqito⟩
as
60 import numpy as np
61
62 e_0, e_1 = standard_basis(2)
63 u_0 = 1 / np.sqrt(2) * (np.kron(e_0, e_0) + np.kron(e_1, e_1))
64 u_0
array([[0.70710678],
[0. ],
[0. ],
[0.70710678]])
The corresponding density operator of \(u_0\) can be obtained from
In |toqito⟩
, that can be obtained as
81 import numpy as np
82
83 e_0, e_1 = standard_basis(2)
84 u_0 = 1 / np.sqrt(2) * (np.kron(e_0, e_0) + np.kron(e_1, e_1))
85 rho_0 = u_0 @ u_0.conj().T
86 rho_0
array([[0.5, 0. , 0. , 0.5],
[0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0. ],
[0.5, 0. , 0. , 0.5]])
Alternatively, we may leverage the bell
function in |toqito⟩
to
generate all four Bell states defined as
in a more concise manner as
105 from toqito.states import bell
106 import numpy as np
107
108 bell(0)
array([[0.70710678],
[0. ],
[0. ],
[0.70710678]])
The Bell states constitute one such well-known class of quantum states. There are many other classes of states that are widely used in the field of quantum information. For instance, the GHZ state
is a well-known 3-qubit quantum state. We can invoke this using |toqito⟩
as
120 from toqito.states import ghz
121
122 ghz(2, 3)
array([[0.70710678],
[0. ],
[0. ],
[0. ],
[0. ],
[0. ],
[0. ],
[0.70710678]])
While the 3-qubit form of the GHZ state is arguably the most notable, it is possible to define a generalized GHZ state
This generalized state may be obtained in |toqito⟩
as well. For instance,
here is the GHZ state \(\mathbb{C}^{4^{\otimes 7}}\) as
140 from toqito.states import ghz
141 import numpy as np
142
143 dim = 4
144 num_parties = 7
145 coeffs = [1 / np.sqrt(30), 2 / np.sqrt(30), 3 / np.sqrt(30), 4 / np.sqrt(30)]
146 vec = ghz(dim, num_parties, coeffs)
147 for idx in np.nonzero(vec)[0]:
148 print(f"Index: {int(idx)}, Value: {vec[idx][0]:.8f}")
Index: 0, Value: 0.18257419
Index: 5461, Value: 0.36514837
Index: 10922, Value: 0.54772256
Index: 16383, Value: 0.73029674
Properties of Quantum States¶
Given a quantum state, it is often useful to be able to determine certain properties of the state.
For instance, we can check if a quantum state is pure, that is, if the density matrix that describes the state has rank 1.
Any one of the Bell states serve as an example of a pure state
163 from toqito.states import bell
164 from toqito.state_props import is_pure
165
166 rho = bell(0) @ bell(0).conj().T
167 is_pure(rho)
True
Another property that is useful is whether a given state is PPT (positive partial transpose), that is, whether the state remains positive after taking the partial transpose of the state.
For quantum states consisting of shared systems of either dimension \(2 \otimes 2\) or \(2 \otimes 3\), the notion of whether a state is PPT serves as a method to determine whether a given quantum state is entangled or separable.
As an example, any one of the Bell states constitute a canonical maximally entangled state over \(2 \otimes 2\) and therefore should not satisfy the PPT criterion.
184 from toqito.states import bell
185 from toqito.state_props import is_ppt
186
187 rho = bell(2) @ bell(2).conj().T
188 is_ppt(rho)
False
As we can see, the PPT criterion is False
for an entangled state in
\(2 \otimes 2\).
Determining whether a quantum state is separable or entangled is often useful
but is, unfortunately, NP-hard. For a given density matrix represented by a
quantum state, we can use |toqito⟩
to run a number of separability tests
from the literature to determine if it is separable or entangled.
For instance, the following bound-entangled tile state is found to be entangled (i.e. not separable).
203 import numpy as np
204 from toqito.state_props import is_separable
205 from toqito.states import tile
206
207 rho = np.identity(9)
208 for i in range(5):
209 rho -= tile(i) @ tile(i).conj().T
210
211 rho /= 4
212 is_separable(rho)
False
Further properties that one can check via |toqito⟩
may be found on this page.
Distance Metrics for Quantum States¶
Given two quantum states, it is often useful to have some way in which to quantify how similar or different one state is from another.
One well known metric is the fidelity function defined for two quantum states. For two states \(\rho\) and \(\sigma\), one defines the fidelity between \(\rho\) and \(\sigma\) as
where \(|| \cdot ||_1\) denotes the trace norm.
The fidelity function yields a value between \(0\) and \(1\), with \(0\) representing the scenario where \(\rho\) and \(\sigma\) are as different as can be and where a value of \(1\) indicates a scenario where \(\rho\) and \(\sigma\) are identical.
Let us consider an example in |toqito⟩
where we wish to calculate the
fidelity function between quantum states that happen to be identical.
242 from toqito.states import bell
243 from toqito.state_metrics import fidelity
244 import numpy as np
245
246 # Define two identical density operators.
247 rho = bell(0) @ bell(0).conj().T
248 sigma = bell(0) @ bell(0).conj().T
249
250 # Calculate the fidelity between `rho` and `sigma`
251 np.around(fidelity(rho, sigma), decimals=2)
np.float64(1.0)
There are a number of other metrics one can compute on two density matrices
including the trace norm, trace distance. These and others are also available
in |toqito⟩
. For a full list of distance metrics one can compute on
quantum states, consult the docs.
Channels¶
A quantum channel can be defined as a completely positive and trace preserving linear map.
More formally, let \(\mathcal{X}\) and \(\mathcal{Y}\) represent complex Euclidean spaces and let \(\text{L}(\cdot)\) represent the set of linear operators. Then a quantum channel, \(\Phi\) is defined as
such that \(\Phi\) is completely positive and trace preserving.
Quantum Channels¶
The partial trace operation is an often used in various applications of quantum information. The partial trace is defined as
\[\left( \text{Tr} \otimes \mathbb{I}_{\mathcal{Y}} \right) \left(X \otimes Y \right) = \text{Tr}(X)Y\]
where \(X \in \text{L}(\mathcal{X})\) and \(Y \in \text{L}(\mathcal{Y})\) are linear operators over complex Euclidean spaces \(\mathcal{X}\) and \(\mathcal{Y}\).
Consider the following matrix
Taking the partial trace over the second subsystem of \(X\) yields the following matrix
By default, the partial trace function in |toqito⟩
takes the trace of the second
subsystem.
309 from toqito.matrix_ops import partial_trace
310 import numpy as np
311
312 test_input_mat = np.arange(1, 17).reshape(4, 4)
313 partial_trace(test_input_mat)
array([[ 7, 11],
[23, 27]])
By specifying the sys = [0]
argument, we can perform the partial trace over the first
subsystem (instead of the default second subsystem as done above). Performing the partial
trace over the first subsystem yields the following matrix
327 from toqito.matrix_ops import partial_trace
328 import numpy as np
329
330 test_input_mat = np.arange(1, 17).reshape(4, 4)
331 partial_trace(test_input_mat, sys=[0])
array([[12, 14],
[20, 22]])
Another often useful channel is the partial transpose. The partial transpose is defined as
\[\left( \text{T} \otimes \mathbb{I}_{\mathcal{Y}} \right) \left(X\right)\]
where \(X \in \text{L}(\mathcal{X})\) is a linear operator over the complex Euclidean space \(\mathcal{X}\) and where \(\text{T}\) is the transpose mapping \(\text{T} \in \text{T}(\mathcal{X})\) defined as
for all \(X \in \text{L}(\mathcal{X})\).
Consider the following matrix
Performing the partial transpose on the matrix \(X\) over the second subsystem yields the following matrix
By default, in |toqito⟩
, the partial transpose function performs the transposition on
the second subsystem as follows.
375 from toqito.matrix_ops import partial_transpose
376 import numpy as np
377
378 test_input_mat = np.arange(1, 17).reshape(4, 4)
379 partial_transpose(test_input_mat)
array([[ 1, 5, 3, 7],
[ 2, 6, 4, 8],
[ 9, 13, 11, 15],
[10, 14, 12, 16]])
By specifying the sys = [0]
argument, we can perform the partial transpose over the
first subsystem (instead of the default second subsystem as done above). Performing the
partial transpose over the first subsystem yields the following matrix
396 from toqito.matrix_ops import partial_transpose
397 import numpy as np
398
399 test_input_mat = np.arange(1, 17).reshape(4, 4)
400 partial_transpose(test_input_mat, sys=[0])
array([[ 1, 2, 9, 10],
[ 5, 6, 13, 14],
[ 3, 4, 11, 12],
[ 7, 8, 15, 16]])
Applying Quantum Channels
Another important operation when working with quantum channels is applying them to quantum states. apply_channel()
in |toqito⟩
provides a convenient way to apply a quantum channel (represented by its Choi matrix) to a given quantum state.
Here, we illustrate how to apply two widely used channels – the depolarizing channel and the dephasing channel – using apply_channel()
.
Depolarizing Channel
The depolarizing channel replaces a state with the maximally mixed state with probability \(p\) and leaves it unchanged with probability \((1-p)\). Mathematically, it is defined as
where \(\mathbb{I}\) is the identity operator and \(d\) is the dimension of the Hilbert space. The example below applies the depolarizing channel with \(p=0.3\) to the computational basis state \(|0\rangle\).
418 import numpy as np
419 from toqito.channel_ops import apply_channel
420 from toqito.channels import depolarizing
421
422 # Create a quantum state |0⟩⟨0|.
423 rho = np.array([[1, 0], [0, 0]])
424
425 # Generate the depolarizing channel Choi matrix with noise probability p = 0.3.
426 choi = depolarizing(2, 0.3)
427
428 # Apply the depolarizing channel using apply_channel.
429 output_state = apply_channel(rho, choi)
430 print(output_state)
[[0.65 0. ]
[0. 0.35]]
Dephasing Channel
The dephasing channel reduces the off-diagonal elements of a density matrix without changing the diagonal entries, thereby diminishing quantum coherence. It is commonly expressed as
where \(Z\) is the Pauli-Z operator and \(p\) represents the dephasing probability. The example below demonstrates how to apply the dephasing channel with \(p=0.4\) to the plus state \(|+\rangle = \frac{1}{\sqrt{2}}(|0\rangle + |1\rangle)\).
442 import numpy as np
443 from toqito.channel_ops import apply_channel
444 from toqito.channels import dephasing
445
446 # Create a quantum state |+⟩⟨+|.
447 rho = np.array([[0.5, 0.5], [0.5, 0.5]])
448
449 # Generate the dephasing channel Choi matrix with dephasing probability p = 0.4.
450 choi = dephasing(2, 0.4)
451
452 # Apply the dephasing channel using apply_channel.
453 output_state = apply_channel(rho, choi)
454 print(output_state)
[[0.5 0.2]
[0.2 0.5]]
Noisy Channels¶
Quantum noise channels model the interaction between quantum systems and their environment, resulting in decoherence and loss of quantum information. The |toqito⟩
library provides implementations of common noise models used in quantum information processing.
Phase Damping Channel
The phase damping channel models quantum decoherence where phase information is lost without any energy dissipation. It is characterized by a parameter \(\gamma\) representing the probability of phase decoherence.
The phase damping channel can be applied to a quantum state as follows:
473 from toqito.channels import phase_damping
474 import numpy as np
475
476 # Create a density matrix with coherence.
477 rho = np.array([[1, 0.5], [0.5, 1]])
478
479 # Apply phase damping with γ = 0.2.
480 result = phase_damping(rho, gamma=0.2)
481 print(result)
[[1. +0.j 0.4472136+0.j]
[0.4472136+0.j 1. +0.j]]
Note that the off-diagonal elements (coherences) are reduced by a factor of \(\sqrt{1-\gamma}\), while the diagonal elements (populations) remain unchanged.
Amplitude Damping Channel
The amplitude damping channel models energy dissipation from a quantum system to its environment, such as the spontaneous emission of a photon. It is parameterized by \(\gamma\), representing the probability of losing a quantum of energy.
Here’s how to use the amplitude damping channel:
496 from toqito.channels import amplitude_damping
497 import numpy as np
498
499 # Create a quantum state.
500 rho = np.array([[0.5, 0.5], [0.5, 0.5]])
501
502 # Apply amplitude damping with γ = 0.3.
503 result = amplitude_damping(rho, gamma=0.3)
504 print(result)
[[0.65 +0.j 0.41833001+0.j]
[0.41833001+0.j 0.35 +0.j]]
Bit-Flip Channel
The bit-flip channel randomly flips the state of a qubit with probability \(p\), analogous to the classical bit-flip error in classical information theory.
516 from toqito.channels import bitflip
517 import numpy as np
518
519 # Create a quantum state |0⟩⟨0|.
520 rho = np.array([[1, 0], [0, 0]])
521
522 # Apply bit-flip with probability = 0.25.
523 result = bitflip(rho, prob=0.25)
524 print(result)
[[0.75+0.j 0. +0.j]
[0. +0.j 0.25+0.j]]
Observe that the result is a mixed state with 75% probability of being in state \(|0\rangle\) and 25% probability of being in state \(|1\rangle\), as expected for a bit flip error with probability \(p = 0.25\).
Pauli Channel
The Pauli channel is a quantum noise model that applies a probabilistic mixture of Pauli operators to a quantum state. It is defined by a probability vector \((p_0, \ldots, p_{4^q - 1})\), where \(q\) is the number of qubits, and \(P_i\) are the Pauli operators acting on the system.
For example, when \(q = 1\), the Pauli operators are: \(P_0 = I\), \(P_1 = X\), \(P_2 = Y\), and \(P_3 = Z\). For multiple qubits, these operators are extended as tensor products.
It is also worth noting that when
\(P_2 = 0\), and \(P_3 = 0\),
pauli_channel()
is equivalent to abitflip()
channel\(P_1 = 0\), and \(P_2 = 0\),
pauli_channel()
is equivalent to a Phase Flip channel\(P_1 = 0\), and \(P_3 = 0\),
pauli_channel()
is equivalent to a Bit and Phase Flip channel
The Pauli channel can be used to apply noise to an input quantum state or generate a Choi matrix.
553 from toqito.channels import pauli_channel
554 import numpy as np
555
556 # Define probabilities for single-qubit Pauli operators.
557 probabilities = np.array([0.5, 0.2, 0.2, 0.1])
558
559 # Define an input density matrix.
560 rho = np.array([[1, 0], [0, 0]])
561
562 # Apply the Pauli channel.
563 _, result = pauli_channel(prob=probabilities, input_mat=rho)
564 print(result)
[[0.6+0.j 0. +0.j]
[0. +0.j 0.4+0.j]]
Here, the probabilities correspond to applying the identity (\(I\)), bit-flip (\(X\)), phase-flip (\(Z\)), and combined bit-phase flip (\(Y\)) operators.
Measurements¶
A measurement can be defined as a function
satisfying
where \(\Sigma\) represents a set of measurement outcomes and where \(\mu(a)\) represents the measurement operator associated with outcome \(a \in \Sigma\).
POVM¶
POVM (Positive Operator-Valued Measure) is a set of positive operators that sum up to the identity.
Consider the following matrices:
Our function expects this set of operators to be a POVM because it checks if the operators sum up to the identity, ensuring that the measurement outcomes are properly normalized.
610 from toqito.measurement_props import is_povm
611 import numpy as np
612
613 meas_1 = np.array([[1, 0], [0, 0]])
614 meas_2 = np.array([[0, 0], [0, 1]])
615 meas = [meas_1, meas_2]
616 is_povm(meas)
True
Random POVM¶
We may also use random_povm()
to randomly generate a POVM, and can verify that a
randomly generated set satisfies the criteria for being a POVM set.
625 from toqito.measurement_props import is_povm
626 from toqito.rand import random_povm
627 import numpy as np
628
629 dim, num_inputs, num_outputs = 2, 2, 2
630 measurements = random_povm(dim, num_inputs, num_outputs)
631 is_povm([measurements[:, :, 0, 0], measurements[:, :, 0, 1]])
True
Alternatively, the following matrices do not constitute a POVM set.
649 from toqito.measurement_props import is_povm
650 import numpy as np
651
652 non_meas_1 = np.array([[1, 2], [3, 4]])
653 non_meas_2 = np.array([[5, 6], [7, 8]])
654 non_meas = [non_meas_1, non_meas_2]
655 is_povm(non_meas)
False
Measurement Operators¶
Consider the following state:
where we define \(u u^* = \rho \in \text{D}(\mathcal{X})\) and \(e_0\) and \(e_1\) are the standard basis vectors.
The measurement operators are defined as shown below:
677 from toqito.matrices import standard_basis
678 from toqito.measurement_ops import measure
679 import numpy as np
680
681 e_0, e_1 = standard_basis(2)
682
683 u = (1 / np.sqrt(3)) * e_0 + (np.sqrt(2 / 3)) * e_1
684 rho = u @ u.conj().T
685
686 proj_0 = e_0 @ e_0.conj().T
687 proj_1 = e_1 @ e_1.conj().T
Then the probability of obtaining outcome \(0\) is given by
695 measure(proj_0, rho)
np.float64(0.3333333333333334)
Similarly, the probability of obtaining outcome \(1\) is given by
703 measure(proj_1, rho)
np.float64(0.6666666666666667)
Pretty Good Measurement¶
Consider “pretty good measurement” on the set of trine states.
The pretty good measurement (PGM), also known as the “square root measurement” is a set of POVMs \((G_1, \ldots, G_n)\) defined as
This measurement was initially defined in [3] and has found applications in quantum state discrimination tasks. While not always optimal, the PGM provides a reasonable measurement strategy that can be computed efficiently.
For example, consider the following trine states:
726 from toqito.states import trine
727 from toqito.measurements import pretty_good_measurement
728
729 states = trine()
730 probs = [1 / 3, 1 / 3, 1 / 3]
731 pgm = pretty_good_measurement(states, probs)
732 pgm
[array([[0.66666667, 0. ],
[0. , 0. ]]), array([[0.16666667, 0.28867513],
[0.28867513, 0.5 ]]), array([[ 0.16666667, -0.28867513],
[-0.28867513, 0.5 ]])]
Pretty Bad Measurement¶
Similarly, we can consider so-called “pretty bad measurement” (PBM) on the set of trine states [4].
The pretty bad measurement (PBM) is a set of POVMs \((B_1, \ldots, B_n)\) defined as
Like the PGM, the PBM provides a measurement strategy for quantum state discrimination, but with different properties that can be useful in certain contexts.
752 from toqito.states import trine
753 from toqito.measurements import pretty_bad_measurement
754
755 states = trine()
756 probs = [1 / 3, 1 / 3, 1 / 3]
757 pbm = pretty_bad_measurement(states, probs)
758 pbm
[array([[0.16666667, 0. ],
[0. , 0.5 ]]), array([[ 0.41666667, -0.14433757],
[-0.14433757, 0.25 ]]), array([[0.41666667, 0.14433757],
[0.14433757, 0.25 ]])]
References¶
Total running time of the script: (0 minutes 0.083 seconds)