OpenKetManual.pdf

23
OpenKet Brief Manual ıctor S C Canela 1

Transcript of OpenKetManual.pdf

Page 1: OpenKetManual.pdf

OpenKet Brief Manual

Vıctor S C Canela

1

Page 2: OpenKetManual.pdf

Contents

1 Introduction 3

2 Kets and Bras 42.1 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.2 Basic Algebra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

3 Operators 63.1 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63.2 Creation / Annihilation . . . . . . . . . . . . . . . . . . . . . . . . 63.3 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3.3.1 Eigenvalue Syntaxis . . . . . . . . . . . . . . . . . . . . . . . 83.3.2 Quantum Gates . . . . . . . . . . . . . . . . . . . . . . . . . 9

4 Built-in Functions 104.1 Hermitian Conjugate: Adj . . . . . . . . . . . . . . . . . . . . . . . 104.2 Trace and Partial Trace: Trace, TraceOut . . . . . . . . . . . . . . 114.3 Matrix Representation: Qmatrix . . . . . . . . . . . . . . . . . . . . 12

5 Time Evolution Examples 135.1 The Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135.2 The Translator QeqN . . . . . . . . . . . . . . . . . . . . . . . . . . 135.3 Two Level Semiclassical Atoms . . . . . . . . . . . . . . . . . . . . 155.4 Two Level Atom Inside a Cavity . . . . . . . . . . . . . . . . . . . . 19

5.4.1 Observables and SubsSol . . . . . . . . . . . . . . . . . . . 21

2

Page 3: OpenKetManual.pdf

1 Introduction

This is an introductory manual to the Python library OpenKet. This library isa tool for manipulating objects from quantum mechanics, such as vectors in theform of kets, operators, etc.

It is free software under GNU-GPL v3 license, and uses other free softwarelibraries, such as Sympy, Numpy and Scipy.

The project started at the end of 2009 with Vicente Rodrıguez under the super-vision of Pablo Barberis-Blostein1, as part of the requirements for his graduation. Istarted working with Pablo one year later, and continued programming OpenKet.

We want OpenKet to be as transparent as possible. This means making theinput process close to writing it on paper. Dirac’s notation is used to representvectors as kets (having the same visual representation |x〉) for example.

The basic structure of the library is the definition of object classes with specificrules regarding the basic algebra they should follow. Then there are functions,intended for a specific purpose, for example generate time evolution equationsfrom a Hamiltonian.

OpenKet can be downloadedfrom http://code.google.com/p/openket/. In order to run OpenKet it is needed:Sympy, Pylab and Scipy2).

OpenKet is in its early stages and has a lot of bugs. Consequently is continu-ously improving.

Before typing the examples presented in the following sections, it is necessaryto import OpenKet3

>>> from openket import *

1at IIMAS, UNAM2Working in iPython with Pylab is the ideal environment for OpenKet3if working in iPython type run -i openket.py

3

Page 4: OpenKetManual.pdf

2 Kets and Bras

2.1 Properties

In quantum mechanics, states are represented as vectors of a Hilbert space. UsingDirac’s notation, these vectors are represented as |x〉 and are called kets. OpenKetunderstands kets as an object (class) with specific properties or methods. We candefine a ket as follows,

>>> x = var("x")

>>> v = Ket(x); v

|x>

In this example x and v are two different objects. x is a Sympy variable and v

is the object Ket. The argument given for this object is the vector tag. This tagmust be either a number class or a Sympy variable, as in the example.

There is another argument (optional) which further specifies the ket. This isthe operator tag. The ket is then the eigenvector of the operator (whose name is’operator tag’), with eigenvalue equal to the ’vector tag’.

>>> u = Ket(1,"Op1")

>>> w = Ket(1, "Op2")

Although u and w have the same vector tag, they are not treated as the sameobject. For this kind of tags, strings as well as Sympy variables may be used.

The object Ket has 2 methods: .op and .eig used to extract all the infor-mation from it. While .eig returns the vector tag, .op returns the operatortag as follows,

>>> u.eig

1

>>> u.op

’Op1’

Vectors corresponding to the dual space are represented by 〈y| and are calledbras. There is also an object Bra in OpenKet, with exactly the same characteristicsas de Ket object,

4

Page 5: OpenKetManual.pdf

>>> y = var("y")

>>> z = Bra(y); z; z.eig; z.op

<y|

y

’default’

Note that when no operator tag is specified .op returns ’default’.

2.2 Basic Algebra

Basic operations may be performed on the Kets and Bras objects, such as:

>>> w = Ket(0); x = Ket(1); y = Bra(0); z = Bra(1)

>>> (2.1 + 2)*w + (4.3 + I)*x

4.1|0> + (4.3 + I)|1>

>>> y - 1.7*y + 0.5*z + I*z

- 0.7<0| + (0.5 + I)<1|

Where the I ≡ i =√−1. Multiplication is also supported for example, if we

multiply a bra by a ket we get a number,

>>> y*w

1

OpenKet assumes w and y are normalized. However, if we were to define statevectors with norm not equal to unity, the results would be different,

>>> psi = 2.1*Ket(0) + I*Ket(1); phi = I*Bra(0) - 4.5*Bra(1)

>>> phi*psi

-2.4*I

Multiplying a ket by a bra results in an outer product,

>>> w*y; psi*phi

|0><0|

2.1*I|0><0| - 9.45|0><1| - |1><0| - 4.5*I|1><1|

5

Page 6: OpenKetManual.pdf

Tensor products can be simulated having two different operator tags. Forexample,

>>> f = Ket(0, "Op1"); g = Ket(0, "Op2"); h = Ket(1, "Op3")

>>> l = Bra(1, "Op1"); m = Bra(1, "Op2"); n = Bra(1, "Op3")

>>> f*g*h

|0 Op1>|0 Op2>|1 Op3>

>>> l*m*n

<1 Op3|<1 Op2|<1 Op1|

3 Operators

3.1 Properties

An operator in OpenKet is an object (class) defined as follows,

>>> A = Operator("A"); A

A

The symbol on the left of the equality does not need to be the same as theargument of Operator, this is just how you are calling it, whereas the argumentis how OpenKet tags it.

This can be made clear with the only method of Operator, .op. This has thesame effect as with kets and bras, it returns the tag of the operator for example,

>>> name = Operator("up"); name.op

’up’

It is convenient to have the same tags if no confusion is produced. Howeverthe option is always available to name objects the way we want.

3.2 Creation / Annihilation

In many physical systems it is convenient to make a canonical transformation fromthe position - momentum operators to the creation - annihilation operators. The

6

Page 7: OpenKetManual.pdf

obvious example is the quantum harmonic oscillator. In terms of these particularoperators, the Hamiltonian may be written,

H = hω(a†a+

1

2

)

The number operator is defined as N = a†a so the above expression becomes,

H = hω(N +

1

2

)

The eigenvectors of N are called Fock states and therefore satisfy the equation,

a†a|n〉 = n|n〉

Where n is the energy level.Individually, the operators a† and a operate on |n〉 as follows,

a†|n〉 =√n+ 1|n+ 1〉

a|n〉 =√n|n− 1〉

OpenKet has two separate object classes for these operators. They are theCreationOperator and the AnnihilationOperator. Both have 2 arguments (op-tional). The first is an ’operator tag’ type; necessary when having tensor productsof vectors with tags that could be confused. The second is a maximum value ofthe ket, i.e. a number such that if the ket has that eigenvalue, applying a creationoperator on it results in zero. This all looks like this,

>>> a = AnnihilationOperator(); aa = CreationOperator(); n=var("n")

>>> s = Ket(n); t = Bra(n)

>>> a*s; aa*s; t*a; t*aa

n**(1/2)|-1 + n>

(1 + n)**(1/2)|1 + n>

(1 + n)**(1/2)<1 + n|

n**(1/2)<-1 + n|

More complex,

7

Page 8: OpenKetManual.pdf

>>> a1 = AnnihilationOperator("mode1")

>>> aa1 = CreationOperator("mode1", 4) # maximum value = 4

>>> a2 = AnnihilationOperator("mode2")

>>> aa2 = CreationOperator("mode2", 9) # maximum value = 9

>>> p = Ket(2, "mode1")*Ket(3, "mode2"); p

|2 mode1>|3 mode2> # bipartite system

>>> a1*p; a2*p; a1*a2*p

2**(1/2)|1 mode1>|3 mode2>

3**(1/2)|2 mode1>|2 mode2>

6**(1/2)|1 mode1>|2 mode2>

>>> aa1*a1*p; aa2*a2*p

2|2 mode1>|3 mode2> # number operator acting on ’mode1’

3|2 mode1>|3 mode2> # number operator acting on ’mode2’

It is possible to apply the operators as many times as we like. For example(maintaining names),

>>> (aa**4)*s

(1 + n)**(1/2)*(2 + n)**(1/2)*(3 + n)**(1/2)*(4 + n)**(1/2)|4 + n>

>>> (aa1**2)*p; (aa1**3)*p

2*3**(1/2)|4 mode1>|3 mode2>

0

The last zero being a result of having surpassed the eigenvalue 4 (maximumvalue) for the photon1 system.

3.3 Examples

3.3.1 Eigenvalue Syntaxis

The eigenvalue equation,

A|a〉 = a|a〉Can be computed using OpenKet. It is necessary to match the names of the

operator tags of both the Ket and the Operator,

>>> a = var("a"); A = Operator("A"); u = Ket(a, "A")

>>> A*u

a|a A>

8

Page 9: OpenKetManual.pdf

3.3.2 Quantum Gates

In quantum computing the basic unit of information is called a qubit, and isrepresented as a linear combination of two vectors: |0〉, |1〉. (computationalbasis). Manipulation of qubits is possible through quantum gates, mathematicallydescribed as operators.

Take for example the Pauli operators. They can be written in terms of exteriorproducts using the computational basis as follows,

σ0 = |0〉〈0|+ |1〉〈1|

σx = |0〉〈1|+ |1〉〈0|

σy = −i|0〉〈1|+ i|1〉〈0|

σz = |0〉〈0| − |1〉〈1|

It is possible to write the above in OpenKet 4, having the advantage that theylook visually the same,

>>> sigma0 = Ket(0)*Bra(0) + Ket(1)*Bra(1); sigma0

|0><0| + |1><1|

>>> sigmaX = Ket(0)*Bra(1) + Ket(1)*Bra(0); sigmaX

|0><1| + |1><0|

>>> sigmaY = -I*Ket(0)*Bra(1) + I*Ket(1)*Bra(0); sigmaY

- I|0><1| + I|1><0|

>>> sigmaZ = Ket(0)*Bra(0) - Ket(1)*Bra(1); sigmaZ

|0><0| - |1><1|

They act on vectors,

>>> v = 3*I*Ket(0) + 1.23*Ket(1); v

3*I|0> + 1.23|1>

>>> sigmaX*v

1.23|0> + 3*I|1>

>>> sigmaY*v

4This is not necessary (though it helps a lot to explain how to build operators). The Paulioperators are defined inside OpenKet, as X(tag), Y(tag), Z(tag), (σx, σy, σz, respectively).Where the tag is an optional argument used to specify the operator tag, if handling more thanone qubit.

9

Page 10: OpenKetManual.pdf

- 1.23*I|0> - 3|1>

>>> sigmaZ*v

3*I|0> - 1.23|1>

We can prove that [σx, σy] = 2iσz,

>>> left = sigmaX*sigmaY; left

I|0><0| - I|1><1

>>> right = sigmaY*sigmaX; right

- I|0><0| + I|1><1|

>>> left - right

2*I|0><0| - 2*I|1><1|

And virtually any gate that we can represent as a sum of exterior products canbe written fairly easy using OpenKet.

4 Built-in Functions

This functions are subroutines within OpenKet, so once you run the library all ofthem will be in your workspace.

4.1 Hermitian Conjugate: Adj

Gives the h.c. of the argument you input, be it a number (complex conjugate), aKet(Bra) (Bra(Ket)), an Operator (AdjointOperator) or a combination of them.For example,

>>> Adj(I)

-I

>>> Adj(Ket(0)); Adj(Bra(1))

<0|

|1>

>>> Adj(I*Ket(0) - 4*Ket(1))

- I<0| - 4<1|

This is particularly useful for computing the density operator of a state, or itsnorm,

10

Page 11: OpenKetManual.pdf

>>> w = I*Ket(0) - 4*Ket(1); w

I|0> - 4|1>

>>> norm = Adj(w)*w; norm

17

>>> rho = w*Adj(w); rho/norm

1/17|0><0| - 4*I/17|0><1| + 4*I/17|1><0| + 16/17|1><1|

