-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Errow when showing operators containing symbolic variables #184
Comments
Hi, Matias! I am surprised that any of this actually works. I expect there will be unexpected breakages in many places. Making the printing more robust would of course be good, but there probably will be more issues that pop up if this is used. I think there are a few action items here, of increasing cost in time and effort. Listing them here just to keep it logged (not sure anyone will be tackling them near term):
Tagging @apkille as he has done a lot of work on QuantumSymbolics |
That's really cool that it works. I remember talking to @jgr-rgb at QNumerics about implementing such a feature for QuantumSymbolics. Perhaps he has some thoughts on this? I don't have any great immediate use cases, but I'll give some thought to it over the next few days. Perhaps being able to calculate symbolic expressions for metrics of states might be useful. This is less on the state-vector side and more for something like Gabs.jl, but analytical calculations involving covariance matrices of two-mode Gaussian bosonic systems tend to be pretty useful (see e.g. this paper and this paper for lots of discussion). Or, maybe using symbolic matrix representations of spin systems for studying like the Ising model or NMR experiments is somehow worthwhile. Regarding the printing, I'm linking #45 as it's related. In case anyone who's reading this isn't aware, it might be tricky to deal with the printing problem without messing with function Base.show(io::IO, mime::MIME"text/plain", x::DenseOpType)
summary(io, x)
print(io, "\n")
Base.show(io, mime, x.data)
end gives julia> α * dagger(a) * a
Operator(dim=3x3)
basis: Fock(cutoff=2)
3×3 Matrix{Complex{Num}}:
0.0 0.0 0.0
0.0 α 0.0
0.0 0.0 2.0α but that removes the feature of modifying REPL output with QO types. |
Wow, thanks for the input! I was not aware of QuantumSymbolics.jl and will check it out. As for use-cases of symbolic quantum operators, I have been playing around with symbolically deriving the equations of motion of a master equation and translating the resulting equations into an ODE (either with custom function or using modeling toolkits). This serves two purposes: First of all, auto-diff-related things basically work out of the box. Secondly, the function that returns the differential # 1. Define symbolic parameters for the system
using QuantumOptics, ModelingToolkit, Symbolics
using ModelingToolkit: t_nounits as t, D_nounits as D
@parameters ω γ # ω is the frequency, γ is the dissipation rate
# Pauli lowering and raising operators
basis = NLevelBasis(2)
bc = FockBasis(1)
σ₋ = destroy(bc)
σ₊ = create(bc)
# Hamiltonian for the Jaynes-Cummings model (symbolic ω)
H = ω * (σ₊ + σ₋); #Define function that returns a symbolic representation of the master equation
function me_symbolic(t,H, J,Jdagger=dagger.(J))
ρ = symbolic_density(H,t);
# Define the commutator [H, ρ] and the Lindblad terms (symbolic γ)
H_comm = H * ρ - ρ * H;
# Lindblad dissipator term: γ (σ₋ρσ₊ - 0.5 * (σ₊σ₋ρ + ρσ₊σ₋))
master_eq = -1im * H_comm;
for (i,j) in enumerate(J)
master_eq = master_eq + ( j* ρ * Jdagger[i] - 0.5 * (Jdagger[i] * j * ρ + ρ * Jdagger[i] * j));
end
return ρ ,master_eq
end
ρ_symbolic ,master_eq = me_symbolic(t,H, [γ * σ₋],[γ * σ₊]); Now printing julia> master_eq.data
2×2 Matrix{Complex{Num}}:
-2ρ₁ˏ₂_im(t)*ω + ρ₂ˏ₂_re(t)*(γ^2) -0.5ρ₁ˏ₂_re(t)*(γ^2) + im*(-ρ₂ˏ₂_re(t)*ω + ρ₁ˏ₁_re(t)*ω - 0.5ρ₁ˏ₂_im(t)*(γ^2))
-0.5ρ₁ˏ₂_re(t)*(γ^2) + im*(ρ₂ˏ₂_re(t)*ω - ρ₁ˏ₁_re(t)*ω + 0.5ρ₁ˏ₂_im(t)*(γ^2)) Thus, we now have the master equation equations of motion. The density matrix elements are generated using: Here #The following are functions that help construct the density matrix as a symbolic matrix.
function variable_complex(name,t, idx...)
if idx[1] == idx[2]
name_ij_re = Symbol(name, join(Symbolics.map_subscripts.(idx), "ˏ"), "_re")
vre = Sym{Symbolics.FnType{Tuple{Real}, Real}}(name_ij_re)(t)
return Complex(Num(setmetadata(vre, Symbolics.VariableSource, (:variables, name_ij_re))))
elseif idx[1]<idx[2]
name_ij_re = Symbol(name, join(Symbolics.map_subscripts.(idx), "ˏ"), "_re")
name_ij_im = Symbol(name, join(Symbolics.map_subscripts.(idx), "ˏ"), "_im")
vre = Sym{Symbolics.FnType{Tuple{Real}, Real}}(name_ij_re)(t)
vim = Sym{Symbolics.FnType{Tuple{Real}, Real}}(name_ij_im)(t)
return Complex(Num(setmetadata(vre, Symbolics.VariableSource, (:variables, name_ij_re))), Num(setmetadata(vim, Symbolics.VariableSource, (:variables, name_ij_im))))
else
name_ij_re = Symbol(name, join(Symbolics.map_subscripts.(reverse(idx)), "ˏ"), "_re")
name_ij_im = Symbol(name, join(Symbolics.map_subscripts.(reverse(idx)), "ˏ"), "_im")
vre = Sym{Symbolics.FnType{Tuple{Real}, Real}}(name_ij_re)(t)
vim = Sym{Symbolics.FnType{Tuple{Real}, Real}}(name_ij_im)(t)
return Complex(Num(setmetadata(vre, Symbolics.VariableSource, (:variables, name_ij_re))), -Num(setmetadata(vim, Symbolics.VariableSource, (:variables, name_ij_im))))
end
end
function variables_complex(name,t, indices...)
[variable_complex(name,t, ij...) for ij in Iterators.product(indices...)]
end
function symbolic_density(ρ,t)
dims = size(ρ)
Operator(ρ.basis_l,variables_complex(:ρ,t,1:dims[1],1:dims[2]))
end We can convert the master equations of motion to a modeling toolkits ODE using: #Function that returns the master equation as a set of equations compatible with modelling toolkit
function convert_to_ode(ρ,master_eq)
lhs_diag = zeros(Complex{Num}, size(ρ.data, 1))
rhs_diag = zeros(Complex{Num}, size(ρ.data, 1))
lhs_real = zeros(Complex{Num}, size(ρ.data, 1)*(size(ρ.data, 2)-1)÷2)
rhs_real = zeros(Complex{Num}, size(ρ.data, 1)*(size(ρ.data, 2)-1)÷2)
lhs_imag = zeros(Complex{Num}, size(ρ.data, 1)*(size(ρ.data, 2)-1)÷2)
rhs_imag = zeros(Complex{Num}, size(ρ.data, 1)*(size(ρ.data, 2)-1)÷2)
for i in 1:size(ρ.data, 1) # Loop over rows
lhs_diag[i] = D(ρ.data[i, i].re)
rhs_diag[i] = master_eq.data[i, i].re
for j in i+1:size(ρ.data, 2) # Loop over columns
idx = ((j - 2) * (j-1)) ÷ 2 + i
lhs_real[idx] = D(ρ.data[i, j].re)
rhs_real[idx] = master_eq.data[i, j].re
lhs_imag[idx] = D(ρ.data[i, j].im)
rhs_imag[idx] = master_eq.data[i, j].im
end
end
ode_eqs = vcat(lhs_diag .~ rhs_diag,lhs_real .~ rhs_real,lhs_imag .~ rhs_imag)
return ode_eqs
end
eqs = convert_to_ode(ρ_symbolic,master_eq)
#Build model and solve
@mtkbuild model = ODESystem(eqs, t)
u0=[1.0,0,0,0]
prob = ODEProblem(model, u0, (0.0, 10.0),[1,1])
using DifferentialEquations
solve(prob) Whether this is actually a good approach, I am not sure. I have tested that autodiff works, and also, initial benchmarks show that this is faster than solving the system using the quantum operators. This is, however, only when everything has been compiled, which is VERY slow for large quantum systems (I have found that using custom code that splits the large odefunction up into smaller bits significantly lowers compile time while not suffering in performance). It seems a general problem with ModelingToolkits.jl when you have a large system of equations. |
When working with symbolic variables, they combine almost seamlessly with the quantum optics operators (nice!). But when printing the operator, an error occurs.
MWE:
Producing:
The error occurs due to the show function for dense operators:
where
round.(x.data; digits=machineprecorder)
throws an error when the data is symbolic.A similar error occurs for a non-dense operator:
giving:
The text was updated successfully, but these errors were encountered: