Basic Operations on Quantum Objects
First things first
Warning
Do not run QuTiP from the installation directory.
To load the qutip modules, first call the import statement:
from qutip import *
This will load all of the user available functions. Often, we also need to import the NumPy and Matplotlib libraries with:
import numpy as np
import matplotlib.pyplot as plt
In the rest of the documentation, functions are written using qutip.module.function() notation which links to the corresponding function in the QuTiP API: Functions. However, in calling import *, we have already loaded all of the QuTiP modules. Therefore, we will only need the function name and not the complete path when calling the function from the interpreter prompt, Python script, or Jupyter notebook.
The quantum object class
Introduction
The key difference between classical and quantum mechanics is the use of operators instead of numbers as variables. Moreover, we need to specify state vectors and their properties. Therefore, in computing the dynamics of quantum systems, we need a data structure that encapsulates the properties of a quantum operator and ket/bra vectors. The quantum object class, qutip.Qobj
, accomplishes this using matrix representation.
To begin, let us create a blank Qobj
:
print(Qobj())
Output:
Quantum object: dims = [[1], [1]], shape = (1, 1), type = bra
Qobj data =
[[0.]]
where we see the blank Qobj
object with dimensions, shape, and data. Here the data corresponds to a 1x1dimensional matrix consisting of a single zero entry.
Hint
By convention, the names of Python classes, such as Qobj()
, are capitalized whereas the names of functions are not.
We can create a Qobj
with a user defined data set by passing a list or array of data into the Qobj
:
print(Qobj([[1],[2],[3],[4],[5]]))
Output:
Quantum object: dims = [[5], [1]], shape = (5, 1), type = ket
Qobj data =
[[1.]
[2.]
[3.]
[4.]
[5.]]
x = np.array([[1, 2, 3, 4, 5]])
print(Qobj(x))
Output:
Quantum object: dims = [[1], [5]], shape = (1, 5), type = bra
Qobj data =
[[1. 2. 3. 4. 5.]]
r = np.random.rand(4, 4)
print(Qobj(r))
Output:
Quantum object: dims = [[4], [4]], shape = (4, 4), type = oper, isherm = False
Qobj data =
[[0.37454012 0.95071431 0.73199394 0.59865848]
[0.15601864 0.15599452 0.05808361 0.86617615]
[0.60111501 0.70807258 0.02058449 0.96990985]
[0.83244264 0.21233911 0.18182497 0.18340451]]
Notice how both the dims and shape change according to the input data. Although dims and shape appear to be the same, dims keep track of the shapes for individual components of a multipartite system, while shape does not. We refer the reader to the section tensor products and partial traces for more information.
Note
If you are running QuTiP from a python script you must use the print
function to view the Qobj attributes.
States and operators
Manually specifying the data for each quantum object is inefficient. Even more so when most objects correspond to commonly used types such as the ladder operators of a harmonic oscillator, the Pauli spin operators for a twolevel system, or state vectors such as Fock states. Therefore, QuTiP includes predefined objects for a variety of states and operators:
States 
Command (# means optional) 
Inputs 

Fock state ket vector 

N = number of levels in Hilbert space, m = level containing excitation (0 if no m given) 
Empty ket vector 

N = number of levels in Hilbert space, 
Fock density matrix (outer product of basis) 

same as basis(N,m) / fock(N,m) 
Coherent state 

alpha = complex number (eigenvalue) for requested coherent state 
Coherent density matrix (outer product) 

same as coherent(N,alpha) 
Thermal density matrix (for n particles) 

n = particle number expectation value 
Maximally mixed density matrix 

N = number of levels in Hilbert space 
Operators 
Command (# means optional) 
Inputs 

Charge operator 

Diagonal operator with entries from M..0..N. 
Commutator 

Kind = ‘normal’ or ‘anti’. 
Diagonals operator 

Quantum object created from arrays of diagonals at given offsets. 
Displacement operator (Singlemode) 

N=number of levels in Hilbert space, alpha = complex displacement amplitude. 
Higher spin operators 

j = integer or halfinteger representing spin, s = ‘x’, ‘y’, ‘z’, ‘+’, or ‘’ 
Identity 

N = number of levels in Hilbert space. 
Identitylike 

qobj = Object to copy dimensions from. 
Lowering (destruction) operator 

same as above 
Momentum operator 

same as above 
Number operator 

same as above 
Phase operator (Singlemode) 

Singlemode PeggBarnett phase operator with ref phase phi0. 
Position operator 

same as above 
Raising (creation) operator 

same as above 
Squeezing operator (Singlemode) 

N=number of levels in Hilbert space, sp = squeezing parameter. 
Squeezing operator (Generalized) 

q1,q2 = Quantum operators (Qobj) sp = squeezing parameter. 
SigmaX 


SigmaY 


SigmaZ 


Sigma plus 


Sigma minus 


Tunneling operator 

Tunneling operator with elements of the form \(N><N+m + N+m><N\). 
As an example, we give the output for a few of these functions:
>>> basis(5,3)
Quantum object: dims = [[5], [1]], shape = (5, 1), type = ket
Qobj data =
[[0.]
[0.]
[0.]
[1.]
[0.]]
>>> coherent(5,0.50.5j)
Quantum object: dims = [[5], [1]], shape = (5, 1), type = ket
Qobj data =
[[ 0.7788017 +0.j ]
[ 0.389391420.38939142j]
[ 0. 0.27545895j]
[0.078986170.07898617j]
[0.04314271+0.j ]]
>>> destroy(4)
Quantum object: dims = [[4], [4]], shape = (4, 4), type = oper, isherm = False
Qobj data =
[[0. 1. 0. 0. ]
[0. 0. 1.41421356 0. ]
[0. 0. 0. 1.73205081]
[0. 0. 0. 0. ]]
>>> sigmaz()
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[ 1. 0.]
[ 0. 1.]]
>>> jmat(5/2.0,'+')
Quantum object: dims = [[6], [6]], shape = (6, 6), type = oper, isherm = False
Qobj data =
[[0. 2.23606798 0. 0. 0. 0. ]
[0. 0. 2.82842712 0. 0. 0. ]
[0. 0. 0. 3. 0. 0. ]
[0. 0. 0. 0. 2.82842712 0. ]
[0. 0. 0. 0. 0. 2.23606798]
[0. 0. 0. 0. 0. 0. ]]
Qobj attributes
We have seen that a quantum object has several internal attributes, such as data, dims, and shape. These can be accessed in the following way:
>>> q = destroy(4)
>>> q.dims
[[4], [4]]
>>> q.shape
(4, 4)
In general, the attributes (properties) of a Qobj
object (or any Python object) can be retrieved using the Q.attribute notation.
In addition to the those shown with the print
function, an instance of the Qobj
class also has the following attributes:
Property 
Attribute 
Description 

Data 

Matrix representing state or operator 
Dimensions 

List keeping track of shapes for individual components of a multipartite system (for tensor products and partial traces). 
Shape 

Dimensions of underlying data matrix. 
is Hermitian? 

Is the operator Hermitian or not? 
Type 

Is object of type ‘ket, ‘bra’, ‘oper’, or ‘super’? 
For the destruction operator above:
>>> q.type
'oper'
>>> q.isherm
False
>>> q.data
Dia(shape=(4, 4), num_diag=1)
The data
attribute returns a Qutip diagonal matrix.
Qobj
instances store their data in Qutip matrix format.
In the core qutip module, the Dense
, CSR
and Dia
formats are available, but other packages can add other formats.
For example, the qutipjax
module adds the Jax
and JaxDia
formats.
One can always access the underlying matrix as a numpy array using Qobj.full
.
It is also possible to access the underlying data in a common format using Qobj.data_as
.
>>> q.data_as("dia_matrix")
<4x4 sparse matrix of type '<class 'numpy.complex128'>'
with 3 stored elements (1 diagonals) in DIAgonal format>
Conversion between storage type is done using the Qobj.to
method.
>>> q.to("CSR").data
CSR(shape=(4, 4), nnz=3)
>>> q.to("CSR").data_as("csr_matrix")
<4x4 sparse matrix of type '<class 'numpy.complex128'>'
with 3 stored elements in Compressed Sparse Row format>
Note that Qobj.data_as
does not do the conversion.
QuTiP will do conversion when needed to keep everything working in any format.
However these conversions could slow down computation and it is recommended to keep to one format family where possible.
For example, core QuTiP Dense
and CSR
work well together and binary operations between these formats is efficient.
However binary operations between Dense
and Jax
should be avoided since it is not always clear whether the operation will be executed by Jax (possibly on a GPU if present) or numpy.
Qobj Math
The rules for mathematical operations on Qobj
instances are similar to standard matrix arithmetic:
>>> q = destroy(4)
>>> x = sigmax()
>>> q + 5
Quantum object: dims = [[4], [4]], shape = (4, 4), type = oper, isherm = False
Qobj data =
[[5. 1. 0. 0. ]
[0. 5. 1.41421356 0. ]
[0. 0. 5. 1.73205081]
[0. 0. 0. 5. ]]
>>> x * x
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[1. 0.]
[0. 1.]]
>>> q ** 3
Quantum object: dims = [[4], [4]], shape = (4, 4), type = oper, isherm = False
Qobj data =
[[0. 0. 0. 2.44948974]
[0. 0. 0. 0. ]
[0. 0. 0. 0. ]
[0. 0. 0. 0. ]]
>>> x / np.sqrt(2)
Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True
Qobj data =
[[0. 0.70710678]
[0.70710678 0. ]]
Of course, like matrices, multiplying two objects of incompatible shape throws an error:
>>> print(q * x)