4.2 Trace and Partial Trace: Trace, TraceOut

OpenKet allows us to trace or partially trace states expressed as a sum of exteriorproducts. Let’s first look into TraceOut. This has 2 arguments: the first one isthe total expression and the second one is the operator tag of the space you willbe tracing out.

Let’s first construct a vector, (2 qubit system, part A and part B) and itsdensity operator,

>>> P = Ket(0, "A")*Ket(1, "B") - Ket(1, "A")*Ket(0, "B"); P

|0 A>|1 B> - |1 A>|0 B>

>>> R = P*Adj(P); R

|0 A>|1 B><1 B|<0 A| - |0 A>|1 B><0 B|<1 A| -

|1 A>|0 B><1 B|<0 A| + |1 A>|0 B><0 B|<1 A|

So tracing out looks like this,

>>> RB = TraceOut(R, "A"); RB

|0 B><0 B| + |1 B><1 B|

>>> RA = TraceOut(R, "B"); RA

|0 A><0 A| + |1 A><1 A|

The total trace is done with the function Trace5 . You only need to input thetotal expression, Trace first finds all the operator tags and will then partially tracethem all out.

>>> Trace(R)

2

5A second optional argument is the base which you would like to trace with, given as a list

11

Page 12: OpenKetManual.pdf

>>> Trace(Ket(0)*Bra(1) + Ket(0)*Bra(0) + 2*I*Ket(1)*Bra(1))

1 + 2*I

One application of this is to be able to find if we are dealing with a mixed ora pure state, by computing the square of the density operator and calculating itstotal trace.

Suppose we have the pure state R1= |ψ〉〈ψ| with, |ψ〉 = (|0〉 − |1〉)/√

2 and wehave the state R2= 0.25|0〉〈0|+ 0.75|1〉〈1|. First we input this states,

>>> psi = (Ket(0) - Ket(1))/sqrt(2)

>>> R1 = psi*Adj(psi); R1

0.5|0><0| - 0.5|0><1| - 0.5|1><0| + 0.5|1><1|

>>> R2 = 0.25*Ket(0)*Bra(0) + 0.75*Ket(1)*Bra(1); R2

0.25|0><0| + 0.75|1><1|

Both of them have total traces equal to one, but if we square them and calculatetheir traces,

>>> Trace(R1*R1)

1.0

>>> Trace(R2*R2)

0.625

Thus concluding that R2 is not a pure state.

4.3 Matrix Representation: Qmatrix

We are able nonetheless to work with the matrix representation of operators. Thisis done via Qmatrix. The function has 2 arguments, the first is the expression(sum of exterior products) and the second is the base in list form. For example,recall the Pauli operators of section 3.3.2; let’s obtain the matrix representationof σx in the computational basis |0〉, |1〉,

>>> basecomp = [Ket(0), Ket(1)]

>>> Qmatrix(sigmaX, base)

[0, 1]

[1, 0]

12

Page 13: OpenKetManual.pdf

Yet if we change base, for example |+〉, |−〉, where |±〉 = (|0〉 ± |1〉) /√

2, thematrix representation changes accordingly,

>>> basepm = [(Ket(0)+Ket(1))/sqrt(2), (Ket(0)-Ket(1))/sqrt(2)]

>>> Qmatrix(sigmaX, basepm)

[1.0, 0]

[ 0, -1.0]

This is useful when calculating for example eigenvalues, making things easierwhen computing more complex properties such as Von Neumann entropy.

5 Time Evolution Examples

5.1 The Problem

One important aspect of understanding a certain system is to understand its timeevolution. In quantum mechanics there are many ways of representing how asystem evolves. In this section we will demonstrate the use of OpenKet subroutinesto solve numerically this problem by considering first the Von Neumann equation,

ρ = − ih

[H, ρ]

Numerically this is a set of coupled ODE’s (ordinary differential equations). Thething is, one has to find a way to write and name the variables to produce a scriptreadable by an ODE’s solver.

The problems presented in this manual are discrete level atoms in special sit-uations. We face this type of problems by first writing H as a sum of exteriorproducts of elements of a base; then verifying the time evolution of the matrixelements of ρ, namely 〈i|ρ|j〉, considering them as variables.

It is convenient to end up with real coefficients equations, due to the fact thatnot all ODE’s solvers work with complex numbers, and the point is to make thisas transparent as possible.

5.2 The Translator QeqN

The first thing is to write the Hamiltonian and every extra term in the right handside of the time evolution equation in terms that OpenKet understands. Thencomes in the function QeqN. Basically it translates terms of the form 〈i|ρ|j〉 to

13

Page 14: OpenKetManual.pdf

symbolic expressions (variables) which are easier to handle. Then it writes theequations into a text file as well as the dictionary relating the 〈i|ρ|j〉 terms withthe variables.

QeqN has the following arguments QeqN(density operator, time derivative, base,file name, dictionary name). The fist argument is just an object class Operator

used to designate the density operator. The second is the right hand side of thetime evolution equation. The third argument is a list, containing the elements ofthe base, expressed as kets. The fourth and fifth arguments are strings namingthe text file and the dictionary written in the file.

QeqN uses 2 other functions: Dictionary(base, density operator) 6 and Qch(expression,dictionary).

Dictionary does just that, create a dictionary (with name D) with words as〈i|ρ|j〉 objects and definitions as variables. It is worth noting that in order to have

always real coefficients, the variables must be in fact 〈i|ρ|j〉+〈j|ρ|i〉2

and 〈i|ρ|j〉−〈j|ρ|i〉2i

;because 〈i|ρ|j〉 is in general complex7. Let’s illustrate this by building the dictio-nary of the base |0〉, |1〉,

>>> base=[Ket(0), Ket(1)]; R=Operator("R")

>>> Dictionary(base, R)

’<0|R|0>’: Re0,

’<0|R|1>’: Re1 + I*Im1,

’<1|R|0>’: Re1 - I*Im1,

’<1|R|1>’: Re2

The symbols Re0, Re1, Im1, Re2 are declared as real variables.The function Qch uses the dictionary created above to substitute bracket terms,

for the symbols declared in Dictionary. For example,

>>> D = Dictionary(base, R)

>>> expr = Bra(0)*R*Ket(0)+2*I*Bra(0)*R*Ket(1)-3.4*Bra(1)*R*Ket(1); expr

<0|R|0> + 2*I<0|R|1> - 3.4<1|R|1>

>>> EXPR = Qch(expr, D); EXPR

Re0 - 3.4*Re2 + 2*I*(Re1 + I*Im1)

So QeqN first creates a dictionary, then substitutes bracket terms, and thenwrites to a file. Before writing to the file, it replaces the real variables with strings

6Not to be confused with the dictionary written in the file7Although 〈i|ρ|i〉 is always real in physical situations

14

Page 15: OpenKetManual.pdf

of the form y[n] where n is some number. The cost of introducing new notationis justified because strings are objects which consume a lot less memory thanvariables, in this case.8 The written file looks something like this,

def f(y, t):

return [-2*y[2],0,y[0] - 1.0*y[3],2*y[2]]

dict=’<1|R|0>’: y1 - I*y2, ’<1|R|1>’: y3,

’<0|R|0>’: y0, ’<0|R|1>’: y1 + I*y2

The text is the usual way to define a function in Python. The name f is alwaysused as well as its arguments.

dict is a dictionary object, having its name specified in the last argument ofQeqN. It serves only the purpose of aiding the user in determining which bracketterm is which string9.

After the return command, there is a list which contains the temporal evolu-tion of the matrix elements. The position of a certain item determines the timederivative of the matrix element corresponding to y[item]. For example, in thetext presented before, we have [-2*y[2],0,y[0] - 1.0*y[3],2*y[2]], so look-ing at the first entry,

ddty[0] = −2y[2]

Equivalent to (using the dict),

d

dt〈0|ρ|0〉 = −2

〈0|ρ|1〉 − 〈1|ρ|0〉2i

The sintaxis of the file is such that the ODE solver odeint from the packageScipy understands it. So all we need to do, is run the file from our workspace,and specify the time step and the initial condition. To illustrate this, we show twointeresting physical problems.

5.3 Two Level Semiclassical Atoms

In some physical situations when an atom interacts with an electromagnetic field,we can describe the atom using a 2 element base; let this base be |0〉, |1〉. Fur-thermore let the energy of the levels be hω0 and hω1 respectively. Now, the total

8Why then declare them first as real variables? Because we need to be able to operate andsimplify expressions, and this is done on symbolic variables, not strings. The package Sympytakes care of all the algebra, but again, in order to do so, the objects must be symbolic variables.

9Helping in the study of observables, as will be evident in the last section

15

Page 16: OpenKetManual.pdf

Hamiltonian may be divided into two parts, the Hamiltonian of the unperturbedatom H0, and the interaction part V . These can be found to have the followingform,

H0 = hω0|0〉〈0|+ hω1|1〉〈1|

V = gE(t)|0〉〈1|+ g∗E(t)|1〉〈0|

Where we have put g = e〈0|~r|1〉 · ~ε and ~E(t) = | ~E(t)|~ε ≡ E(t)~ε with |~ε| = 1. Weassume the field has the form,

E(t) = A cos(νt)

It is convenient to change to the interaction picture, and following the rotatingwave approximation (RWA) we end up with the Hamiltonian (setting Ω = gA

2hand

having the frequency of the field equal to the difference of the two level frequencies),

H = h(Ω|0〉〈1|+ Ω∗|0〉〈1|)

Following is the script which numerically solves the time evolution of the systemdescribed10,

run -i openket.py

from scipy.integrate import odeint # import Scipy’s ODE solver

R = Operator("R")

b = [Ket(0), Ket(1)]

H = Ket(0)*Bra(1) + Ket(1)*Bra(0)

Rdot = -I*Commutator(H, R)

QeqN(R, Rdot, b, "func", "dic")

run -i func

initialc = [1,0,0,0] # initial condition

t = linspace(0,10,1000) # time-step

sol = odeint(f, initialc, t)

es = sol[:,3]

plot(t, es)

The graph we obtain is the following,

10assuming Ω = 1 for simplicity

16

Page 17: OpenKetManual.pdf

0 2 4 6 8 10

t0.0

0.2

0.4

0.6

0.8

1.0

⟨ 1|ρ|1⟩

Figure 1: Time evolution for 〈1|ρ|1〉 with the initial condition ρ(t=0) = |0〉〈0|

Note that the initial condition is given as a list, where the position in the listindicates the bracket term that one wishes to give as the initial condition (the dic

tells you which is which). Also the time-step is made with the command linspace

having 3 arguments: the first two specify the interval of solution while the thirdone is the total number of points in the interval. In the script, sol is the actualsolution, and has the form,

sol = [

t=0︷ ︸︸ ︷[y[0], y[1], y[2], y[3]],

t=0.01︷ ︸︸ ︷[y[0], y[1], y[2], y[3]], . . . ,

t=10︷ ︸︸ ︷[y[0], y[1], y[2], y[3]]]

So the command es = sol[:,3] partitions the total solution sol and onlykeeps the third entry (y[3] or 〈1|ρ|1〉) in all the lists within sol. The last line isjust the plotting of the probability of finding the atom in the excited state versustime.

Suppose we don’t want to make the RWA, or treat the problem from theinteraction picture; that is, work with the total Hamiltonian11 H = H0 + V ,

H = hω0|0〉〈0|+ hω1|1〉〈1|+ 2hΩ cos(νt)|0〉〈1|+ 2hΩ∗ cos(νt)|1〉〈0|

A possible script, (notice that the structure is the same as the last script)12:

11Note that it is time-dependent explicitly12Setting Ω = 0.5, again for simplicity

17

Page 18: OpenKetManual.pdf

run -i openket.py

from scipy.integrate import odeint

from sympy import * # the whole package

# to be able to treat cos(n*t) as a variable

R = Operator("R")

b = [Ket(0), Ket(1)]

w1=10; w0=1; v=9 # define parameters (resonant condition: v = w1-w0)

t = var("t")

H = w0*Ket(0)*Bra(0) + w1*Ket(1)*Bra(1)

H = H + cos(v*t)*(Ket(0)*Bra(1) + Ket(1)*Bra(0))

Rdot = -I*Commutator(H, R)

QeqN(R, Rdot, b, "func", "dic")

run -i func

initialc = [1,0,0,0]

t = linspace(0,10,1000) # has to have the same tag as the variable t

sol = odeint(f, initialc, t)

es = sol[:,3]

plot(t, es)

We obtain,

0 2 4 6 8 10

t0.0

0.2

0.4

0.6

0.8

1.0

⟨ 1|ρ|1⟩

Figure 2: Time evolution for 〈1|ρ|1〉 with the initial condition ρ(t=0) = |0〉〈0| andwithout the RWA. Values (ω0 = 1;ω1 = 10; ν = 9)

Changing the parameters a bit,

18

Page 19: OpenKetManual.pdf

0 2 4 6 8 10

t0.0

0.2

0.4

0.6

0.8

1.0

⟨ 1|ρ|1⟩

(a) Full size

5.5 6.0 6.5 7.0

t0.00

0.02

0.04

0.06

0.08

0.10

0.12

⟨ 1|ρ|1⟩

(b) Detail

Figure 3: Time evolution for 〈1|ρ|1〉 with the initial condition ρ(t=0) = |0〉〈0| andwithout the RWA. Values (ω0 = 1;ω1 = 100; ν = 99)

5.4 Two Level Atom Inside a Cavity

The last scenario tests basically every aspect of OpenKet. Let a two level atominside a cavity interact with an electromagnetic field. There is laser pumping andlosses, and there is spontaneous emission from the atom. This can be modeled byhaving a Hamiltonian of the form,

H = g(a†σ− + aσ+

)+ ε

(a† + a

)Where σ− = |0〉〈1|;σ+ = |1〉〈0|. The Von Neumann equation has extra dissi-

pative terms,

ρ = − ih

[H, ρ

]+ kL(a) +

γ

2L(σ−)

Having L(o) = 2oρo† − ρo†o− o†oρ.To solve this we have to use a finite base (truncating the Fock states), such as,

|i〉atom|j〉photon : i = 0, 1 ; j = 0, 1, . . . , n

Deciding which n has to be carefully thought, (both because of physical issues,and computer speed issues).

Let us work with a base having this structure and with n = 4. Writing thescript is no problem, it’s just copying the Hamiltonian and time evolution equationsfrom before 13

13the base will have 10 elements |0〉at|0〉ph, |0〉at|1〉ph, . . . , |1〉at|0〉ph, |1〉at|1〉ph, . . . , |1〉at|4〉ph

19

Page 20: OpenKetManual.pdf

run -i openket.py

from scipy.integrate import odeint

a = AnnihilationOperator("photon",4) # maximum value 4

aa = CreationOperator("photon",4) # maximum value 4

splus = Ket(1,"atom")*Bra(0,"atom")

sminus = Adj(splus)

g = 0.6; epsilon = 0.1; k = 0.5; gamma = 1.5 # define parameters

H = g*(aa*sminus + a*splus) + epsilon*(a + aa) # Hamiltonian

def L(x): # Define the function L

R=Operator("R")

return 2*x*R*Adj(x) - R*Adj(x)*x - Adj(x)*x*R

R = Operator("R")

Rdot = - I*Commutator(H, R) + \k*(2*a*R*aa - R*aa*a - aa*a*R) + (gamma/2.0)*L(sminus)

l=[]; m=[]; b=[] # start the base construction

for i in range(2):

l.append(Ket(i, "atom")) # atom part

for j in range(5):

m.append(Ket(j,"photon")) # photon part

for i in l:

for j in m:

b.append(i*j) # put them together

y0 = [] # initial condition

for i in range(100):

y0.append(0) # fill it with zeroes

y0[0] = 1 # at time t=0 we have atom at base state and no photons

t = linspace(0,10,100)

QeqN(R, Rdot, b, "func", "dic")

run -i func

sol = odeint(f, y0, t)

This completely solves the problem numerically. sol has all the time evolutioninformation of ρ. The problem is, how can we manage this information in orderto say, plot the time evolution of a certain observable?

producing a 100 element density matrix. When we input the initial condition, it must be in theform of a list of 100 entries. This is not difficult at all, as may be evident from the script. Thechallenge is to obtain a certain observable.

20

Page 21: OpenKetManual.pdf

5.4.1 Observables and SubsSol

Suppose now we want to plot the mean number of photons against time. Theobservable is tr(ρa†a). To be able to plot this, we must first compute the traceand have something of the type,

∑i,j cij〈i|ρ|j〉 This can be done directly with the

function Trace using the second optional argument, (keeping the names we definedin the previous script)

>>> obs = Trace(R*aa*a, b); obs

<1 photon|<0 atom|R|0 atom>|1 photon> + <1 photon|<1 atom|R|1 atom>|1 photon> +

2<2 photon|<0 atom|R|0 atom>|2 photon> + 2<2 photon|<1 atom|R|1 atom>|2 photon> +

3<3 photon|<0 atom|R|0 atom>|3 photon> + 3<3 photon|<1 atom|R|1 atom>|3 photon> +

4<4 photon|<0 atom|R|0 atom>|4 photon> + 4<4 photon|<1 atom|R|1 atom>|4 photon>

The point is to transform the above expression into a symbolic variable type.Remember the dictionary object dic, (obtained from QeqN)

>>> dic

’<0 photon|<0 atom|R|0 atom>|0 photon>’: y0,

’<0 photon|<0 atom|R|0 atom>|1 photon>’: y1 + I*y2,

’<0 photon|<0 atom|R|0 atom>|2 photon>’: y3 + I*y4,

...

’<0 photon|<1 atom|R|0 atom>|2 photon>’: y41 - I*y42,

’<0 photon|<1 atom|R|0 atom>|3 photon>’: y54 - I*y55,

’<0 photon|<1 atom|R|0 atom>|4 photon>’: y65 - I*y66,

’<0 photon|<1 atom|R|1 atom>|0 photon>’: y75,

’<0 photon|<1 atom|R|1 atom>|1 photon>’: y76 + I*y77,

...

So using this dictionary and the function Qch, we are able to transform theexpression obs,

>>> obs = Qch(obs, dic); obs

y19 + y84 + 2*y36 + 2*y91 + 3*y51 + 3*y96 + 4*y64 + 4*y99

Now comes SubsSol. At this point, we have the instructions of what we wantto plot (the above expression) and the whole time evolution (the object sol); to

21

Page 22: OpenKetManual.pdf

plot this manually would be very unpractical (you would have to type sol[:,19]

+ sol[:,84] + 2*sol[:,36] + . . . ).SubsSol does this for you14. It has two arguments SubsSol(obs, sol). The

first argument is the instructions of what to plot, (the expression with the yi’s

variables); and the second argument is the object sol which returns odeint, i.e.the whole time evolution. It looks like this,

>>> OBS = SubsSol(obs, sol)

Then OBS is an array of elements which has the time evolution of the meannumber of photons, in this case. Plotting OBS results in,

0 2 4 6 8 10

t0.000

0.002

0.004

0.006

0.008

0.010

0.012

0.014

tr(ρN

)

Figure 4: Evolution of the mean number of photons, having the atom at the basestate and no photons as an initial condition

Let’s try now plotting the time evolution of the probability of finding the atomat the base state. This can be calculated with tr(ρ|0〉〈0|atom ⊗ 1photon). Typingthis in OpenKet is easy:

>>> bs=Trace(R*Ket(0, ’atom’)*Bra(0, ’atom’), b); bs

<0 photon|<0 atom|R|0 atom>|0 photon> + <1 photon|<0 atom|R|0 atom>|1 photon> +

<2 photon|<0 atom|R|0 atom>|2 photon> + <3 photon|<0 atom|R|0 atom>|3 photon> +

<4 photon|<0 atom|R|0 atom>|4 photon>

14it also supports expressions with functions in them e.g. sin, log, cos, etc. (actually allfunctions recognized by Sympy)

22

Page 23: OpenKetManual.pdf

Note that the tensor product with the identity on the photon part is implicit.Next, we change it to sympy variables using the dic,

>>> bs = Qch(bs, dic); bs

y0 + y19 + y36 + y51 + y64

Obtain the numerical information with SubsSol,

>>> BS = SubsSol(bs, sol)

>>> plot(t, BS)

The plot15,

0 2 4 6 8 10t

0.992

0.993

0.994

0.995

0.996

0.997

0.998

0.999

1.000

tr(ρ|0⟩⟨ 0|

atom⊗

photon

)

Figure 5: Time-evolution of the Probability of finding the atom in the base state;having the atom at the base state and no photons as an initial condition

15This can be done in one line, due to the fact that Python allows for nesting of functions:>>> plot(t, SubsSol(Qch(Trace(R*Ket(0,’atom’)*Bra(0,’atom’), b), dic), sol, b))

23