Skip to content

Commit 9f5a033

Browse files
Align steadystate ODE solver
1 parent e3b2c09 commit 9f5a033

File tree

4 files changed

+58
-76
lines changed

4 files changed

+58
-76
lines changed

docs/src/users_guide/steadystate.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ To change a solver, use the keyword argument `solver`, for example:
3737
ρ_ss = steadystate(H, c_ops; solver = SteadyStateLinearSolver())
3838
```
3939

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-
4440
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.
4541

4642
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:

src/steadystate.jl

Lines changed: 52 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct SteadyStateEigenSolver <: SteadyStateSolver end
2727
2828
A solver which solves [`steadystate`](@ref) by finding the inverse of Liouvillian [`SuperOperator`](@ref) using the `alg`orithms given in [`LinearSolve.jl`](https://docs.sciml.ai/LinearSolve/stable/).
2929
30-
# Parameters
30+
# Arguments
3131
- `alg::SciMLLinearSolveAlgorithm=KrylovJL_GMRES()`: algorithms given in [`LinearSolve.jl`](https://docs.sciml.ai/LinearSolve/stable/)
3232
- `Pl::Union{Function,Nothing}=nothing`: left preconditioner, see documentation [Solving for Steady-State Solutions](@ref doc:Solving-for-Steady-State-Solutions) for more details.
3333
- `Pr::Union{Function,Nothing}=nothing`: right preconditioner, see documentation [Solving for Steady-State Solutions](@ref doc:Solving-for-Steady-State-Solutions) for more details.
@@ -43,16 +43,46 @@ Base.@kwdef struct SteadyStateLinearSolver{
4343
end
4444

4545
@doc raw"""
46-
SteadyStateODESolver(alg = Tsit5())
46+
SteadyStateODESolver(
47+
alg = Tsit5(),
48+
ψ0 = nothing,
49+
tmax = Inf,
50+
reltol = 1.0e-6,
51+
abstol = 1.0e-8,
52+
)
4753
4854
An ordinary differential equation (ODE) solver for solving [`steadystate`](@ref).
4955
50-
It includes a field (attribute) `SteadyStateODESolver.alg` that specifies the solving algorithm. Default to `Tsit5()`.
56+
Solve the stationary state based on time evolution (ordinary differential equations; `OrdinaryDiffEq.jl`) with a given initial state.
57+
58+
The termination condition of the stationary state ``|\rho\rangle\rangle`` is that either the following condition is `true`:
59+
60+
```math
61+
\lVert\frac{\partial |\hat{\rho}\rangle\rangle}{\partial t}\rVert \leq \textrm{reltol} \times\lVert\frac{\partial |\hat{\rho}\rangle\rangle}{\partial t}+|\hat{\rho}\rangle\rangle\rVert
62+
```
63+
64+
or
65+
66+
```math
67+
\lVert\frac{\partial |\hat{\rho}\rangle\rangle}{\partial t}\rVert \leq \textrm{abstol}
68+
```
5169
52-
For more details about the solvers, please refer to [`OrdinaryDiffEq.jl`](https://docs.sciml.ai/OrdinaryDiffEq/stable/)
70+
# Arguments
71+
- `alg::OrdinaryDiffEqAlgorithm=Tsit5()`: The algorithm to solve the ODE.
72+
- `ψ0::Union{Nothing,QuantumObject}=nothing`: The initial state of the system. If not specified, a random pure state will be generated.
73+
- `tmax::Real=Inf`: The final time step for the steady state problem.
74+
- `reltol::Real=1.0e-6`: Relative tolerance in steady state terminate condition. It can be different from the integrator's tolerance.
75+
- `abstol::Real=1.0e-8`: Absolute tolerance in steady state terminate condition. It can be different from the integrator's tolerance.
76+
77+
For more details about the solvers, please refer to [`OrdinaryDiffEq.jl`](https://docs.sciml.ai/OrdinaryDiffEq/stable/).
5378
"""
54-
Base.@kwdef struct SteadyStateODESolver{MT<:OrdinaryDiffEqAlgorithm} <: SteadyStateSolver
79+
Base.@kwdef struct SteadyStateODESolver{MT<:OrdinaryDiffEqAlgorithm,ST<:Union{Nothing,QuantumObject}} <:
80+
SteadyStateSolver
5581
alg::MT = Tsit5()
82+
ψ0::ST = nothing
83+
tmax::Float64 = Inf
84+
reltol::Float64 = 1.0e-4
85+
abstol::Float64 = 1.0e-6
5686
end
5787

5888
@doc raw"""
@@ -175,68 +205,20 @@ function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::Stea
175205
return QuantumObject(ρss, Operator, L.dimensions)
176206
end
177207

178-
_steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::SteadyStateODESolver; kwargs...) = throw(
179-
ArgumentError(
180-
"The initial state ψ0 is required for SteadyStateODESolver, use the following call instead: `steadystate(H, ψ0, tmax, c_ops)`.",
181-
),
182-
)
183-
184-
@doc raw"""
185-
steadystate(
186-
H::QuantumObject{HOpType},
187-
ψ0::QuantumObject{StateOpType},
188-
tmax::Real = Inf,
189-
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
190-
solver::SteadyStateODESolver = SteadyStateODESolver(),
191-
reltol::Real = 1.0e-8,
192-
abstol::Real = 1.0e-10,
193-
kwargs...,
194-
)
195-
196-
Solve the stationary state based on time evolution (ordinary differential equations; `OrdinaryDiffEq.jl`) with a given initial state.
197-
198-
The termination condition of the stationary state ``|\rho\rangle\rangle`` is that either the following condition is `true`:
208+
function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::SteadyStateODESolver; kwargs...)
209+
tmax = solver.tmax
210+
abstol = solver.abstol
211+
reltol = solver.reltol
199212

200-
```math
201-
\lVert\frac{\partial |\hat{\rho}\rangle\rangle}{\partial t}\rVert \leq \textrm{reltol} \times\lVert\frac{\partial |\hat{\rho}\rangle\rangle}{\partial t}+|\hat{\rho}\rangle\rangle\rVert
202-
```
203-
204-
or
205-
206-
```math
207-
\lVert\frac{\partial |\hat{\rho}\rangle\rangle}{\partial t}\rVert \leq \textrm{abstol}
208-
```
213+
ψ0 = isnothing(solver.ψ0) ? rand_ket(L.dimensions) : solver.ψ0
209214

210-
# Parameters
211-
- `H`: The Hamiltonian or the Liouvillian of the system.
212-
- `ψ0`: The initial state of the system.
213-
- `tmax=Inf`: The final time step for the steady state problem.
214-
- `c_ops=nothing`: The list of the collapse operators.
215-
- `solver`: see [`SteadyStateODESolver`](@ref) for more details.
216-
- `reltol=1.0e-8`: Relative tolerance in steady state terminate condition and solver adaptive timestepping.
217-
- `abstol=1.0e-10`: Absolute tolerance in steady state terminate condition and solver adaptive timestepping.
218-
- `kwargs`: The keyword arguments for the ODEProblem.
219-
"""
220-
function steadystate(
221-
H::QuantumObject{HOpType},
222-
ψ0::QuantumObject{StateOpType},
223-
tmax::Real = Inf,
224-
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
225-
solver::SteadyStateODESolver = SteadyStateODESolver(),
226-
reltol::Real = 1.0e-8,
227-
abstol::Real = 1.0e-10,
228-
kwargs...,
229-
) where {
230-
HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},
231-
StateOpType<:Union{KetQuantumObject,OperatorQuantumObject},
232-
}
233215
ftype = _FType(ψ0)
234-
cb = TerminateSteadyState(abstol, reltol, _steadystate_ode_condition)
216+
_terminate_func = SteadyStateODECondition(similar(mat2vec(ket2dm(ψ0)).data))
217+
cb = TerminateSteadyState(abstol, reltol, _terminate_func)
235218
sol = mesolve(
236-
H,
219+
L,
237220
ψ0,
238221
[ftype(0), ftype(tmax)],
239-
c_ops,
240222
progress_bar = Val(false),
241223
save_everystep = false,
242224
saveat = ftype[],
@@ -247,12 +229,17 @@ function steadystate(
247229
return ρss
248230
end
249231

250-
function _steadystate_ode_condition(integrator, abstol, reltol, min_t)
232+
struct SteadyStateODECondition{CT<:AbstractArray}
233+
cache::CT
234+
end
235+
236+
function (f::SteadyStateODECondition)(integrator, abstol, reltol, min_t)
251237
# this condition is same as DiffEqBase.NormTerminationMode
252238

253-
du_dt = (integrator.u - integrator.uprev) / integrator.dt
254-
norm_du_dt = norm(du_dt)
255-
if (norm_du_dt <= reltol * norm(du_dt + integrator.u)) || (norm_du_dt <= abstol)
239+
f.cache .= (integrator.u .- integrator.uprev) ./ integrator.dt
240+
norm_du_dt = norm(f.cache)
241+
f.cache .+= integrator.u
242+
if norm_du_dt <= reltol * norm(f.cache) || norm_du_dt <= abstol
256243
return true
257244
else
258245
return false

test/core-test/steady_state.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
rho_me = sol_me.states[end]
1212

1313
solver = SteadyStateODESolver()
14-
ρ_ss = steadystate(H, psi0, t_l[end], c_ops, solver = solver)
14+
ρ_ss = steadystate(H, c_ops, solver = solver)
1515
@test tracedist(rho_me, ρ_ss) < 1e-4
16-
@test_throws ArgumentError steadystate(H, c_ops, solver = solver)
1716

1817
solver = SteadyStateDirectSolver()
1918
ρ_ss = steadystate(H, c_ops, solver = solver)
@@ -34,9 +33,9 @@
3433
@testset "Type Inference (steadystate)" begin
3534
L = liouvillian(H, c_ops)
3635

37-
solver = SteadyStateODESolver()
38-
@inferred steadystate(H, psi0, t_l[end], c_ops, solver = solver)
39-
@inferred steadystate(L, psi0, t_l[end], solver = solver)
36+
solver = SteadyStateODESolver(tmax = t_l[end])
37+
@inferred steadystate(H, c_ops, solver = solver)
38+
@inferred steadystate(L, solver = solver)
4039

4140
solver = SteadyStateDirectSolver()
4241
@inferred steadystate(H, c_ops, solver = solver)

test/ext-test/gpu/cuda_ext.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ end
146146
c_ops_gpu_csr = [CuSparseMatrixCSR(c_op) for c_op in c_ops_gpu_csc]
147147
ρ_ss_gpu_csr = steadystate(H_gpu_csr, c_ops_gpu_csr, solver = SteadyStateLinearSolver())
148148

149-
@test ρ_ss_cpu.data Array(ρ_ss_gpu_csc.data) atol = 1e-8*length(ρ_ss_cpu)
150-
@test ρ_ss_cpu.data Array(ρ_ss_gpu_csr.data) atol = 1e-8*length(ρ_ss_cpu)
149+
@test ρ_ss_cpu.data Array(ρ_ss_gpu_csc.data) atol = 1e-8 * length(ρ_ss_cpu)
150+
@test ρ_ss_cpu.data Array(ρ_ss_gpu_csr.data) atol = 1e-8 * length(ρ_ss_cpu)
151151
end
152152

153153
@testset "CUDA ptrace" begin

0 commit comments

Comments
 (0)