|
| 1 | +module QuantumOpticsBaseSciMLOperatorsExt |
| 2 | + |
| 3 | +import Base: +, -, *, /, copy, eltype, size, transpose |
| 4 | +import LinearAlgebra: adjoint, mul! |
| 5 | +import SparseArrays |
| 6 | +import QuantumOpticsBase |
| 7 | +import QuantumOpticsBase: Bra, Ket |
| 8 | +import SciMLOperators |
| 9 | + |
| 10 | +const QOB = QuantumOpticsBase |
| 11 | + |
| 12 | +struct SciMLLazyOperator{BL,BR,O} <: QOB.LazyOperator{BL,BR} |
| 13 | + basis_l::BL |
| 14 | + basis_r::BR |
| 15 | + operator::O |
| 16 | + |
| 17 | + function SciMLLazyOperator(basis_l::BL, basis_r::BR, operator::O) where {BL,BR,O} |
| 18 | + expected = (length(basis_l), length(basis_r)) |
| 19 | + size(operator) == expected || throw(DimensionMismatch("SciML operator has size $(size(operator)), expected $expected from bases.")) |
| 20 | + return new{BL,BR,O}(basis_l, basis_r, operator) |
| 21 | + end |
| 22 | +end |
| 23 | + |
| 24 | +copy(op::SciMLLazyOperator) = SciMLLazyOperator(op.basis_l, op.basis_r, copy(op.operator)) |
| 25 | +eltype(op::SciMLLazyOperator) = eltype(op.operator) |
| 26 | +size(op::SciMLLazyOperator) = (length(op.basis_l), length(op.basis_r)) |
| 27 | +size(op::SciMLLazyOperator, dim::Integer) = size(op)[dim] |
| 28 | + |
| 29 | +function _check_same_bases(a, b) |
| 30 | + a.basis_l == b.basis_l && a.basis_r == b.basis_r || throw(QOB.IncompatibleBases()) |
| 31 | + return nothing |
| 32 | +end |
| 33 | + |
| 34 | +function _check_multiplicable(a, b) |
| 35 | + a.basis_r == b.basis_l || throw(QOB.IncompatibleBases()) |
| 36 | + return nothing |
| 37 | +end |
| 38 | + |
| 39 | +_wrap(basis_l, basis_r, operator) = SciMLLazyOperator(basis_l, basis_r, operator) |
| 40 | + |
| 41 | +function _operator_number_type(op) |
| 42 | + T = eltype(op) |
| 43 | + return T <: Number ? T : ComplexF64 |
| 44 | +end |
| 45 | + |
| 46 | +function _matrix_identity(::Type{T}, basis_l, basis_r) where {T} |
| 47 | + if length(basis_l) == length(basis_r) |
| 48 | + return SciMLOperators.IdentityOperator(length(basis_l)) |
| 49 | + end |
| 50 | + return SciMLOperators.MatrixOperator(QOB.identityoperator(T, basis_l, basis_r).data) |
| 51 | +end |
| 52 | + |
| 53 | +_sciml_operator(op::SciMLLazyOperator) = op.operator |
| 54 | +_sciml_operator(op::QOB.DataOperator) = SciMLOperators.MatrixOperator(op.data) |
| 55 | + |
| 56 | +function _sciml_operator(op::QOB.LazySum) |
| 57 | + if isempty(op.operators) |
| 58 | + return SciMLOperators.NullOperator(length(op.basis_l), length(op.basis_r)) |
| 59 | + end |
| 60 | + |
| 61 | + result = op.factors[1] * _sciml_operator(op.operators[1]) |
| 62 | + for i in 2:length(op.operators) |
| 63 | + result = result + op.factors[i] * _sciml_operator(op.operators[i]) |
| 64 | + end |
| 65 | + return result |
| 66 | +end |
| 67 | + |
| 68 | +function _combined_lazytensor_product(op::QOB.LazyProduct) |
| 69 | + length(op.operators) == 2 || return nothing |
| 70 | + all(operator -> operator isa QOB.LazyTensor, op.operators) || return nothing |
| 71 | + |
| 72 | + result = op.operators[1] |
| 73 | + for i in 2:length(op.operators) |
| 74 | + result = result * op.operators[i] |
| 75 | + result isa QOB.LazyTensor || return nothing |
| 76 | + end |
| 77 | + return result |
| 78 | +end |
| 79 | + |
| 80 | +function _sciml_operator(op::QOB.LazyProduct) |
| 81 | + combined = _combined_lazytensor_product(op) |
| 82 | + combined === nothing || return op.factor * _sciml_operator(combined) |
| 83 | + |
| 84 | + result = _sciml_operator(op.operators[1]) |
| 85 | + for i in 2:length(op.operators) |
| 86 | + result = result * _sciml_operator(op.operators[i]) |
| 87 | + end |
| 88 | + return op.factor * result |
| 89 | +end |
| 90 | + |
| 91 | +function _sciml_operator(op::QOB.LazyTensor) |
| 92 | + T = _operator_number_type(op) |
| 93 | + factors = map(1:length(op.basis_l.bases)) do index |
| 94 | + op_index = findfirst(isequal(index), op.indices) |
| 95 | + if op_index === nothing |
| 96 | + return _matrix_identity(T, op.basis_l.bases[index], op.basis_r.bases[index]) |
| 97 | + end |
| 98 | + return _sciml_operator(op.operators[op_index]) |
| 99 | + end |
| 100 | + |
| 101 | + # QuantumOpticsBase stores tensor data as kron(right, left), which matches |
| 102 | + # SciMLOperators when the factor order is reversed. |
| 103 | + tensor_operator = SciMLOperators.TensorProductOperator(reverse(factors)...) |
| 104 | + return op.factor * tensor_operator |
| 105 | +end |
| 106 | + |
| 107 | +function _sciml_operator(op::QOB.AbstractOperator) |
| 108 | + return SciMLOperators.MatrixOperator(QOB.dense(op).data) |
| 109 | +end |
| 110 | + |
| 111 | +function QOB.sciml_lazy_operator(op::QOB.AbstractOperator; cache=nothing) |
| 112 | + wrapped = _wrap(op.basis_l, op.basis_r, _sciml_operator(op)) |
| 113 | + cache === nothing && return wrapped |
| 114 | + return QOB.cache_sciml_lazy_operator(wrapped, cache) |
| 115 | +end |
| 116 | + |
| 117 | +function QOB.sciml_lazy_operator(op::SciMLLazyOperator; cache=nothing) |
| 118 | + cache === nothing && return op |
| 119 | + return QOB.cache_sciml_lazy_operator(op, cache) |
| 120 | +end |
| 121 | + |
| 122 | +function QOB.cache_sciml_lazy_operator(op::SciMLLazyOperator, state::Ket) |
| 123 | + return QOB.cache_sciml_lazy_operator(op, state.data) |
| 124 | +end |
| 125 | + |
| 126 | +function QOB.cache_sciml_lazy_operator(op::SciMLLazyOperator, data::AbstractVecOrMat) |
| 127 | + return _wrap(op.basis_l, op.basis_r, SciMLOperators.cache_operator(op.operator, data)) |
| 128 | +end |
| 129 | + |
| 130 | +function QOB.cache_sciml_lazy_operator(op::QOB.AbstractOperator, state) |
| 131 | + return QOB.cache_sciml_lazy_operator(QOB.sciml_lazy_operator(op), state) |
| 132 | +end |
| 133 | + |
| 134 | +function QOB.dense(op::SciMLLazyOperator) |
| 135 | + return QOB.DenseOperator(op.basis_l, op.basis_r, Matrix(op.operator)) |
| 136 | +end |
| 137 | + |
| 138 | +function SparseArrays.sparse(op::SciMLLazyOperator) |
| 139 | + return QOB.SparseOperator(op.basis_l, op.basis_r, SparseArrays.sparse(Matrix(op.operator))) |
| 140 | +end |
| 141 | + |
| 142 | +function -(op::SciMLLazyOperator) |
| 143 | + return _wrap(op.basis_l, op.basis_r, -op.operator) |
| 144 | +end |
| 145 | + |
| 146 | +function +(a::SciMLLazyOperator, b::SciMLLazyOperator) |
| 147 | + _check_same_bases(a, b) |
| 148 | + return _wrap(a.basis_l, a.basis_r, a.operator + b.operator) |
| 149 | +end |
| 150 | + |
| 151 | +function -(a::SciMLLazyOperator, b::SciMLLazyOperator) |
| 152 | + _check_same_bases(a, b) |
| 153 | + return _wrap(a.basis_l, a.basis_r, a.operator - b.operator) |
| 154 | +end |
| 155 | + |
| 156 | +function +(a::SciMLLazyOperator, b::QOB.AbstractOperator) |
| 157 | + return a + QOB.sciml_lazy_operator(b) |
| 158 | +end |
| 159 | + |
| 160 | +function +(a::QOB.AbstractOperator, b::SciMLLazyOperator) |
| 161 | + return QOB.sciml_lazy_operator(a) + b |
| 162 | +end |
| 163 | + |
| 164 | +function -(a::SciMLLazyOperator, b::QOB.AbstractOperator) |
| 165 | + return a - QOB.sciml_lazy_operator(b) |
| 166 | +end |
| 167 | + |
| 168 | +function -(a::QOB.AbstractOperator, b::SciMLLazyOperator) |
| 169 | + return QOB.sciml_lazy_operator(a) - b |
| 170 | +end |
| 171 | + |
| 172 | +function *(a::SciMLLazyOperator, b::SciMLLazyOperator) |
| 173 | + _check_multiplicable(a, b) |
| 174 | + return _wrap(a.basis_l, b.basis_r, a.operator * b.operator) |
| 175 | +end |
| 176 | + |
| 177 | +function *(a::SciMLLazyOperator{B1,B2}, b::QOB.Operator{B2,B3,T}) where {B1,B2,B3,T} |
| 178 | + return a * QOB.sciml_lazy_operator(b) |
| 179 | +end |
| 180 | + |
| 181 | +function *(a::QOB.Operator{B1,B2,T}, b::SciMLLazyOperator{B2,B3}) where {B1,B2,B3,T} |
| 182 | + return QOB.sciml_lazy_operator(a) * b |
| 183 | +end |
| 184 | + |
| 185 | +function *(a::SciMLLazyOperator, b::QOB.AbstractOperator) |
| 186 | + return a * QOB.sciml_lazy_operator(b) |
| 187 | +end |
| 188 | + |
| 189 | +function *(a::QOB.AbstractOperator, b::SciMLLazyOperator) |
| 190 | + return QOB.sciml_lazy_operator(a) * b |
| 191 | +end |
| 192 | + |
| 193 | +*(a::Number, b::SciMLLazyOperator) = _wrap(b.basis_l, b.basis_r, a * b.operator) |
| 194 | +*(a::SciMLLazyOperator, b::Number) = _wrap(a.basis_l, a.basis_r, a.operator * b) |
| 195 | +/(a::SciMLLazyOperator, b::Number) = _wrap(a.basis_l, a.basis_r, a.operator / b) |
| 196 | + |
| 197 | +function QOB.dagger(op::SciMLLazyOperator) |
| 198 | + return _wrap(op.basis_r, op.basis_l, adjoint(op.operator)) |
| 199 | +end |
| 200 | + |
| 201 | +function transpose(op::SciMLLazyOperator) |
| 202 | + return _wrap(op.basis_r, op.basis_l, transpose(op.operator)) |
| 203 | +end |
| 204 | + |
| 205 | +function _operator_for_mul(op, data) |
| 206 | + SciMLOperators.iscached(op) && return op |
| 207 | + return SciMLOperators.cache_operator(op, data) |
| 208 | +end |
| 209 | + |
| 210 | +function mul!(result::Ket{B1}, op::SciMLLazyOperator{B1,B2}, state::Ket{B2}, alpha, beta) where {B1,B2} |
| 211 | + sciml_op = _operator_for_mul(op.operator, state.data) |
| 212 | + if isone(alpha) && iszero(beta) |
| 213 | + mul!(result.data, sciml_op, state.data) |
| 214 | + return result |
| 215 | + end |
| 216 | + mul!(result.data, sciml_op, state.data, alpha, beta) |
| 217 | + return result |
| 218 | +end |
| 219 | + |
| 220 | +function mul!(result::Bra{B2}, state::Bra{B1}, op::SciMLLazyOperator{B1,B2}, alpha, beta) where {B1,B2} |
| 221 | + sciml_op = _operator_for_mul(transpose(op.operator), state.data) |
| 222 | + if isone(alpha) && iszero(beta) |
| 223 | + mul!(result.data, sciml_op, state.data) |
| 224 | + return result |
| 225 | + end |
| 226 | + mul!(result.data, sciml_op, state.data, alpha, beta) |
| 227 | + return result |
| 228 | +end |
| 229 | + |
| 230 | +end # module |
0 commit comments