Manipulating States and Operators

Introduction

In the previous guide section Basic Operations on Quantum Objects, we saw how to create states and operators, using the functions built into QuantumToolbox. In this portion of the guide, we will look at performing basic operations with states and operators. For more detailed demonstrations on how to use and manipulate these objects, see the examples given in the tutorial section.

State Vectors (kets or bras)

Here we begin by creating a Fock basis (or fock) vacuum state vector $|0\rangle$ with in a Hilbert space with 5 number states, from 0 to 4:

vac = basis(5, 0)
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 1.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

and then create a lowering operator $\hat{a}$ corresponding to 5 number states using the destroy function:

a = destroy(5)
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=false
5×5 SparseMatrixCSC{ComplexF64, Int64} with 4 stored entries:
     ⋅      1.0+0.0im          ⋅              ⋅          ⋅    
     ⋅          ⋅      1.41421+0.0im          ⋅          ⋅    
     ⋅          ⋅              ⋅      1.73205+0.0im      ⋅    
     ⋅          ⋅              ⋅              ⋅      2.0+0.0im
     ⋅          ⋅              ⋅              ⋅          ⋅    

Now lets apply the lowering operator \hat{a} to our vacuum state vac:

a * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

We see that, as expected, the vacuum is transformed to the zero vector. A more interesting example comes from using the adjoint of the lowering operator $\hat{a}$, the raising operator $\hat{a}^\dagger$:

a' * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 1.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

The raising operator has in indeed raised the state vac from the vacuum to the $|1\rangle$ state. Instead of using the adjoint method to raise the state, we could have also used the built-in create function to make a raising operator:

ad = create(5)
ad * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 1.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

which does the same thing. We can raise the vacuum state more than once by successively apply the raising operator:

ad * ad * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
                0.0 + 0.0im
                0.0 + 0.0im
 1.4142135623730951 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

or just taking the square of the raising operator $\left(\hat{a}^\dagger\right)^2$:

ad^2 * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
                0.0 + 0.0im
                0.0 + 0.0im
 1.4142135623730951 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

Applying the raising operator twice gives the expected $\sqrt{n+1}$ dependence. We can use the product of $\hat{a}^\dagger \hat{a}$ to also apply the number operator to the state vector vac:

ad * a * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

or on the $|1\rangle$ state:

ad * a * (ad * vac)
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 1.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

or on the $|2\rangle$ state:

ad * a * (ad^2 * vac)
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
                0.0 + 0.0im
                0.0 + 0.0im
 2.8284271247461907 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

Notice how in this last example, application of the number operator does not give the expected value $n=2$, but rather $2\sqrt{2}$. This is because this last state is not normalized to unity as $\hat{a}^\dagger|n\rangle=\sqrt{n+1}|n+1\rangle$. Therefore, we should normalize (or use unit) our vector first:

ad * a * normalize(ad^2 * vac)
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
                0.0 + 0.0im
                0.0 + 0.0im
 2.0000000000000004 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

Since we are giving a demonstration of using states and operators, we have done a lot more work than we should have. For example, we do not need to operate on the vacuum state to generate a higher number Fock state. Instead we can use the basis (or fock) function to directly obtain the required state:

ket = basis(5, 2)
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 0.0 + 0.0im
 1.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

Notice how it is automatically normalized. We can also use the built in number operator num:

n = num(5)
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 SparseMatrixCSC{ComplexF64, Int64} with 5 stored entries:
 0.0+0.0im      ⋅          ⋅          ⋅          ⋅    
     ⋅      1.0+0.0im      ⋅          ⋅          ⋅    
     ⋅          ⋅      2.0+0.0im      ⋅          ⋅    
     ⋅          ⋅          ⋅      3.0+0.0im      ⋅    
     ⋅          ⋅          ⋅          ⋅      4.0+0.0im

Therefore, instead of ad * a * normalize(ad^2 * vac), we have:

n * ket
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 0.0 + 0.0im
 2.0 + 0.0im
 0.0 + 0.0im
 0.0 + 0.0im

We can also create superpositions of states:

ket = normalize(basis(5, 0) + basis(5, 1))
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
 0.7071067811865475 + 0.0im
 0.7071067811865475 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

where we have used the normalize function again to normalize the state. Apply the number opeartor again:

n * ket
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
                0.0 + 0.0im
 0.7071067811865475 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im
                0.0 + 0.0im

We can also create coherent states and squeezed states by applying the displace and squeeze functions to the vacuum state:

vac = basis(5, 0)

d = displace(5, 1im)

s = squeeze(5, 0.25 + 0.25im)

d * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
  0.6065568176126114 + 0.0im
                 0.0 + 0.6062813254779008im
 -0.4303873979781239 + 0.0im
                 0.0 - 0.24104350624628343im
 0.14552146626026782 + 0.0im
d * s * vac
Quantum Object:   type=Ket   dims=[5]   size=(5,)
5-element Vector{ComplexF64}:
   0.6589378628979528 + 0.08139380927428255im
  0.10779461991734264 + 0.5157973476443225im
   -0.375672173778824 - 0.013268528813115979im
 -0.02688063342547209 - 0.2382877475293735im
    0.263528135717665 + 0.11512177609766486im

Of course, displacing the vacuum gives a coherent state, which can also be generated using the built in coherent function.

Density matrices

One of the main purpose of QuantumToolbox is to explore the dynamics of open quantum systems, where the most general state of a system is no longer a state vector, but rather a density matrix. Since operations on density matrices operate identically to those of vectors, we will just briefly highlight creating and using these structures.

The simplest density matrix is created by forming the outer-product $|\psi\rangle\langle\psi|$ of a ket vector:

ket = basis(5, 2)
ket * ket'
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 Matrix{ComplexF64}:
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  1.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im

A similar task can also be accomplished via the fock_dm or ket2dm functions:

fock_dm(5, 2)
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 Matrix{ComplexF64}:
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  1.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
ket2dm(ket)
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 Matrix{ComplexF64}:
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  1.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im

If we want to create a density matrix with equal classical probability of being found in the $|2\rangle$ or $|4\rangle$ number states, we can do the following:

0.5 * fock_dm(5, 2) + 0.5 * fock_dm(5, 4) # with fock_dm
0.5 * ket2dm(basis(5, 2)) + 0.5 * ket2dm(basis(5, 4)) # with ket2dm
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 Matrix{ComplexF64}:
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.5+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  0.5+0.0im

There are also several other built-in functions for creating predefined density matrices, for example coherent_dm and thermal_dm which create coherent state and thermal state density matrices, respectively.

coherent_dm(5, 1.25)
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 Matrix{ComplexF64}:
 0.209807+0.0im  0.261411+0.0im  …   0.155726+0.0im   0.133908+0.0im
 0.261411+0.0im  0.325707+0.0im      0.194028+0.0im   0.166843+0.0im
 0.235097+0.0im  0.292921+0.0im      0.174497+0.0im   0.150049+0.0im
 0.155726+0.0im  0.194028+0.0im      0.115585+0.0im  0.0993908+0.0im
 0.133908+0.0im  0.166843+0.0im     0.0993908+0.0im  0.0854655+0.0im
thermal_dm(5, 1.25)
Quantum Object:   type=Operator   dims=[5]   size=(5, 5)   ishermitian=true
5×5 Matrix{Float64}:
 0.46928  0.0       0.0       0.0        0.0
 0.0      0.260711  0.0       0.0        0.0
 0.0      0.0       0.144839  0.0        0.0
 0.0      0.0       0.0       0.0804663  0.0
 0.0      0.0       0.0       0.0        0.0447035

QuantumToolbox also provides a set of distance metrics for determining how close two density matrix distributions are to each other. Included are the fidelity, and trace distance (tracedist).

x = coherent_dm(5, 1.25)

y = coherent_dm(5, 1.25im)

z = thermal_dm(5, 0.125)

fidelity(x, y)
0.2125206772965313