TypeError Traceback (most recent call last)
<ipythoninput330b599f41213e> in <module>
> 1 print(q * x)
~/Documents/qutip_dev/qutip/qutip/qobj.py in __mul__(self, other)
553
554 else:
> 555 raise TypeError("Incompatible Qobj shapes")
556
557 elif isinstance(other, np.ndarray):
TypeError: Incompatible Qobj shapes
In addition, the logic operators “is equal” == and “is not equal” != are also supported.
Functions operating on Qobj class
Like attributes, the quantum object class has defined functions (methods) that operate on Qobj
class instances. For a general quantum object Q
:
Function 
Command 
Description 

Check Hermicity 

Check if quantum object is Hermitian 
Conjugate 

Conjugate of quantum object. 
Cosine 

Cosine of quantum object. 
Dagger (adjoint) 

Returns adjoint (dagger) of object. 
Diagonal 

Returns the diagonal elements. 
Diamond Norm 

Returns the diamond norm. 
Eigenenergies 

Eigenenergies (values) of operator. 
Eigenstates 

Returns eigenvalues and eigenvectors. 
Exponential 

Matrix exponential of operator. 
Full 

Returns full (not sparse) array of Q’s data. 
Groundstate 

Eigenval & eigket of Qobj groundstate. 
Matrix inverse 

Matrix inverse of the Qobj. 
Matrix Element 

Matrix element <braQket> 
Norm 

Returns L2 norm for states, trace norm for operators. 
Overlap 

Overlap between current Qobj and a given state. 
Partial Trace 

Partial trace returning components selected using ‘sel’ parameter. 
Permute 

Permutes the tensor structure of a composite object in the given order. 
Projector 

Form projector operator from given ket or bra vector. 
Sine 

Sine of quantum operator. 
Sqrt 

Matrix sqrt of operator. 
Tidyup 

Removes small elements from Qobj. 
Trace 

Returns trace of quantum object. 
Conversion 

Convert the matrix format CSR / Dense. 
Transform 

A basis transformation defined by matrix or list of kets ‘inpt’ . 
Transpose 

Transpose of quantum object. 
Truncate Neg 

Truncates negative eigenvalues 
Unit 

Returns normalized (unit) vector Q/Q.norm(). 
>>> basis(5, 3)
Quantum object: dims = [[5], [1]], shape = (5, 1), type = ket
Qobj data =
[[0.]
[0.]
[0.]
[1.]
[0.]]
>>> basis(5, 3).dag()
Quantum object: dims = [[1], [5]], shape = (1, 5), type = bra
Qobj data =
[[0. 0. 0. 1. 0.]]
>>> coherent_dm(5, 1)
Quantum object: dims = [[5], [5]], shape = (5, 5), type = oper, isherm = True
Qobj data =
[[0.36791117 0.36774407 0.26105441 0.14620658 0.08826704]
[0.36774407 0.36757705 0.26093584 0.14614018 0.08822695]
[0.26105441 0.26093584 0.18523331 0.10374209 0.06263061]
[0.14620658 0.14614018 0.10374209 0.05810197 0.035077 ]
[0.08826704 0.08822695 0.06263061 0.035077 0.0211765 ]]
>>> coherent_dm(5, 1).diag()
array([0.36791117, 0.36757705, 0.18523331, 0.05810197, 0.0211765 ])
>>> coherent_dm(5, 1).full()
array([[0.36791117+0.j, 0.36774407+0.j, 0.26105441+0.j, 0.14620658+0.j,
0.08826704+0.j],
[0.36774407+0.j, 0.36757705+0.j, 0.26093584+0.j, 0.14614018+0.j,
0.08822695+0.j],
[0.26105441+0.j, 0.26093584+0.j, 0.18523331+0.j, 0.10374209+0.j,
0.06263061+0.j],
[0.14620658+0.j, 0.14614018+0.j, 0.10374209+0.j, 0.05810197+0.j,
0.035077 +0.j],
[0.08826704+0.j, 0.08822695+0.j, 0.06263061+0.j, 0.035077 +0.j,
0.0211765 +0.j]])
>>> coherent_dm(5, 1).norm()
1.0000000175063126
>>> coherent_dm(5, 1).sqrtm()
Quantum object: dims = [[5], [5]], shape = (5, 5), type = oper, isherm = False
Qobj data =
[[0.36791117+3.66778589e09j 0.367744072.13388761e09j
0.261054411.51480558e09j 0.146206588.48384618e10j
0.088267045.12182118e10j]
[0.367744072.13388761e09j 0.36757705+2.41479965e09j
0.260935841.11446422e09j 0.14614018+8.98971115e10j
0.08822695+6.40705133e10j]
[0.261054411.51480558e09j 0.260935841.11446422e09j
0.18523331+4.02032413e09j 0.103742093.39161017e10j
0.062630613.71421368e10j]
[0.146206588.48384618e10j 0.14614018+8.98971115e10j
0.103742093.39161017e10j 0.05810197+3.36300708e10j
0.035077 +2.36883273e10j]
[0.088267045.12182118e10j 0.08822695+6.40705133e10j
0.062630613.71421368e10j 0.035077 +2.36883273e10j
0.0211765 +1.71630348e10j]]
>>> coherent_dm(5, 1).tr()
1.0
>>> (basis(4, 2) + basis(4, 1)).unit()
Quantum object: dims = [[4], [1]], shape = (4, 1), type = ket
Qobj data =
[[0. ]
[0.70710678]
[0.70710678]
[0. ]]