Introductory tutorial
This tutorial will illustrate the basics of how to use qustop.
This is a user guide for qustop and is not meant to serve as an
introduction to quantum information. For introductory material on quantum
information, please consult “Quantum Information and Quantum Computation” by
Nielsen and Chuang or the freely available lecture notes “Introduction to
Quantum Computing” by
John Watrous.
More advanced tutorials can be found on the main documentation directory.
This tutorial assumes you have qustop installed on your machine. If you
do not, please consult the getting started instructions.
States, ensembles, and measurements
Quantum states, and collections of those quantum states that form ensembles, are
the core building blocks of qustop.
Quantum states
A quantum state is a density operator
where \(\mathbb{C}^d\) is a complex Euclidean space of dimension \(d\) 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\). We will typically represent complex Euclidean spaces using the scripted capital letters \(\mathcal{A}, \mathcal{B}, \mathcal{X}, \mathcal{Y}\), etc.
Consider the density matrix corresponding to one of the four Bell states
where
such that \(\mathcal{A} = \mathbb{C}^2\) and \(\mathcal{B} =
\mathbb{C}^2\). We can use qustop to encode this state as follows.
import numpy as np
from qustop import State
# Define the |0> and |1> ket vectors.
q_0 = np.array([[1, 0]]).T
q_1 = np.array([[0, 1]]).T
# Define the respective dimensions of each complex Euclidean space.
dims = [2, 2]
# Define the Bell state vector.
psi_0 = 1/np.sqrt(2) * np.kron(q_0, q_0) + 1/np.sqrt(2) * np.kron(q_1, q_1)
# Define a `State` object corresponding to the Bell vector.
rho_0 = State(psi_0, dims)
Printing the rho_0 variable gives some further information about the
state.
>>> print(rho_0)
State:
dimensions = [2, 2],
spaces = ℂ^2 ⊗ ℂ^2,
labels = A_1 ⊗ B_2,
pure = True,
shape = (4, 4),
For instance, we see the shape attribute gives information about the
size of the density matrix of the state. There is also information about the
subsystems along with which party the subsystems belong to (either Alice or
Bob), whether the state is pure, etc.
We can use the value property of any State object to obtain the
numpy matrix representation of the quantum state
>>> print(rho_0.value)
[[0.5 0. 0. 0.5]
[0. 0. 0. 0. ]
[0. 0. 0. 0. ]
[0.5 0. 0. 0.5]]
We can also do things like take tensor products of State objects.
>>> sigma_0 = rho_0.kron(rho_0)
>>> print(sigma_0)
State:
dimensions = [2, 2, 2, 2],
spaces = ℂ^2 ⊗ ℂ^2 ⊗ ℂ^2 ⊗ ℂ^2,
labels = A_1 ⊗ B_2 ⊗ A_3 ⊗ B_4,
pure = True,
shape = (16, 16),
It is sometimes convenient to swap the subsystems of a given state. For
instance, this example shows how we can swap the second and third subsystems of
the sigma_0 state.
>>> sigma_0.swap([2, 3])
>>> print(sigma_0)
State:
dimensions = [2, 2, 2, 2],
spaces = ℂ^2 ⊗ ℂ^2 ⊗ ℂ^2 ⊗ ℂ^2,
labels = A_1 ⊗ A_3 ⊗ B_2 ⊗ B_4,
pure = True,
shape = (16, 16),
Notice how the A_3 and B_2 subsystems are swapped.
Ensembles
An ensemble is a collection of \(N\) quantum states defined over some complex Euclidean space \(\mathcal{X}\) as
where \((p_1, \ldots, p_N)\) is a vector of probability values and where \(\rho_1, \ldots, \rho_N \in \text{D}(\mathcal{X})\) are quantum states.
Recall the four two-qubit Bell states
The corresponding density operators may be defined as
We can define the following ensemble consisting of the Bell states where the probability of selecting any one state from the ensemble is equal to \(1/4\):
In qustop, we would define this ensemble like so
from toqito.states import bell
from qustop import State, Ensemble
# Construct the corresponding density matrices of the Bell states.
dims = [2, 2]
states = [
State(bell(0) * bell(0).conj().T, dims),
State(bell(1) * bell(1).conj().T, dims),
State(bell(2) * bell(2).conj().T, dims),
State(bell(3) * bell(3).conj().T, dims)
]
ensemble = Ensemble(states=states, probs=[1/4, 1/4, 1/4, 1/4])
Printing out any Ensemble object gives us some information about the
contents:
>>> print(ensemble)
Ensemble:
num_states = 4,
states = ρ_0 ⊗ ρ_1 ⊗ ρ_2 ⊗ ρ_3,
is_mutually_orthogonal = True,
is_linearly_independent = True,
We can see certain pieces of information including how many states are contained in the ensemble, whether the states in the ensemble are all mutually orthogonal, linearly independent, etc.
We can access any of the states from the Ensemble object using standard
array indexing notation. For instance, here is how we can access the first state
in the ensemble.
>>> print(ensemble[0])
State:
dimensions = [2, 2],
spaces = ℂ^2 ⊗ ℂ^2,
labels = A_1 ⊗ B_2,
pure = True,
shape = (4, 4),
We may also wish to apply some of the functions that we saw before for
State objects onto the entire ensemble. For instance, here is an example
of how we can swap the first and second subsystems of each state in the
ensemble.
>>> ensemble.swap([1, 2])
>>> print(ensemble[0])
State:
dimensions = [2, 2],
spaces = ℂ^2 ⊗ ℂ^2,
labels = B_2 ⊗ A_1,
pure = True,
shape = (4, 4),
Measurements
A measurement is defined as a function
for some finite and nonempty set \(\Sigma\) and some complex Euclidean space \(\mathcal{X}\) satisfying the constraint that
There are many different classes of measurements.
Quantum state distinguishability
Given an ensemble of quantum states, we can consider the setting of quantum state distinguishability. This setting can be considered as an interaction between two parties–typically denoted as Alice and Bob.
A more in-depth description and tutorial on this setting in qustop can
be found in:
More in-depth descriptions pertaining to quantum state distinguishability under positive, PPT, and separable measurements can be found in: