In [38]:
import numpy as np
from qutip import *

Introduction

In the previous guide section Basic Operations on Quantum Objects, we saw how to create states and operators, using the functions built into QuTiP. 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 on the tutorials web page.

State Vectors

Here we begin by creating a Fock ground state vector $\left|0\right>$ with in a Hilbert space with 5 number states, from 0 to 4 using the basis function:


In [3]:
vac = basis(5, 0)
vac


Out[3]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}1.0\\0.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

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


In [4]:
a = destroy(5)
a


Out[4]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = False\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 1.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 1.414 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 1.732 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 2.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\end{array}\right)\end{equation*}

Now lets apply the destruction operator to our vacuum state vac,


In [5]:
a * vac


Out[5]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

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, the raising operator $\hat{a}^\dagger$:


In [6]:
a.dag() * vac


Out[6]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\1.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

The raising operator has in indeed raised the state vec from the vacuum to the $\left| 1\right>$ state. Instead of using the dagger Qobj.dag() method to raise the state, we could have also used the built in create function to make a raising operator:


In [7]:
c = create(5)
c * vac


Out[7]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\1.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

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


In [8]:
c * c * vac


Out[8]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\1.414\\0.0\\0.0\\\end{array}\right)\end{equation*}

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


In [9]:
c ** 2 * vac


Out[9]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\1.414\\0.0\\0.0\\\end{array}\right)\end{equation*}

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


In [10]:
c * a * vac


Out[10]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

or on the $\left| 1\right>$ state:


In [11]:
c * a * (c * vac)


Out[11]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\1.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

or the $\left| 2\right>$ state:


In [12]:
c * a * (c**2 * vac)


Out[12]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\2.828\\0.0\\0.0\\\end{array}\right)\end{equation*}

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 $c\left| n\right> = \sqrt{n+1}\left| n+1\right>$. Therefore, we should normalize our vector first:


In [13]:
c * a * (c**2 * vac).unit()


Out[13]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\2.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

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:


In [15]:
ket = basis(5, 2)
ket


Out[15]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\1.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

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


In [17]:
n = num(5)
n


Out[17]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 1.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 2.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 3.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 4.0\\\end{array}\right)\end{equation*}

Therefore, instead of c * a * (c ** 2 * vac).unit() we have:


In [18]:
n * ket


Out[18]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\2.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

We can also create superpositions of states:


In [21]:
ket = (basis(5, 0) + basis(5, 1)).unit()
ket


Out[21]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.707\\0.707\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

where we have used the Qobj.unit method to again normalize the state. Operating with the number function again:


In [22]:
n * ket


Out[22]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.707\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

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


In [23]:
vac = basis(5, 0)
d = displace(5, 1j)
s = squeeze(5, 0.25 + 0.25j)
d * vac


Out[23]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.607\\0.606j\\-0.430\\-0.241j\\0.146\\\end{array}\right)\end{equation*}

In [24]:
d * s * vac


Out[24]:
Quantum object: dims = [[5], [1]], shape = [5, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}(0.659+0.081j)\\(0.108+0.516j)\\(-0.376-0.013j)\\(-0.027-0.238j)\\(0.264+0.115j)\\\end{array}\right)\end{equation*}

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 QuTiP is to explore the dynamics of open quantum systems, where the most general state of a system is not 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 $\left|\psi\right>\left<\psi\right|$ of a ket vector:


In [26]:
ket = basis(5, 2)
ket * ket.dag()


Out[26]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 1.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\end{array}\right)\end{equation*}

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


In [27]:
fock_dm(5, 2)


Out[27]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 1.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\end{array}\right)\end{equation*}

In [28]:
ket2dm(ket)


Out[28]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 1.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\\end{array}\right)\end{equation*}

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


In [29]:
0.5 * ket2dm(basis(5, 4)) + 0.5 * ket2dm(basis(5, 2))


Out[29]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.500 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.500\\\end{array}\right)\end{equation*}

or use 0.5 * fock_dm(5, 2) + 0.5 * fock_dm(5, 4). 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.


In [30]:
coherent_dm(5, 1.25)


Out[30]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.210 & 0.261 & 0.235 & 0.156 & 0.134\\0.261 & 0.326 & 0.293 & 0.194 & 0.167\\0.235 & 0.293 & 0.263 & 0.174 & 0.150\\0.156 & 0.194 & 0.174 & 0.116 & 0.099\\0.134 & 0.167 & 0.150 & 0.099 & 0.085\\\end{array}\right)\end{equation*}

In [31]:
thermal_dm(5, 1.25)


Out[31]:
Quantum object: dims = [[5], [5]], shape = [5, 5], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.469 & 0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.261 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 0.145 & 0.0 & 0.0\\0.0 & 0.0 & 0.0 & 0.080 & 0.0\\0.0 & 0.0 & 0.0 & 0.0 & 0.045\\\end{array}\right)\end{equation*}

QuTiP also provides a set of distance metrics for determining how close two density matrix distributions are to each other. Included are the trace distance tracedist, fidelity fidelity, Hilbert-Schmidt distance hilbert_dist, Bures distance bures_dist, and Bures angle bures_angle.


In [33]:
x = coherent_dm(5, 1.25)
y = coherent_dm(5, 1.25j)  # <-- note the 'j'
z = thermal_dm(5, 0.125)

In [34]:
fidelity(x, x)


Out[34]:
1.0000000208397526

In [35]:
tracedist(y, y)


Out[35]:
0.0

We also know that for two pure states, the trace distance (T) and the fidelity (F) are related by $T = \sqrt{1 - F^{2}}$.


In [40]:
print(tracedist(y, x), np.sqrt(1 - fidelity(y, x) ** 2))


0.9771565895267291 0.977156570135

For a pure state and a mixed state, $1 - F^{2} \le T$ which can also be verified:


In [41]:
print(1 - fidelity(x, z) ** 2, tracedist(x, z))


0.7782890497791632 0.8559028328862591

Qubit (Two-level) Systems

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:


In [42]:
spin = basis(2, 0)

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?


In [43]:
vac = basis(2, 0)

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 to get the $\left|1\right>$ state:


In [44]:
c = create(2)
c * vac


Out[44]:
Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\1.0\\\end{array}\right)\end{equation*}

For a spin system, the operator analogous to the raising operator is the sigma-plus operator sigmap. Operating on the spin state gives:


In [45]:
sigmap() * spin


Out[45]:
Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\0.0\\\end{array}\right)\end{equation*}

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 sigmaz operator:


In [46]:
sigmaz()


Out[46]:
Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}1.0 & 0.0\\0.0 & -1.0\\\end{array}\right)\end{equation*}

In [47]:
sigmaz() * spin


Out[47]:
Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}1.0\\0.0\\\end{array}\right)\end{equation*}

In [48]:
spin2 = basis(2, 1)
sigmaz() * spin2


Out[48]:
Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\-1.0\\\end{array}\right)\end{equation*}

The answer is now apparent. Since the QuTiP sigmaz function uses the standard z-basis representation of the sigma-z spin operator, the spin state corresponds to the $\left|\uparrow\right>$ state of a two-level spin system while spin2 gives the $\left|\downarrow\right>$ 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, when the spin system is in the $\left|\uparrow\right>$ state:


In [49]:
sigmaz() * spin


Out[49]:
Quantum object: dims = [[2], [1]], shape = [2, 1], type = ket\begin{equation*}\left(\begin{array}{*{11}c}1.0\\0.0\\\end{array}\right)\end{equation*}

the non-zero component is the zeroth-element of the underlying matrix (remember that python uses c-indexing, and matrices start with the zeroth element). The $\left|\downarrow\right>$ state therefore has a non-zero entry in the first index position. This corresponds nicely with the quantum information definitions of qubit states, where the excited $\left|\uparrow\right>$ state is label as $\left|0\right>$, and the $\left|\downarrow\right>$ state by $\left|1\right>$.

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:


In [50]:
vac = basis(5, 0)
one = basis(5, 1)
c = create(5)
N = num(5)

expect(N, vac)


Out[50]:
0.0

In [51]:
expect(N, one)


Out[51]:
1.0

In [52]:
coh = coherent_dm(5, 1.0j)
expect(N, coh)


Out[52]:
0.9970555745806599

In [53]:
cat = (basis(5, 4) + 1.0j * basis(5, 3)).unit()
expect(c, cat)


Out[53]:
0.9999999999999998j

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, than 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 as well:


In [54]:
up = basis(2, 0)
down = basis(2, 1)

expect(sigmaz(), up)


Out[54]:
1.0

In [55]:
expect(sigmaz(), down)


Out[55]:
-1.0

as well as the composite objects discussed in the next section (Tensor Products & Partial Traces):


In [56]:
spin1 = basis(2, 0)
spin2 = basis(2, 1)
two_spins = tensor(spin1, spin2)
sz1 = tensor(sigmaz(), qeye(2))
sz2 = tensor(qeye(2), sigmaz())

expect(sz1, two_spins)


Out[56]:
1.0

In [57]:
expect(sz2, two_spins)


Out[57]:
-1.0

Super Operators and Vectorized Operators

In addition to state vectors and density operators, QuTiP allows for representing maps that act linearly on density operators using the Kraus, Liouville supermatrix and Choi matrix formalisms. This support is based on the correspondance between linear operators acting on a Hilbert space, and vectors in two copies of that Hilbert space, :$\mathrm{vec} : \mathcal{L}(\mathcal{H}) \to \mathcal{H} \otimes \mathcal{H}$.

This isomorphism is implemented in QuTiP by the operator_to_vector and vector_to_operator functions:


In [59]:
rho = fock_dm(2,0)
vec_rho = operator_to_vector(rho)
vec_rho


Out[59]:
Quantum object: dims = [[[2], [2]], [1]], shape = [4, 1], type = operator-ket\begin{equation*}\left(\begin{array}{*{11}c}1.0\\0.0\\0.0\\0.0\\\end{array}\right)\end{equation*}

In [60]:
rho2 = vector_to_operator(vec_rho)
(rho - rho2).norm()


Out[60]:
0.0

The Qobj.type attribute indicates whether a quantum object is a vector corresponding to an operator (operator-ket), or its Hermitian conjugate (operator-bra).

Note that QuTiP uses the column-stacking convention for the isomorphism between $\mathcal{L}(\mathcal{H})$ and $\mathcal{H} \otimes \mathcal{H}$:


In [61]:
A = Qobj(np.arange(4).reshape((2, 2)))
A


Out[61]:
Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 1.0\\2.0 & 3.0\\\end{array}\right)\end{equation*}

In [62]:
operator_to_vector(A)


Out[62]:
Quantum object: dims = [[[2], [2]], [1]], shape = [4, 1], type = operator-ket\begin{equation*}\left(\begin{array}{*{11}c}0.0\\2.0\\1.0\\3.0\\\end{array}\right)\end{equation*}

Since $\mathcal{H} \otimes \mathcal{H}$ is a vector space, linear maps on this space can be represented as matrices, often called supermatrices. Using the Qobj, the spre and spost functions, supermatrices corresponding to left- and right-multiplication respectively can be quickly constructed.


In [64]:
X = sigmax()
S = spre(X) * spost(X.dag()) # Represents conjugation by X.

Note that this is done automatically by the to_super function when given type='oper' input.


In [65]:
S2 = to_super(X)
(S - S2).norm()


Out[65]:
0.0

Quantum objects representing superoperators are denoted by type='super':


In [66]:
S


Out[66]:
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = [4, 4], type = super, isherm = True\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 1.0\\0.0 & 0.0 & 1.0 & 0.0\\0.0 & 1.0 & 0.0 & 0.0\\1.0 & 0.0 & 0.0 & 0.0\\\end{array}\right)\end{equation*}

Information about superoperators, such as whether they represent completely positive maps, is exposed through the iscp, Qobj.istp and Qobj.iscptp attributes:


In [67]:
S.iscp, S.istp, S.iscptp


Out[67]:
(True, True, 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 Hamilonian along with a list of collapse operators, and returns a type="super" object that can be exponentiated to find the superoperator for that evolution.


In [68]:
H = 10 * sigmaz()
c1 = destroy(2)
L = liouvillian(H, [c1])
L


Out[68]:
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = [4, 4], type = super, isherm = False\begin{equation*}\left(\begin{array}{*{11}c}0.0 & 0.0 & 0.0 & 1.0\\0.0 & (-0.500+20.0j) & 0.0 & 0.0\\0.0 & 0.0 & (-0.500-20.0j) & 0.0\\0.0 & 0.0 & 0.0 & -1.0\\\end{array}\right)\end{equation*}

In [74]:
S = (12 * L).expm()
S


Out[74]:
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = [4, 4], type = super, isherm = False\begin{equation*}\left(\begin{array}{*{11}c}1.0 & 0.0 & 0.0 & 1.000\\0.0 & (8.075\times10^{-04}+0.002j) & 0.0 & 0.0\\0.0 & 0.0 & (8.075\times10^{-04}-0.002j) & 0.0\\0.0 & 0.0 & 0.0 & 6.144\times10^{-06}\\\end{array}\right)\end{equation*}

Once a superoperator has been obtained, it can be converted between the supermatrix, Kraus and Choi formalisms by using the superop_reps.to_super, superop_reps.to_kraus and superop_reps.to_choi functions. The Qobj.superrep attribute keeps track of what reprsentation is a Qobj is currently using.


In [76]:
J = to_choi(S)
J


Out[76]:
Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = [4, 4], type = super, isherm = True, superrep = choi\begin{equation*}\left(\begin{array}{*{11}c}1.0 & 0.0 & 0.0 & (8.075\times10^{-04}-0.002j)\\0.0 & 0.0 & 0.0 & 0.0\\0.0 & 0.0 & 1.000 & 0.0\\(8.075\times10^{-04}+0.002j) & 0.0 & 0.0 & 6.144\times10^{-06}\\\end{array}\right)\end{equation*}

In [77]:
K = to_kraus(J)
K


Out[77]:
[Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False
 Qobj data =
 [[  1.00000000e+00 +1.34376978e-22j   0.00000000e+00 +0.00000000e+00j]
  [  0.00000000e+00 +0.00000000e+00j   8.07531120e-04 +2.34352424e-03j]],
 Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False
 Qobj data =
 [[ -1.11923759e-13 +6.02807402e-15j   0.00000000e+00 +0.00000000e+00j]
  [  0.00000000e+00 +0.00000000e+00j   1.70093171e-11 +4.18976706e-11j]],
 Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = True
 Qobj data =
 [[ 0.  0.]
  [ 0.  0.]],
 Quantum object: dims = [[2], [2]], shape = [2, 2], type = oper, isherm = False
 Qobj data =
 [[ 0.          0.99999693]
  [ 0.          0.        ]]]

In [1]:
from IPython.core.display import HTML
def css_styling():
    styles = open("../styles/guide.css", "r").read()
    return HTML(styles)
css_styling()


Out[1]:

In [ ]: