Skip to content

Commit 0ce0b45

Browse files
Add more generic solver for steadystate_floquet (#396)
1 parent 28fe4a9 commit 0ce0b45

File tree

5 files changed

+69
-31
lines changed

5 files changed

+69
-31
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Fix erroneous definition of the stochastic term in `smesolve`. ([#393])
1212
- Change name of `MultiSiteOperator` to `multisite_operator`. ([#394])
1313
- Fix `smesolve` for specifying initial state as density matrix. ([#395])
14+
- Add more generic solver for `steadystate_floquet` to allow more linear solvers. ([#396])
1415
- Fix time evolution output when using `saveat` keyword argument. ([#398])
1516

1617
## [v0.26.0]
@@ -125,4 +126,5 @@ Release date: 2024-11-13
125126
[#393]: https://github.com/qutip/QuantumToolbox.jl/issues/393
126127
[#394]: https://github.com/qutip/QuantumToolbox.jl/issues/394
127128
[#395]: https://github.com/qutip/QuantumToolbox.jl/issues/395
129+
[#396]: https://github.com/qutip/QuantumToolbox.jl/issues/396
128130
[#398]: https://github.com/qutip/QuantumToolbox.jl/issues/398

docs/src/resources/api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,12 @@ liouvillian_generalized
212212

213213
```@docs
214214
steadystate
215-
steadystate_floquet
215+
steadystate_fourier
216216
SteadyStateDirectSolver
217217
SteadyStateEigenSolver
218218
SteadyStateLinearSolver
219219
SteadyStateODESolver
220+
SSFloquetEffectiveLiouvillian
220221
```
221222

222223
### [Dynamical Shifted Fock method](@id doc-API:Dynamical-Shifted-Fock-method)

docs/src/users_guide/steadystate.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,4 @@ fig
121121

122122
## Calculate steady state for periodically driven systems
123123

124-
See the docstring of [`steadystate_floquet`](@ref) for more details.
124+
See the docstring of [`steadystate_fourier`](@ref) for more details.

src/steadystate.jl

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
export steadystate, steadystate_floquet
1+
export steadystate, steadystate_fourier, steadystate_floquet
22
export SteadyStateSolver,
33
SteadyStateDirectSolver,
44
SteadyStateEigenSolver,
55
SteadyStateLinearSolver,
66
SteadyStateODESolver,
7-
SteadyStateFloquetSolver,
8-
SSFloquetLinearSystem,
97
SSFloquetEffectiveLiouvillian
108

119
abstract type SteadyStateSolver end
12-
abstract type SteadyStateFloquetSolver end
1310

1411
@doc raw"""
1512
SteadyStateDirectSolver()
@@ -58,8 +55,18 @@ Base.@kwdef struct SteadyStateODESolver{MT<:OrdinaryDiffEqAlgorithm} <: SteadySt
5855
alg::MT = Tsit5()
5956
end
6057

61-
struct SSFloquetLinearSystem <: SteadyStateFloquetSolver end
62-
Base.@kwdef struct SSFloquetEffectiveLiouvillian{SSST<:SteadyStateSolver} <: SteadyStateFloquetSolver
58+
@doc raw"""
59+
SSFloquetEffectiveLiouvillian(steadystate_solver = SteadyStateDirectSolver())
60+
61+
A solver which solves [`steadystate_fourier`](@ref) by first extracting an effective time-independent Liouvillian and then using the `steadystate_solver` to extract the steadystate..
62+
63+
# Parameters
64+
- `steadystate_solver::SteadyStateSolver=SteadyStateDirectSolver()`: The solver to use for the effective Liouvillian.
65+
66+
!!! note
67+
This solver is only available for [`steadystate_fourier`](@ref).
68+
"""
69+
Base.@kwdef struct SSFloquetEffectiveLiouvillian{SSST<:SteadyStateSolver} <: SteadyStateSolver
6370
steadystate_solver::SSST = SteadyStateDirectSolver()
6471
end
6572

@@ -85,6 +92,12 @@ function steadystate(
8592
solver::SteadyStateSolver = SteadyStateDirectSolver(),
8693
kwargs...,
8794
) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}}
95+
solver isa SSFloquetEffectiveLiouvillian && throw(
96+
ArgumentError(
97+
"The solver `SSFloquetEffectiveLiouvillian` is only available for the `steadystate_fourier` function.",
98+
),
99+
)
100+
88101
L = liouvillian(H, c_ops)
89102

90103
return _steadystate(L, solver; kwargs...)
@@ -247,15 +260,15 @@ function _steadystate_ode_condition(integrator, abstol, reltol, min_t)
247260
end
248261

249262
@doc raw"""
250-
steadystate_floquet(
251-
H_0::QuantumObject{OpType1},
252-
H_p::QuantumObject{OpType2},
253-
H_m::QuantumObject{OpType3},
263+
steadystate_fourier(
264+
H_0::QuantumObject,
265+
H_p::QuantumObject,
266+
H_m::QuantumObject,
254267
ωd::Number,
255268
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
256269
n_max::Integer = 2,
257270
tol::R = 1e-8,
258-
solver::FSolver = SSFloquetLinearSystem,
271+
solver::FSolver = SteadyStateLinearSolver(),
259272
kwargs...,
260273
)
261274
@@ -265,11 +278,11 @@ Considering a monochromatic drive at frequency ``\omega_d``, we divide it into t
265278
`H_p` and `H_m`, where `H_p` oscillates
266279
as ``e^{i \omega t}`` and `H_m` oscillates as ``e^{-i \omega t}``.
267280
There are two solvers available for this function:
268-
- `SSFloquetLinearSystem`: Solves the linear system of equations.
281+
- `SteadyStateLinearSolver`: Solves the linear system of equations.
269282
- `SSFloquetEffectiveLiouvillian`: Solves the effective Liouvillian.
270283
For both cases, `n_max` is the number of Fourier components to consider, and `tol` is the tolerance for the solver.
271284
272-
In the case of `SSFloquetLinearSystem`, the full linear system is solved at once:
285+
In the case of `SteadyStateLinearSolver`, the full linear system is solved at once:
273286
274287
```math
275288
( \mathcal{L}_0 - i n \omega_d ) \hat{\rho}_n + \mathcal{L}_1 \hat{\rho}_{n-1} + \mathcal{L}_{-1} \hat{\rho}_{n+1} = 0
@@ -312,7 +325,10 @@ This will allow to simultaneously obtain all the ``\hat{\rho}_n``.
312325
In the case of `SSFloquetEffectiveLiouvillian`, instead, the effective Liouvillian is calculated using the matrix continued fraction method.
313326
314327
!!! note "different return"
315-
The two solvers returns different objects. The `SSFloquetLinearSystem` returns a list of [`QuantumObject`](@ref), containing the density matrices for each Fourier component (``\hat{\rho}_{-n}``, with ``n`` from ``0`` to ``n_\textrm{max}``), while the `SSFloquetEffectiveLiouvillian` returns only ``\hat{\rho}_0``.
328+
The two solvers returns different objects. The `SteadyStateLinearSolver` returns a list of [`QuantumObject`](@ref), containing the density matrices for each Fourier component (``\hat{\rho}_{-n}``, with ``n`` from ``0`` to ``n_\textrm{max}``), while the `SSFloquetEffectiveLiouvillian` returns only ``\hat{\rho}_0``.
329+
330+
!!! note
331+
`steadystate_floquet` is a synonym of `steadystate_fourier`.
316332
317333
## Arguments
318334
- `H_0::QuantumObject`: The Hamiltonian or the Liouvillian of the undriven system.
@@ -322,38 +338,38 @@ In the case of `SSFloquetEffectiveLiouvillian`, instead, the effective Liouvilli
322338
- `c_ops::Union{Nothing,AbstractVector} = nothing`: The optional collapse operators.
323339
- `n_max::Integer = 2`: The number of Fourier components to consider.
324340
- `tol::R = 1e-8`: The tolerance for the solver.
325-
- `solver::FSolver = SSFloquetLinearSystem`: The solver to use.
341+
- `solver::FSolver = SteadyStateLinearSolver`: The solver to use.
326342
- `kwargs...`: Additional keyword arguments to be passed to the solver.
327343
"""
328-
function steadystate_floquet(
344+
function steadystate_fourier(
329345
H_0::QuantumObject{OpType1},
330346
H_p::QuantumObject{OpType2},
331347
H_m::QuantumObject{OpType3},
332348
ωd::Number,
333349
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
334350
n_max::Integer = 2,
335351
tol::R = 1e-8,
336-
solver::FSolver = SSFloquetLinearSystem(),
352+
solver::FSolver = SteadyStateLinearSolver(),
337353
kwargs...,
338354
) where {
339355
OpType1<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},
340356
OpType2<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},
341357
OpType3<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},
342358
R<:Real,
343-
FSolver<:SteadyStateFloquetSolver,
359+
FSolver<:SteadyStateSolver,
344360
}
345361
L_0 = liouvillian(H_0, c_ops)
346362
L_p = liouvillian(H_p)
347363
L_m = liouvillian(H_m)
348-
return _steadystate_floquet(L_0, L_p, L_m, ωd, solver; n_max = n_max, tol = tol, kwargs...)
364+
return _steadystate_fourier(L_0, L_p, L_m, ωd, solver; n_max = n_max, tol = tol, kwargs...)
349365
end
350366

351-
function _steadystate_floquet(
367+
function _steadystate_fourier(
352368
L_0::QuantumObject{SuperOperatorQuantumObject},
353369
L_p::QuantumObject{SuperOperatorQuantumObject},
354370
L_m::QuantumObject{SuperOperatorQuantumObject},
355371
ωd::Number,
356-
solver::SSFloquetLinearSystem;
372+
solver::SteadyStateLinearSolver;
357373
n_max::Integer = 1,
358374
tol::R = 1e-8,
359375
kwargs...,
@@ -387,9 +403,18 @@ function _steadystate_floquet(
387403
v0 = zeros(T, n_fourier * N)
388404
v0[n_max*N+1] = weight
389405

390-
Pl = ilu(M, τ = 0.01)
406+
(haskey(kwargs, :Pl) || haskey(kwargs, :Pr)) && error("The use of preconditioners must be defined in the solver.")
407+
if !isnothing(solver.Pl)
408+
kwargs = merge((; kwargs...), (Pl = solver.Pl(M),))
409+
elseif isa(M, SparseMatrixCSC)
410+
kwargs = merge((; kwargs...), (Pl = ilu(M, τ = 0.01),))
411+
end
412+
!isnothing(solver.Pr) && (kwargs = merge((; kwargs...), (Pr = solver.Pr(M),)))
413+
!haskey(kwargs, :abstol) && (kwargs = merge((; kwargs...), (abstol = tol,)))
414+
!haskey(kwargs, :reltol) && (kwargs = merge((; kwargs...), (reltol = tol,)))
415+
391416
prob = LinearProblem(M, v0)
392-
ρtot = solve(prob, KrylovJL_GMRES(), Pl = Pl, abstol = tol, reltol = tol).u
417+
ρtot = solve(prob, solver.alg; kwargs...).u
393418

394419
offset1 = n_max * N
395420
offset2 = (n_max + 1) * N
@@ -409,7 +434,7 @@ function _steadystate_floquet(
409434
return ρ_list
410435
end
411436

412-
function _steadystate_floquet(
437+
function _steadystate_fourier(
413438
L_0::QuantumObject{SuperOperatorQuantumObject},
414439
L_p::QuantumObject{SuperOperatorQuantumObject},
415440
L_m::QuantumObject{SuperOperatorQuantumObject},
@@ -425,3 +450,6 @@ function _steadystate_floquet(
425450

426451
return steadystate(L_eff; solver = solver.steadystate_solver, kwargs...)
427452
end
453+
454+
# TODO: Synonym to align with QuTiP. Track https://github.com/qutip/qutip/issues/2632 when this can be removed.
455+
const steadystate_floquet = steadystate_fourier

test/core-test/steady_state.jl

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,23 @@
6565
H_td = (H, (H_t, coeff))
6666

6767
sol_me = mesolve(H_td, psi0, t_l, c_ops, e_ops = e_ops, progress_bar = Val(false))
68-
ρ_ss1 = steadystate_floquet(H, -1im * 0.5 * H_t, 1im * 0.5 * H_t, 1, c_ops, solver = SSFloquetLinearSystem())[1]
68+
ρ_ss1 = steadystate_fourier(H, -1im * 0.5 * H_t, 1im * 0.5 * H_t, 1, c_ops, solver = SteadyStateLinearSolver())[1]
6969
ρ_ss2 =
70-
steadystate_floquet(H, -1im * 0.5 * H_t, 1im * 0.5 * H_t, 1, c_ops, solver = SSFloquetEffectiveLiouvillian())
70+
steadystate_fourier(H, -1im * 0.5 * H_t, 1im * 0.5 * H_t, 1, c_ops, solver = SSFloquetEffectiveLiouvillian())
7171

7272
@test abs(sum(sol_me.expect[1, end-100:end]) / 101 - expect(e_ops[1], ρ_ss1)) < 1e-3
7373
@test abs(sum(sol_me.expect[1, end-100:end]) / 101 - expect(e_ops[1], ρ_ss2)) < 1e-3
7474

75-
@testset "Type Inference (steadystate_floquet)" begin
76-
@inferred steadystate_floquet(H, -1im * 0.5 * H_t, 1im * 0.5 * H_t, 1, c_ops, solver = SSFloquetLinearSystem())
77-
@inferred steadystate_floquet(
75+
@testset "Type Inference (steadystate_fourier)" begin
76+
@inferred steadystate_fourier(
77+
H,
78+
-1im * 0.5 * H_t,
79+
1im * 0.5 * H_t,
80+
1,
81+
c_ops,
82+
solver = SteadyStateLinearSolver(),
83+
)
84+
@inferred steadystate_fourier(
7885
H,
7986
-1im * 0.5 * H_t,
8087
1im * 0.5 * H_t,

0 commit comments

Comments
 (0)