For Better Performance Please Use Chrome or Firefox Web Browser

Time Evolution and Quantum System Dynamics

The solver.Result Class

Before embarking on simulating the dynamics of quantum systems, we will first look at the data structure used for returning the simulation results to the user. This object is a qutip.solver.Result class that stores all the crucial data needed for analyzing and plotting the results of a simulation. Like the qutip.Qobj class, the Result class has a collection of properties for storing information. However, in contrast to the Qobj class, this structure contains no methods, and is therefore nothing but a container object. A generic Result object result contains the following properties for storing simulation data:

Property Description
result.solver String indicating which solver was used to generate the data.
result.time List/array of times at which simulation data is calculated.
result.xpect List/array of expectation values, if requested.
result.states List/array of state vectors/density matrices calculated at times, if requested.

Accessing Result Data

To understand how to access the data in a Result object we will use an example as a guide, although we do not worry about the simulation details at this stage. Like all solvers, the Master Equation solver used in this example returns an Result object, here called simply result. To access the data for the expectation values one can do:

In [1]: expt0 = result.expect[0]
In [2]: expt1 = result.expect[1]

Recall that Python uses C-style indexing that begins with zero (i.e., [0] => 1st collapse operator data). Together with the array of times at which these expectation values are calculated:

In [3]: time = result.time

we can plot the resulting expectation values:

In [4]: plot(time, expt0, time, expt1)

Saving and Loading Result Objects

The main advantage in using the Result class as a data storage object comes from the simplicity in which simulation data can be stored and later retrieved. The qutip.fileio.qsave and qutip.fileio.qload functions are designed for this task. To begin, let us save the data object from the previous section into a file called “cavity-data” in the current working directory by calling:

In [5]: qsave(result, 'cavity-data')

All of the data results are then stored in a single file of the same name with a “.qu” extension. Therefore, everything needed to later this data is stored in a single file. Loading the file is just as easy as saving:

In [6]: stored_result = qload('cavity-data')

Unitary evolution

The dynamics of a closed (pure) quantum system is governed by the Schrödinger equation. In general, the Schrödinger equation is a partial differential equation (PDE). For computational purposes it is useful to expand the PDE in a set of basis functions that span the Hilbert space of the Hamiltonian, and to write the equation in matrix and vector form. Such matrix equation can, in principle, be solved by diagonalizing the Hamiltonian matrix. The Schrödinger equation, which governs the time-evolution of closed quantum systems, is defined by its Hamiltonian and state vector. Given a Hamiltonian, we can calculate the unitary (non-dissipative) time-evolution of an arbitrary state vector psi0 using the QuTiP function qutip.mesolve. It evolves the state vector and evaluates the expectation values for a set of operators expt_ops at the points in time in the list times, using an ordinary differential equation solver. Alternatively, we can use the function qutip.essolve, which uses the exponential-series technique to calculate the time evolution of a system. The qutip.mesolve and qutip.essolve functions take the same arguments and it is therefore easy switch between the two solvers.

For example, the time evolution of a coherent state in simple harmonic potenital is calculated with the following code:

In [7]: dim = 10
In [8]: w = 1
In [9]: alpha = 1j
In [10]: a = destroy(dim)
In [11]: H = w*a.dag()*a
In [12]: psi0 = coherent(dim, alpha)
In [13]: time = np.linspace(0.0, np.pi/w, 10)
In [14]: result = mesolve(H, psi0, time, [], [a +a.dag()])

The brackets in the fourth and fifth arguments are an empty list of collapse operators and expectation value operators, respectively. Since we consider unitary evolution in this example the collapse operators are taken null. See the next section for examples on how dissipation is included by defining a list of collapse operators.

The function returns an instance of qutip.solver.Result, as described before. The attribute expect in result is a list of expectation values for the operators that are included in the list in the fifth argument. Adding operators to this list results in a larger output list returned by the function (one array of numbers, corresponding to the times in times, for each operator).

The resulting list of expectation values can easily be visualized using matplotlib’s plotting functions:

 

تحت نظارت وف ایرانی