Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/SLH.jl
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,69 @@ Return the Hamiltonian `H` of an SLH object.
hamiltonian(G::SLH) = G.hamiltonian


# ──────────────────────────────────────────────
# Display
# ──────────────────────────────────────────────

# `S`, `L` and `H` hold expressions that grow without bound, so they are described
# rather than printed; the accessors give the operators themselves.

_is_symbolic(x) = x isa SQA.QField || x isa BasicSymbolic || x isa Symbolics.Num
_is_symbolic(x::Complex) = _is_symbolic(real(x)) || _is_symbolic(imag(x))

function _is_identity(S::AbstractMatrix)
for j in axes(S, 2), i in axes(S, 1)
x = S[i, j]
_is_time_dep(x) && return false
(i == j ? _isunit(x) : iszero(x)) || return false
end
return true
end

_nterms(x) = x isa SQA.QAdd && hasproperty(x, :arguments) ? length(x.arguments) : 1

_plural(n::Int, noun::AbstractString) = string(n, " ", noun, n == 1 ? "" : "s")

_dims(x) = string(size(x, 1), "×", size(x, 2))

function _describe(x)
_is_time_dep(x) && return "time-dependent"
iszero(x) && return "0"
x isa SQA.QField && return string("symbolic, ", _plural(_nterms(x), "term"))
x isa QuantumOpticsBase.AbstractOperator && return string("numeric, ", _dims(x))
_is_symbolic(x) && return "symbolic"
return sprint(show, x) # a plain number describes itself
end

function _describe_scattering(S::AbstractMatrix)
_is_identity(S) && return string(_dims(S), " identity")
any(_is_time_dep, S) && return string(_dims(S), " time-dependent")
return string(_dims(S), " ", _is_symbolic(first(S)) ? "symbolic" : string(eltype(S)))
end

function _describe_lindblad(L::AbstractVector)
n = length(L)
any(_is_time_dep, L) && return _plural(n, "time-dependent jump operator")
x = first(L)
numeric = x isa QuantumOpticsBase.AbstractOperator
adjective = numeric ? "numeric " : _is_symbolic(x) ? "symbolic " : ""
desc = _plural(n, adjective * "jump operator")
notes = numeric ? [_dims(x)] : String[]
nzero = count(iszero, L)
nzero == 0 || push!(notes, nzero == n ? "all zero" : string(nzero, " zero"))
return isempty(notes) ? desc : string(desc, " (", join(notes, ", "), ")")
end

Base.show(io::IO, ::SLH{N}) where {N} =
print(io, "SLH{", N, "} with ", N, N == 1 ? " port" : " ports")

function Base.show(io::IO, ::MIME"text/plain", G::SLH)
show(io, G)
print(io, "\n S = ", _describe_scattering(scattering(G)))
print(io, "\n L = ", _describe_lindblad(lindblad(G)))
return print(io, "\n H = ", _describe(hamiltonian(G)))
end

# ──────────────────────────────────────────────
# Equality
# ──────────────────────────────────────────────
Expand Down
53 changes: 53 additions & 0 deletions test/test_SLH.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,59 @@ using Test
@test iszero(simplify(lindblad(G_cas)[2] - (gv2' * av2)))
end

@testset "show" begin
@test sprint(show, G_u) == "SLH{1} with 1 port"
@test sprint(show, MIME("text/plain"), G_u) ==
"SLH{1} with 1 port\n" *
" S = 1×1 identity\n" *
" L = 1 symbolic jump operator\n" *
" H = 0"

@test sprint(show, MIME("text/plain"), G_u ⊞ G_c) ==
"SLH{2} with 2 ports\n" *
" S = 2×2 identity\n" *
" L = 2 symbolic jump operators\n" *
" H = symbolic, 1 term"

# no equation ever reaches the output, whatever the system size
M = 6
@variables s[1:M, 1:M]::Complex
ops = [gu * au, √(γ) * c, gv * av, gu * au * c' * av, c' * c * av, au * av' * c]
G_big = SLH([s[i, j] for i = 1:M, j = 1:M], ops, sum(ops[i]' * ops[i] for i = 1:M))
@test sprint(show, MIME("text/plain"), G_big) ==
"SLH{6} with 6 ports\n" *
" S = 6×6 symbolic\n" *
" L = 6 symbolic jump operators\n" *
" H = symbolic, 8 terms"

# passive component: nothing couples to the ports
@variables r::Real τ::Real
@test sprint(show, MIME("text/plain"), SLH([r τ; τ -r], [0, 0], 0)) ==
"SLH{2} with 2 ports\n" *
" S = 2×2 symbolic\n" *
" L = 2 jump operators (all zero)\n" *
" H = 0"

L_f = FunctionWrapper{typeof(gu' * au),Tuple{Float64}}(t -> gu' * au)
@test sprint(show, MIME("text/plain"), SLH(1, L_f, 0 * au)) ==
"SLH{1} with 1 port\n" *
" S = 1×1 identity\n" *
" L = 1 time-dependent jump operator\n" *
" H = time-dependent"

bc = FockBasis(4)
a_op = destroy(bc)
@test sprint(
show,
MIME("text/plain"),
SLH(1, sparse(a_op), sparse(dagger(a_op) * a_op)),
) ==
"SLH{1} with 1 port\n" *
" S = 1×1 identity\n" *
" L = 1 numeric jump operator (5×5)\n" *
" H = numeric, 5×5"
end

@testset "numeric type stability" begin
bc = FockBasis(4)
a_op = destroy(bc)
Expand Down
Loading