Skip to content

Commit bdac5f4

Browse files
authored
[Docs] Update docstring and documentation for steadystate (#240)
* add missing docstrings for `steadystate` solvers * update documentation for `steadystate` * format files * improve `steadystate` documentation
1 parent c4df497 commit bdac5f4

File tree

5 files changed

+267
-91
lines changed

5 files changed

+267
-91
lines changed

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const PAGES = [
3434
"Time Evolution and Dynamics" => [
3535
"Introduction" => "users_guide/time_evolution/intro.md",
3636
],
37-
"Solving for Steady-State Solutions" => [],
37+
"Solving for Steady-State Solutions" => "users_guide/steadystate.md",
3838
"Symmetries" => [],
3939
"Two-time correlation functions" => [],
4040
"Extensions" => [

docs/src/api.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ liouvillian
195195
liouvillian_generalized
196196
steadystate
197197
steadystate_floquet
198+
SteadyStateDirectSolver
199+
SteadyStateEigenSolver
200+
SteadyStateLinearSolver
198201
SteadyStateODESolver
199202
```
200203

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# [Solving for Steady-State Solutions](@id doc:Solving-for-Steady-State-Solutions)
2+
3+
## Introduction
4+
5+
For time-independent open quantum systems with decay rates larger than the corresponding excitation rates, the system will tend toward a steady state as ``t\rightarrow\infty`` that satisfies the equation
6+
7+
```math
8+
\frac{d\hat{\rho}_{\textrm{ss}}}{dt} = \mathcal{L}\hat{\rho}_{\textrm{ss}}=0.
9+
```
10+
11+
Although the requirement for time-independence seems quite restrictive, one can often employ a transformation to the interaction picture that yields a time-independent Hamiltonian. For many these systems, solving for the asymptotic density matrix ``\hat{\rho}_{\textrm{ss}}`` can be achieved using direct or iterative solution methods faster than using master equation or Monte-Carlo simulations. Although the steady state equation has a simple mathematical form, the properties of the Liouvillian operator are such that the solutions to this equation are anything but straightforward to find.
12+
13+
## Steady State solvers in `QuantumToolbox.jl`
14+
In `QuantumToolbox.jl`, the steady-state solution for a system Hamiltonian or Liouvillian is given by [`steadystate`](@ref). This function implements a number of different methods for finding the steady state, each with their own pros and cons, where the method used can be chosen using the `solver` keyword argument.
15+
16+
| **Solver** | **Description** |
17+
|:-----------|:----------------|
18+
| [`SteadyStateDirectSolver()`](@ref SteadyStateDirectSolver) | Directly solve ``Ax=b`` using the standard method given in `Julia` `LinearAlgebra` |
19+
| [`SteadyStateLinearSolver()`](@ref SteadyStateLinearSolver) | Directly solve ``Ax=b`` using the algorithms given in [`LinearSolve.jl`](https://docs.sciml.ai/LinearSolve/stable/) |
20+
| [`SteadyStateEigenSolver()`](@ref SteadyStateEigenSolver) | Find the zero (or lowest) eigenvalue of ``\mathcal{L}`` using [`eigsolve`](@ref) |
21+
| [`SteadyStateODESolver()`](@ref SteadyStateODESolver) | Solving time evolution with algorithms given in [`DifferentialEquations.jl` (ODE Solvers)](https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/) |
22+
23+
## Using Steady State solvers
24+
25+
The function [`steadystate`](@ref) can take either a Hamiltonian and a list of collapse operators `c_ops` as input, generating internally the corresponding Liouvillian ``\mathcal{L}`` in Lindblad form, or alternatively, a Liouvillian ``\mathcal{L}`` passed by the user.
26+
27+
```julia
28+
ρ_ss = steadystate(H) # Hamiltonian
29+
ρ_ss = steadystate(H, c_ops) # Hamiltonian and collapse operators
30+
ρ_ss = steadystate(L) # Liouvillian
31+
```
32+
where `H` is a quantum object representing the system Hamiltonian ([`Operator`](@ref)) or Liouvillian ([`SuperOperator`](@ref)), and `c_ops` (defaults to `nothing`) can be a list of [`QuantumObject`](@ref) for the system collapse operators. The output, labelled as `ρ_ss`, is the steady-state solution for the systems. If no other keywords are passed to the function, the default solver [`SteadyStateDirectSolver()`](@ref SteadyStateDirectSolver) is used.
33+
34+
To change a solver, use the keyword argument `solver`, for example:
35+
36+
```julia
37+
ρ_ss = steadystate(H, c_ops; solver = SteadyStateLinearSolver())
38+
```
39+
40+
!!! note "Initial state for `SteadyStateODESolver()`"
41+
It is necessary to provide the initial state `ψ0` for ODE solving method, namely
42+
`steadystate(H, ψ0, tspan, c_ops, solver = SteadyStateODESolver())`, where `tspan::Real` represents the final time step, defaults to `Inf` (infinity).
43+
44+
Although it is not obvious, the [`SteadyStateDirectSolver()`](@ref SteadyStateDirectSolver) and [`SteadyStateEigenSolver()`](@ref SteadyStateEigenSolver) methods all use an LU decomposition internally and thus can have a large memory overhead. In contrast, for [`SteadyStateLinearSolver()`](@ref SteadyStateLinearSolver), iterative algorithms provided by [`LinearSolve.jl`](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/), such as `KrylovJL_GMRES()` and `KrylovJL_BICGSTAB()`, do not factor the matrix and thus take less memory than the LU methods and allow, in principle, for extremely large system sizes. The downside is that these methods can take much longer than the direct method as the condition number of the Liouvillian matrix is large, indicating that these iterative methods require a large number of iterations for convergence.
45+
46+
To overcome this, one can provide preconditioner that solves for an approximate inverse for the (modified) Liouvillian, thus better conditioning the problem, leading to faster convergence. The left and right preconditioner can be specified as the keyword argument `Pl` and `Pr`, respectively:
47+
```julia
48+
solver = SteadyStateLinearSolver(alg = KrylovJL_GMRES(), Pl = Pl, Pr = Pr)
49+
```
50+
The use of a preconditioner can actually make these iterative methods faster than the other solution methods. The problem with precondioning is that it is only well defined for Hermitian matrices. Since the Liouvillian is non-Hermitian, the ability to find a good preconditioner is not guaranteed. Moreover, if a preconditioner is found, it is not guaranteed to have a good condition number.
51+
52+
Furthermore, `QuantiumToolbox` can take advantage of the Intel (Pardiso) LU solver in the Intel Math Kernel library (Intel-MKL) by using [`LinearSolve.jl`](https://docs.sciml.ai/LinearSolve/stable/) and either [`Pardiso.jl`](https://github.com/JuliaSparse/Pardiso.jl) or [`MKL_jll.jl`](https://github.com/JuliaBinaryWrappers/MKL_jll.jl):
53+
54+
```julia
55+
using QuantumToolbox
56+
using LinearSolve # must be loaded
57+
58+
using Pardiso
59+
solver = SteadyStateLinearSolver(alg = MKLPardisoFactorize())
60+
61+
using MKL_jll
62+
solver = SteadyStateLinearSolver(alg = MKLLUFactorization())
63+
```
64+
65+
See [`LinearSolve.jl` Solvers](https://docs.sciml.ai/LinearSolve/stable/solvers/solvers/) for more details.
66+
67+
## Example: Harmonic Oscillator in Thermal Bath
68+
69+
```@example steady_state_example
70+
using QuantumToolbox
71+
using CairoMakie
72+
CairoMakie.enable_only_mime!(MIME"image/svg+xml"())
73+
74+
75+
# Define parameters
76+
N = 20 # number of basis states to consider
77+
a = destroy(N)
78+
H = a' * a
79+
ψ0 = basis(N, 10) # initial state
80+
κ = 0.1 # coupling to oscillator
81+
n_th = 2 # temperature with average of 2 excitations
82+
83+
# collapse operators
84+
# c_op_list = [ emission ; absorption ]
85+
c_op_list = [ sqrt(κ * (1 + n_th)) * a ; sqrt(κ * n_th) * a' ]
86+
87+
# find steady-state solution
88+
ρ_ss = steadystate(H, c_op_list)
89+
90+
# find expectation value for particle number in steady state
91+
e_ops = [a' * a]
92+
exp_ss = real(expect(e_ops[1], ρ_ss))
93+
94+
tlist = LinRange(0, 50, 100)
95+
96+
# monte-carlo
97+
sol_mc = mcsolve(H, ψ0, tlist, c_op_list, e_ops=e_ops, n_traj=100, progress_bar=false)
98+
exp_mc = real(sol_mc.expect[1, :])
99+
100+
# master eq.
101+
sol_me = mesolve(H, ψ0, tlist, c_op_list, e_ops=e_ops, progress_bar=false)
102+
exp_me = real(sol_me.expect[1, :])
103+
104+
# plot the results
105+
fig = Figure(size = (800, 400), fontsize = 15)
106+
ax = Axis(fig[1, 1],
107+
title = L"Decay of Fock state $|10\rangle$ in a thermal environment with $\langle n\rangle=2$",
108+
xlabel = "Time",
109+
ylabel = "Number of excitations",
110+
titlesize = 24,
111+
xlabelsize = 20,
112+
ylabelsize = 20
113+
)
114+
lines!(ax, tlist, exp_mc, label = "Monte-Carlo", linewidth = 2, color = :blue)
115+
lines!(ax, tlist, exp_me, label = "Master Equation", linewidth = 2, color = :orange, linestyle = :dash)
116+
lines!(ax, tlist, exp_ss .* ones(length(tlist)), label = "Steady State", linewidth = 2, color = :red)
117+
axislegend(ax, position = :rt)
118+
119+
fig
120+
```
121+
122+
## Calculate steady state for periodically driven systems
123+
124+
See the docstring of [`steadystate_floquet`](@ref) for more details.

0 commit comments

Comments
 (0)