|
| 1 | +import QuantumToolbox: _spre, _spost, _sprepost, makeVal, getVal |
| 2 | + |
| 3 | +@doc raw""" |
| 4 | + bloch_redfield_tensor( |
| 5 | + H::QuantumObject{Operator}, |
| 6 | + a_ops::Union{AbstractVector, Tuple}, |
| 7 | + c_ops::Union{AbstractVector, Tuple, Nothing}=nothing; |
| 8 | + sec_cutoff::Real=0.1, |
| 9 | + fock_basis::Union{Val,Bool}=Val(false) |
| 10 | + ) |
| 11 | +
|
| 12 | +Calculates the Bloch-Redfield tensor ([`SuperOperator`](@ref)) for a system given a set of operators and corresponding spectral functions that describes the system's coupling to its environment. |
| 13 | +
|
| 14 | +## Arguments |
| 15 | +
|
| 16 | +- `H`: The system Hamiltonian. Must be an [`Operator`](@ref) |
| 17 | +- `a_ops`: Nested list with each element is a `Tuple` of operator-function pairs `(a_op, spectra)`, and the coupling [`Operator`](@ref) `a_op` must be hermitian with corresponding `spectra` being a `Function` of transition energy |
| 18 | +- `c_ops`: List of collapse operators corresponding to Lindblad dissipators |
| 19 | +- `sec_cutoff`: Cutoff for secular approximation. Use `-1` if secular approximation is not used when evaluating bath-coupling terms. |
| 20 | +- `fock_basis`: Whether to return the tensor in the input (fock) basis or the diagonalized (eigen) basis. |
| 21 | +
|
| 22 | +## Return |
| 23 | +
|
| 24 | +The return depends on `fock_basis`. |
| 25 | +
|
| 26 | +- `fock_basis=Val(true)`: return the Bloch-Redfield tensor (in the fock basis) only. |
| 27 | +- `fock_basis=Val(false)`: return the Bloch-Redfield tensor (in the eigen basis) along with the transformation matrix from eigen to fock basis. |
| 28 | +""" |
| 29 | +function bloch_redfield_tensor( |
| 30 | + H::QuantumObject{Operator}, |
| 31 | + a_ops::Union{AbstractVector, Tuple}, |
| 32 | + c_ops::Union{AbstractVector, Tuple, Nothing}=nothing; |
| 33 | + sec_cutoff::Real=0.1, |
| 34 | + fock_basis::Union{Val,Bool}=Val(false) |
| 35 | + ) |
| 36 | + rst = eigenstates(H) |
| 37 | + U = QuantumObject(rst.vectors, Operator(), H.dimensions) |
| 38 | + sec_cutoff = float(sec_cutoff) |
| 39 | + |
| 40 | + # in fock basis |
| 41 | + R0 = liouvillian(H, c_ops) |
| 42 | + |
| 43 | + # set fock_basis=Val(false) and change basis together at the end |
| 44 | + R1 = 0 |
| 45 | + if !isempty(a_ops) |
| 46 | + R1 += mapreduce(x -> _brterm(rst, x[1], x[2], sec_cutoff, Val(false)), +, a_ops) |
| 47 | + |
| 48 | + # do (a_op, spectra) |
| 49 | + # _brterm(rst, a_op, spectra, sec_cutoff, Val(false)) |
| 50 | + # end |
| 51 | + end |
| 52 | + |
| 53 | + SU = sprepost(U, U') # transformation matrix from eigen basis back to fock basis |
| 54 | + if getVal(fock_basis) |
| 55 | + return R0 + SU * R1 * SU' |
| 56 | + else |
| 57 | + return SU' * R0 * SU + R1, U |
| 58 | + end |
| 59 | +end |
| 60 | + |
| 61 | + |
| 62 | +function brcrossterm( |
| 63 | + H::T, a_op::T, b_op::T, spectra::F; sec_cutoff::Real=0.1, |
| 64 | + fock_basis::Bool=false |
| 65 | + ) where {T<:QuantumObject{Operator}, F<:Function} |
| 66 | + return brcrossterm(H, a_op, b_op, spectra, sec_cutoff, makeVal(fock_basis)) |
| 67 | +end |
| 68 | + |
| 69 | +function brcrossterm( |
| 70 | + H::T, a_op::T, b_op::T, spectra::F, sec_cutoff::Real, fock_basis::Val{true} |
| 71 | + ) where {T<:QuantumObject{Operator}, F<:Function} |
| 72 | + rst = eigenstates(H) |
| 73 | + return _brcrossterm(rst, a_op, b_op, spectra, sec_cutoff, Val(true)) |
| 74 | +end |
| 75 | + |
| 76 | +function brcrossterm( |
| 77 | + H::T, a_op::T, b_op::T, spectra::F, sec_cutoff::Real, fock_basis::Val{false} |
| 78 | + ) where {T<:QuantumObject{Operator}, F<:Function} |
| 79 | + rst = eigenstates(H) |
| 80 | + return _brcrossterm(rst, a_op, b_op, spectra, sec_cutoff, Val(false)), Qobj(rst.vectors, Operator(), rst.dimensions) |
| 81 | +end |
| 82 | + |
| 83 | +function _brcrossterm( |
| 84 | + rst::EigsolveResult, |
| 85 | + a_op::T, |
| 86 | + b_op::T, |
| 87 | + spectra::F, |
| 88 | + sec_cutoff::Real, |
| 89 | + fock_basis::Union{Val{true},Val{false}} |
| 90 | + ) where {T<:QuantumObject{Operator},F<:Function} |
| 91 | + |
| 92 | + _check_br_spectra(spectra) |
| 93 | + |
| 94 | + cutoff = (sec_cutoff == -1) ? Inf : sec_cutoff |> float |
| 95 | + U, N = rst.vectors, length(rst.values) |
| 96 | + |
| 97 | + skew = @. rst.values - rst.values' |> real |
| 98 | + spectrum = spectra.(skew) |
| 99 | + |
| 100 | + A_mat = U' * a_op.data * U |
| 101 | + B_mat = U' * b_op.data * U |
| 102 | + |
| 103 | + m_cut = similar(A_mat) |
| 104 | + map!(x -> abs(x) < cutoff, m_cut, skew) |
| 105 | + |
| 106 | + ac_term = (A_mat .* spectrum) * B_mat |
| 107 | + bd_term = A_mat * (B_mat .* trans(spectrum)) |
| 108 | + ac_term .*= m_cut |
| 109 | + bd_term .*= m_cut |
| 110 | + |
| 111 | + Id = I(N) |
| 112 | + vec_skew = vec(skew) |
| 113 | + M_cut = @. abs(vec_skew - vec_skew') < cutoff |
| 114 | + |
| 115 | + out = 1/2 * ( |
| 116 | + + QuantumToolbox._sprepost(B_mat .* trans(spectrum), A_mat) |
| 117 | + + QuantumToolbox._sprepost(B_mat, A_mat .* spectrum) |
| 118 | + - _spost(ac_term, Id) |
| 119 | + - QuantumToolbox._spre(bd_term, Id) |
| 120 | + ) .* M_cut |
| 121 | + |
| 122 | + if getVal(fock_basis) |
| 123 | + return QuantumObject(_sprepost(U, U') * out * _sprepost(U', U), SuperOperator(), rst.dimensions) |
| 124 | + else |
| 125 | + return QuantumObject(out, SuperOperator(), rst.dimensions) |
| 126 | + end |
| 127 | +end |
| 128 | + |
| 129 | +@doc raw""" |
| 130 | + brterm( |
| 131 | + H::QuantumObject{Operator}, |
| 132 | + a_op::QuantumObject{Operator}, |
| 133 | + spectra::Function; |
| 134 | + sec_cutoff::Real=0.1, |
| 135 | + fock_basis::Union{Bool, Val}=Val(false) |
| 136 | + ) |
| 137 | +
|
| 138 | +Calculates the contribution of one coupling operator to the Bloch-Redfield tensor. |
| 139 | +
|
| 140 | +## Argument |
| 141 | +
|
| 142 | +- `H`: The system Hamiltonian. Must be an [`Operator`](@ref) |
| 143 | +- `a_op`: The operator coupling to the environment. Must be hermitian. |
| 144 | +- `spectra`: The corresponding environment spectra as a `Function` of transition energy. |
| 145 | +- `sec_cutoff`: Cutoff for secular approximation. Use `-1` if secular approximation is not used when evaluating bath-coupling terms. |
| 146 | +- `fock_basis`: Whether to return the tensor in the input (fock) basis or the diagonalized (eigen) basis. |
| 147 | +
|
| 148 | +## Return |
| 149 | +
|
| 150 | +The return depends on `fock_basis`. |
| 151 | +
|
| 152 | +- `fock_basis=Val(true)`: return the Bloch-Redfield term (in the fock basis) only. |
| 153 | +- `fock_basis=Val(false)`: return the Bloch-Redfield term (in the eigen basis) along with the transformation matrix from eigen to fock basis. |
| 154 | +""" |
| 155 | +function brterm( |
| 156 | + H::QuantumObject{Operator}, |
| 157 | + a_op::QuantumObject{Operator}, |
| 158 | + spectra::Function; |
| 159 | + sec_cutoff::Real=0.1, |
| 160 | + fock_basis::Union{Bool, Val}=Val(false) |
| 161 | + ) |
| 162 | + rst = eigenstates(H) |
| 163 | + term = _brterm(rst, a_op, spectra, sec_cutoff, makeVal(fock_basis)) |
| 164 | + if getVal(fock_basis) |
| 165 | + return term |
| 166 | + else |
| 167 | + return term, Qobj(rst.vectors, Operator(), rst.dimensions) |
| 168 | + end |
| 169 | +end |
| 170 | + |
| 171 | +function _brterm( |
| 172 | + rst::EigsolveResult, |
| 173 | + a_op::T, |
| 174 | + spectra::F, |
| 175 | + sec_cutoff::Real, |
| 176 | + fock_basis::Union{Val{true},Val{false}} |
| 177 | + ) where {T<:QuantumObject{Operator},F<:Function} |
| 178 | + |
| 179 | + _check_br_spectra(spectra) |
| 180 | + |
| 181 | + U, N = rst.vectors, prod(rst.dimensions) |
| 182 | + |
| 183 | + skew = @. rst.values - rst.values' |> real |
| 184 | + spectrum = spectra.(skew) |
| 185 | + |
| 186 | + A_mat = U' * a_op.data * U |
| 187 | + |
| 188 | + ac_term = (A_mat .* spectrum) * A_mat |
| 189 | + bd_term = A_mat * (A_mat .* trans(spectrum)) |
| 190 | + |
| 191 | + if sec_cutoff != -1 |
| 192 | + m_cut = similar(skew) |
| 193 | + map!(x -> abs(x) < sec_cutoff, m_cut, skew) |
| 194 | + ac_term .*= m_cut |
| 195 | + bd_term .*= m_cut |
| 196 | + |
| 197 | + Id = I(N) |
| 198 | + vec_skew = vec(skew) |
| 199 | + M_cut = @. abs(vec_skew - vec_skew') < sec_cutoff |
| 200 | + end |
| 201 | + |
| 202 | + out = 0.5 * ( |
| 203 | + + QuantumToolbox._sprepost(A_mat .* trans(spectrum), A_mat) |
| 204 | + + QuantumToolbox._sprepost(A_mat, A_mat .* spectrum) |
| 205 | + - _spost(ac_term, Id) |
| 206 | + - QuantumToolbox._spre(bd_term, Id) |
| 207 | + ) |
| 208 | + |
| 209 | + (sec_cutoff != -1) && (out .*= M_cut) |
| 210 | + |
| 211 | + |
| 212 | + if getVal(fock_basis) |
| 213 | + SU = _sprepost(U, U') |
| 214 | + return QuantumObject(SU * out * SU', SuperOperator(), rst.dimensions) |
| 215 | + else |
| 216 | + return QuantumObject(out, SuperOperator(), rst.dimensions) |
| 217 | + end |
| 218 | +end |
| 219 | + |
| 220 | + |
| 221 | + |
| 222 | +@doc raw""" |
| 223 | + brmesolve( |
| 224 | + H::QuantumObject{Operator}, |
| 225 | + ψ0::QuantumObject, |
| 226 | + tlist::AbstractVector, |
| 227 | + a_ops::Union{Nothing, AbstractVector, Tuple}, |
| 228 | + c_ops::Union{Nothing, AbstractVector, Tuple}=nothing; |
| 229 | + sec_cutoff::Real=0.1, |
| 230 | + e_ops::Union{Nothing, AbstractVector}=nothing, |
| 231 | + kwargs..., |
| 232 | + ) |
| 233 | +
|
| 234 | +Solves for the dynamics of a system using the Bloch-Redfield master equation, given an input Hamiltonian, Hermitian bath-coupling terms and their associated spectral functions, as well as possible Lindblad collapse operators. |
| 235 | +
|
| 236 | +## Arguments |
| 237 | +
|
| 238 | +- `H`: The system Hamiltonian. Must be an [`Operator`](@ref) |
| 239 | +- `ψ0`: Initial state of the system $|\psi(0)\rangle$. It can be either a [`Ket`](@ref), [`Operator`](@ref) or [`OperatorKet`](@ref). |
| 240 | +- `tlist`: List of times at which to save either the state or the expectation values of the system. |
| 241 | +- `a_ops`: Nested list with each element is a `Tuple` of operator-function pairs `(a_op, spectra)`, and the coupling [`Operator`](@ref) `a_op` must be hermitian with corresponding `spectra` being a `Function` of transition energy |
| 242 | +- `c_ops`: List of collapse operators corresponding to Lindblad dissipators |
| 243 | +- `sec_cutoff`: Cutoff for secular approximation. Use `-1` if secular approximation is not used when evaluating bath-coupling terms. |
| 244 | +- `e_ops`: List of operators for which to calculate expectation values. It can be either a `Vector` or a `Tuple`. |
| 245 | +- `kwargs`: Keyword arguments for [`mesolve`](@ref). |
| 246 | +
|
| 247 | +## Notes |
| 248 | +
|
| 249 | +- This function will automatically generate the [`bloch_redfield_tensor`](@ref) and solve the time evolution with [`mesolve`](@ref). |
| 250 | +
|
| 251 | +# Returns |
| 252 | +
|
| 253 | +- `sol::TimeEvolutionSol`: The solution of the time evolution. See also [`TimeEvolutionSol`](@ref) |
| 254 | +""" |
| 255 | +function brmesolve( |
| 256 | + H::QuantumObject{Operator}, |
| 257 | + ψ0::QuantumObject, |
| 258 | + tlist::AbstractVector, |
| 259 | + a_ops::Union{Nothing, AbstractVector, Tuple}, |
| 260 | + c_ops::Union{Nothing, AbstractVector, Tuple}=nothing; |
| 261 | + sec_cutoff::Real=0.1, |
| 262 | + e_ops::Union{Nothing, AbstractVector}=nothing, |
| 263 | + kwargs..., |
| 264 | + ) |
| 265 | + |
| 266 | + R = bloch_redfield_tensor(H, a_ops, c_ops; sec_cutoff=sec_cutoff, fock_basis=Val(true)) |
| 267 | + |
| 268 | + return mesolve(R, ψ0, tlist, nothing; e_ops=e_ops, kwargs...) |
| 269 | +end |
| 270 | + |
| 271 | +function _check_br_spectra(f::Function) |
| 272 | + meths = methods(f, [Real]) |
| 273 | + length(meths.ms) == 0 && throw( |
| 274 | + ArgumentError("The following function must accept one argument: `$(meths.mt.name)(ω)` with ω<:Real") |
| 275 | + ) |
| 276 | + return nothing |
| 277 | +end |
0 commit comments