Note that the definition of fidelity here is from Nielsen & Chuang, "Quantum Computation and Quantum Information". It is the square root of the fidelity defined in R. Jozsa, Journal of Modern Optics, 41:12, 2315 (1994). We also know that for two pure states, the trace distance ($T$) and the fidelity ($F$) are related by $T = \sqrt{1-F^2}$:

tracedist(x, y) ≈ sqrt(1 - (fidelity(x, y))^2)
true

For a pure state and a mixed state, $1 - F \leq T$ which can also be verified:

1 - fidelity(x, z) < tracedist(x, z)
true

Two-level systems (Qubits)

Having spent a fair amount of time on basis states that represent harmonic oscillator states, we now move on to qubit, or two-level quantum systems (for example a spin-$1/2$). To create a state vector corresponding to a qubit system, we use the same basis, or fock, function with only two levels:

spin = basis(2, 0)
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
 1.0 + 0.0im
 0.0 + 0.0im

Now at this point one may ask how this state is different than that of a harmonic oscillator in the vacuum state truncated to two energy levels?

vac = basis(2, 0)
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
 1.0 + 0.0im
 0.0 + 0.0im

At this stage, there is no difference. This should not be surprising as we called the exact same function twice. The difference between the two comes from the action of the spin operators sigmax, sigmay, sigmaz, sigmap, and sigmam on these two-level states. For example, if vac corresponds to the vacuum state of a harmonic oscillator, then, as we have already seen, we can use the raising operator (create) to get the $|1\rangle$ state:

create(2) * vac
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
 0.0 + 0.0im
 1.0 + 0.0im

For a spin system, the operator analogous to the raising operator is the $\hat{\sigma}_+$ operator sigmap. Applying on the spin state gives:

sigmap() * spin
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
 0.0 + 0.0im
 0.0 + 0.0im

Now we see the difference! The sigmap operator acting on the spin state returns the zero vector. Why is this? To see what happened, let us use the $\hat{\sigma}_z$ (sigmaz) operator:

sigmaz()
Quantum Object:   type=Operator   dims=[2]   size=(2, 2)   ishermitian=true
2×2 SparseMatrixCSC{ComplexF64, Int64} with 2 stored entries:
 1.0+0.0im       ⋅    
     ⋅      -1.0+0.0im
sigmaz() * spin
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
 1.0 + 0.0im
 0.0 + 0.0im
spin2 = basis(2, 1)
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
 0.0 + 0.0im
 1.0 + 0.0im
sigmaz() * spin2
Quantum Object:   type=Ket   dims=[2]   size=(2,)
2-element Vector{ComplexF64}:
  0.0 + 0.0im
 -1.0 + 0.0im

The answer is now apparent. Since the QuantumToolbox sigmaz function uses the standard $Z$-basis representation of the $\hat{\sigma}_z$ spin operator, the spin state corresponds to the $|\uparrow\rangle$ state of a two-level spin system while spin2 gives the $|\downarrow\rangle$ state. Therefore, in our previous example sigmap() * spin, we raised the qubit state out of the truncated two-level Hilbert space resulting in the zero state.

While at first glance this convention might seem somewhat odd, it is in fact quite handy. For one, the spin operators remain in the conventional form. Second, this corresponds nicely with the quantum information definitions of qubit states, where the excited $|\uparrow\rangle$ state is label as $|0\rangle$, and the $|\downarrow\rangle$ state by $|1\rangle$.

If one wants to create spin operators for higher spin systems, then the jmat function comes in handy.

Expectation values

Some of the most important information about quantum systems comes from calculating the expectation value of operators, both Hermitian and non-Hermitian, as the state or density matrix of the system varies in time. Therefore, in this section we demonstrate the use of the expect function. To begin:

vac = basis(5, 0)

one = basis(5, 1)

c = create(5)

N = num(5)

coh = coherent_dm(5, 1.0im)

cat = normalize(basis(5, 4) + 1.0im * basis(5, 3))

println(expect(N, vac) ≈ 0)
println(expect(N, one) ≈ 1)
println(expect(N, coh) ≈ 0.9970555745806597)
println(expect(c, cat) ≈ 1im)
true
true
true
true

The expect function also accepts lists or arrays of state vectors or density matrices for the second input:

states = [normalize(c^k * vac) for k in 0:4]

expect(N, states)
5-element Vector{ComplexF64}:
 0.0 + 0.0im
 1.0 + 0.0im
 2.0 + 0.0im
 3.0 + 0.0im
 4.0 + 0.0im
cat_list = [normalize(basis(5, 4) + x * basis(5, 3)) for x in [0, 1.0im, -1.0, -1.0im]]

expect(c, cat_list)
4-element Vector{ComplexF64}:
                 0.0 + 0.0im
                 0.0 + 0.9999999999999998im
 -0.9999999999999998 + 0.0im
                 0.0 - 0.9999999999999998im

Notice how in this last example, all of the return values are complex numbers. This is because the expect function looks to see whether the operator is Hermitian or not. If the operator is Hermitian, then the output will always be real. In the case of non-Hermitian operators, the return values may be complex. Therefore, the expect function will return an array of complex values for non-Hermitian operators when the input is a list/array of states or density matrices.

Of course, the expect function works for spin states and operators:

up = basis(2, 0)

dn = basis(2, 1)

println(expect(sigmaz(), up) ≈ 1)
println(expect(sigmaz(), dn) ≈ -1)
true
true

as well as the composite objects discussed in the next section Tensor Products and Partial Traces:

spin1 = basis(2, 0)

spin2 = basis(2, 1)

two_spins = tensor(spin1, spin2)

sz1 = tensor(sigmaz(), qeye(2))

sz2 = tensor(qeye(2), sigmaz())

println(expect(sz1, two_spins) ≈ 1)
println(expect(sz2, two_spins) ≈ -1)
true
true

Superoperators and Vectorized Operators

In addition to state vectors and density operators, QuantumToolbox allows for representing maps that act linearly on density operators using the Liouville supermatrix formalisms.

This support is based on the correspondence between linear operators acting on a Hilbert space, and vectors in two copies of that Hilbert space (which is also called the Fock-Liouville space),

\[\textrm{vec} : \mathcal{L}(\mathcal{H}) \rightarrow \mathcal{H}\otimes\mathcal{H}.\]

Therefore, a given density matrix $\hat{\rho}$ can then be vectorized, denoted as

\[|\hat{\rho}\rangle\rangle = \textrm{vec}(\hat{\rho}).\]

QuantumToolbox uses the column-stacking convention for the isomorphism between $\mathcal{L}(\mathcal{H})$ and $\mathcal{H}\otimes\mathcal{H}$. This isomorphism is implemented by the functions mat2vec and vec2mat:

rho = Qobj([1 2; 3 4])
Quantum Object:   type=Operator   dims=[2]   size=(2, 2)   ishermitian=false
2×2 Matrix{Int64}:
 1  2
 3  4
vec_rho = mat2vec(rho)
Quantum Object:   type=OperatorKet   dims=[2]   size=(4,)
4-element Vector{Int64}:
 1
 3
 2
 4
rho2 = vec2mat(vec_rho)
Quantum Object:   type=Operator   dims=[2]   size=(2, 2)   ishermitian=false
2×2 Matrix{Int64}:
 1  2
 3  4

The QuantumObject.type attribute indicates whether a quantum object is a vector corresponding to an OperatorKet, or its Hermitian conjugate OperatorBra. One can also use isoper, isoperket, and isoperbra to check the type:

println(isoper(vec_rho))
println(isoperket(vec_rho))
println(isoperbra(vec_rho))
println(isoper(vec_rho'))
println(isoperket(vec_rho'))
println(isoperbra(vec_rho'))
false
true
false
false
false
true

Because Julia is a column-oriented languages (like Fortran and MATLAB), in QuantumToolbox, we define the spre (left), spost (right), and sprepost (left-and-right) multiplication superoperators as follows:

\[\begin{align} \hat{A}\hat{\rho}~~~ &\rightarrow \textrm{spre}(\hat{A}) * \textrm{vec}(\hat{\rho}) = \hat{\mathbb{1}}\otimes \hat{A} ~ |\hat{\rho}\rangle\rangle,\notag\\ \hat{\rho} \hat{B} &\rightarrow \textrm{spost}(\hat{B}) * \textrm{vec}(\hat{\rho}) = \hat{B}^T\otimes \hat{\mathbb{1}} ~ |\hat{\rho}\rangle\rangle,\notag\\ \hat{A} \hat{\rho} \hat{B} &\rightarrow \textrm{sprepost}(\hat{A},\hat{B}) * \textrm{vec}(\hat{\rho}) = \hat{B}^T\otimes \hat{A} ~ |\hat{\rho}\rangle\rangle,\notag \end{align}\]

where $\hat{\mathbb{1}}$ represents the identity operator with Hilbert space dimension equal to $\hat{\rho}$.

A = Qobj([1 2; 3 4])
S_A = spre(A)
Quantum Object:   type=SuperOperator   dims=[2]   size=(4, 4)
4×4 SparseMatrixCSC{Int64, Int64} with 8 stored entries:
 1  2  ⋅  ⋅
 3  4  ⋅  ⋅
 ⋅  ⋅  1  2
 ⋅  ⋅  3  4
B = Qobj([5 6; 7 8])
S_B = spost(B)
Quantum Object:   type=SuperOperator   dims=[2]   size=(4, 4)
4×4 SparseMatrixCSC{Int64, Int64} with 8 stored entries:
 5  ⋅  7  ⋅
 ⋅  5  ⋅  7
 6  ⋅  8  ⋅
 ⋅  6  ⋅  8
S_AB = sprepost(A, B)
Quantum Object:   type=SuperOperator   dims=[2]   size=(4, 4)
4×4 SparseMatrixCSC{Int64, Int64} with 16 stored entries:
  5  10   7  14
 15  20  21  28
  6  12   8  16
 18  24  24  32
S_AB ≈ S_A * S_B ≈ S_B * S_A
true

One can also use issuper to check the type:

println(isoper(S_AB))
println(issuper(S_AB))
false
true

With the above definitions, the following equalities hold in Julia:

\[\textrm{vec}(\hat{A} \hat{\rho} \hat{B}) = \textrm{spre}(\hat{A}) * \textrm{spre}(\hat{B}) * \textrm{vec}(\hat{\rho}) = \textrm{sprepost}(\hat{A},\hat{B}) * \textrm{vec}(\hat{\rho}) ~~\forall~~\hat{A}, \hat{B}, \hat{\rho}\]

N  = 10
A = Qobj(rand(ComplexF64, N, N))
B = Qobj(rand(ComplexF64, N, N))
ρ = rand_dm(N) # random density matrix
mat2vec(A * ρ * B) ≈ spre(A) * spost(B) * mat2vec(ρ) ≈ sprepost(A, B) * mat2vec(ρ)
true

In addition, dynamical generators on this extended space, often called Liouvillian superoperators, can be created using the liouvillian function. Each of these takes a Hamiltonian along with a list of collapse operators, and returns a type=SuperOperator object that can be exponentiated to find the superoperator for that evolution.

H = 10 * sigmaz()

c = destroy(2)

L = liouvillian(H, [c])
Quantum Object:   type=SuperOperator   dims=[2]   size=(4, 4)
4×4 SparseMatrixCSC{ComplexF64, Int64} with 4 stored entries:
     ⋅           ⋅            ⋅        1.0+0.0im
     ⋅      -0.5+20.0im       ⋅            ⋅    
     ⋅           ⋅       -0.5-20.0im       ⋅    
     ⋅           ⋅            ⋅       -1.0+0.0im
t = 0.8
exp(L * t)
Quantum Object:   type=SuperOperator   dims=[2]   size=(4, 4)
4×4 SparseMatrixCSC{ComplexF64, Int64} with 5 stored entries:
 1.0+0.0im            ⋅                     ⋅           0.550671+0.0im
     ⋅      -0.641938-0.192987im            ⋅                    ⋅    
     ⋅                ⋅           -0.641938+0.192987im           ⋅    
     ⋅                ⋅                     ⋅           0.449329+0.0im

See the section Time Evolution and Quantum System Dynamics for more details.