diff --git a/docs/src/api.md b/docs/src/api.md index 858e7590..ad2bf5de 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -8,11 +8,17 @@ CurrentModule = QuantumToolbox ```@docs BraQuantumObject +Bra KetQuantumObject +Ket OperatorQuantumObject +Operator OperatorBraQuantumObject +OperatorBra OperatorKetQuantumObject +OperatorKet SuperOperatorQuantumObject +SuperOperator QuantumObject Qobj ket2dm diff --git a/src/QuantumToolbox.jl b/src/QuantumToolbox.jl index 8fd4ef56..c05d8528 100644 --- a/src/QuantumToolbox.jl +++ b/src/QuantumToolbox.jl @@ -39,6 +39,7 @@ include("negativity.jl") include("progress_bar.jl") export QuantumObject, Qobj, BraQuantumObject, KetQuantumObject, OperatorQuantumObject, OperatorBraQuantumObject, OperatorKetQuantumObject, SuperOperatorQuantumObject, TimeEvolutionSol +export Bra, Ket, Operator, OperatorBra, OperatorKet, SuperOperator export isket, isbra, isoper, isoperbra, isoperket, issuper, ket2dm export spre, spost, sprepost, lindblad_dissipator export fock, basis, coherent diff --git a/src/general_functions.jl b/src/general_functions.jl index 5fdf12ac..d2bc93cd 100644 --- a/src/general_functions.jl +++ b/src/general_functions.jl @@ -113,9 +113,9 @@ end @doc raw""" vec2mat(A::QuantumObject) -Convert a quantum object from vector (`OperatorKetQuantumObject`-type) to matrix (`OperatorQuantumObject`-type) +Convert a quantum object from vector ([`OperatorKetQuantumObject`](@ref)-type) to matrix ([`OperatorQuantumObject`](@ref)-type) """ -vec2mat(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObject}) where {T} = QuantumObject(vec2mat(A.data), OperatorQuantumObject(), A.dims) +vec2mat(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObject}) where {T} = QuantumObject(vec2mat(A.data), Operator, A.dims) @doc raw""" gaussian(x::Number, μ::Number, σ::Number) @@ -404,6 +404,6 @@ end @doc raw""" mat2vec(A::QuantumObject) -Convert a quantum object from matrix (`OperatorQuantumObject`-type) to vector (`OperatorKetQuantumObject`-type) +Convert a quantum object from matrix ([`OperatorQuantumObject`](@ref)-type) to vector ([`OperatorKetQuantumObject`](@ref)-type) """ -mat2vec(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = QuantumObject(mat2vec(A.data), OperatorKetQuantumObject(), A.dims) \ No newline at end of file +mat2vec(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = QuantumObject(mat2vec(A.data), OperatorKet, A.dims) \ No newline at end of file diff --git a/src/negativity.jl b/src/negativity.jl index c112b038..3651d515 100644 --- a/src/negativity.jl +++ b/src/negativity.jl @@ -6,7 +6,7 @@ where ``\rho^{\Gamma}`` is the partial transpose of ``\rho`` with respect to the and ``\Vert X \Vert_1=\Tr\sqrt{X^\dagger X}`` is the trace norm. # Arguments -- `ρ::QuantumObject`: The density matrix (`ρ.type` must be `OperatorQuantumObject`). +- `ρ::QuantumObject`: The density matrix (`ρ.type` must be [`OperatorQuantumObject`](@ref)). - `subsys::Int`: an index that indicates which subsystem to compute the negativity for. - `logarithmic::Bool`: choose whether to calculate logarithmic negativity or not. Default as `false` @@ -60,7 +60,7 @@ end Return the partial transpose of a density matrix ``\rho``, where `mask` is an array/vector with length that equals the length of `ρ.dims`. The elements in `mask` are boolean (`true` or `false`) which indicates whether or not the corresponding subsystem should be transposed. # Arguments -- `ρ::QuantumObject`: The density matrix (`ρ.type` must be `OperatorQuantumObject`). +- `ρ::QuantumObject`: The density matrix (`ρ.type` must be [`OperatorQuantumObject`](@ref)). - `mask::Vector{Bool}`: A boolean vector selects which subsystems should be transposed. # Returns @@ -88,7 +88,7 @@ function _partial_transpose(ρ::QuantumObject{<:AbstractArray, OperatorQuantumOb ] return QuantumObject( reshape(PermutedDimsArray(reshape(ρ.data, (ρ.dims..., ρ.dims...)), pt_idx), size(ρ)), - OperatorQuantumObject(), + Operator, ρ.dims ) end @@ -130,7 +130,7 @@ function _partial_transpose(ρ::QuantumObject{<:AbstractSparseArray, OperatorQua return QuantumObject( sparse(I_pt, J_pt, V_pt, M, N), - OperatorQuantumObject(), + Operator, ρ.dims ) end \ No newline at end of file diff --git a/src/quantum_object.jl b/src/quantum_object.jl index daacb035..ddcb47b5 100644 --- a/src/quantum_object.jl +++ b/src/quantum_object.jl @@ -12,6 +12,13 @@ Constructor representing a bra state ``\bra{\psi}``. """ struct BraQuantumObject <: QuantumObjectType end +@doc raw""" + const Bra = BraQuantumObject() + +A constant representing the type of [`BraQuantumObject`](@ref) +""" +const Bra = BraQuantumObject() + @doc raw""" KetQuantumObject <: QuantumObjectType @@ -19,6 +26,13 @@ Constructor representing a ket state ``\ket{\psi}``. """ struct KetQuantumObject <: QuantumObjectType end +@doc raw""" + const Ket = KetQuantumObject() + +A constant representing the type of [`KetQuantumObject`](@ref) +""" +const Ket = KetQuantumObject() + @doc raw""" OperatorQuantumObject <: QuantumObjectType @@ -26,6 +40,13 @@ Constructor representing an operator ``\hat{O}``. """ struct OperatorQuantumObject <: QuantumObjectType end +@doc raw""" + const Operator = OperatorQuantumObject() + +A constant representing the type of [`OperatorQuantumObject`](@ref) +""" +const Operator = OperatorQuantumObject() + @doc raw""" SuperOperatorQuantumObject <: QuantumObjectType @@ -33,6 +54,13 @@ Constructor representing a super-operator ``\hat{\mathcal{O}}``. """ struct SuperOperatorQuantumObject <: QuantumObjectType end +@doc raw""" + const SuperOperator = OperatorQuantumObject() + +A constant representing the type of [`SuperOperatorQuantumObject`](@ref) +""" +const SuperOperator = SuperOperatorQuantumObject() + @doc raw""" OperatorBraQuantumObject <: QuantumObjectType @@ -40,6 +68,13 @@ Constructor representing a bra state in the super-operator formalism ``\langle\l """ struct OperatorBraQuantumObject <: QuantumObjectType end +@doc raw""" + const OperatorBra = OperatorBraQuantumObject() + +A constant representing the type of [`OperatorBraQuantumObject`](@ref) +""" +const OperatorBra = OperatorBraQuantumObject() + @doc raw""" OperatorKetQuantumObject <: QuantumObjectType @@ -47,6 +82,13 @@ Constructor representing a ket state in the super-operator formalism ``|\rho\ran """ struct OperatorKetQuantumObject <: QuantumObjectType end +@doc raw""" + const OperatorKet = OperatorKetQuantumObject() + +A constant representing the type of [`OperatorKetQuantumObject`](@ref) +""" +const OperatorKet = OperatorKetQuantumObject() + @doc raw""" mutable struct QuantumObject{MT<:AbstractArray,ObjType<:QuantumObjectType} data::MT @@ -91,11 +133,11 @@ function QuantumObject(A::AbstractArray{T, N}; type::ObjType=nothing, dims=nothi # decide QuantumObjectType from the size of A if type === nothing if Size[1] == Size[2] - type = OperatorQuantumObject() + type = Operator elseif Size[2] == 1 - type = KetQuantumObject() + type = Ket elseif Size[1] == 1 - type = BraQuantumObject() + type = Bra else throw(DomainError(Size, "The dimension of the array is not compatible with Quantum Object")) end @@ -332,17 +374,17 @@ end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject}, B::QuantumObject{<:AbstractArray{T2},KetQuantumObject}) where {T1,T2} A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) - QuantumObject(A.data * B.data, KetQuantumObject(), A.dims) + QuantumObject(A.data * B.data, Ket, A.dims) end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},BraQuantumObject}, B::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject}) where {T1,T2} A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) - QuantumObject(A.data * B.data, BraQuantumObject(), A.dims) + QuantumObject(A.data * B.data, Bra, A.dims) end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},KetQuantumObject}, B::QuantumObject{<:AbstractArray{T2},BraQuantumObject}) where {T1,T2} A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) - QuantumObject(A.data * B.data, OperatorQuantumObject(), A.dims) + QuantumObject(A.data * B.data, Operator, A.dims) end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},BraQuantumObject}, B::QuantumObject{<:AbstractArray{T2},KetQuantumObject}) where {T1,T2} @@ -352,7 +394,7 @@ end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},SuperOperatorQuantumObject}, B::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject}) where {T1,T2} A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) - QuantumObject(vec2mat(A.data * mat2vec(B.data)), OperatorQuantumObject(), A.dims) + QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator, A.dims) end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},OperatorBraQuantumObject}, B::QuantumObject{<:AbstractArray{T2},OperatorKetQuantumObject}) where {T1,T2} @@ -362,12 +404,12 @@ end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},SuperOperatorQuantumObject}, B::QuantumObject{<:AbstractArray{T2},OperatorKetQuantumObject}) where {T1,T2} A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) - QuantumObject(A.data * B.data, OperatorKetQuantumObject(), A.dims) + QuantumObject(A.data * B.data, OperatorKet, A.dims) end function LinearAlgebra.:(*)(A::QuantumObject{<:AbstractArray{T1},OperatorBraQuantumObject}, B::QuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject}) where {T1,T2} A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) - QuantumObject(A.data * B.data, OperatorBraQuantumObject(), A.dims) + QuantumObject(A.data * B.data, OperatorBra, A.dims) end LinearAlgebra.:(^)(A::QuantumObject{<:AbstractArray{T},OpType}, n::T1) where {T,T1<:Number,OpType<:QuantumObjectType} = @@ -388,13 +430,13 @@ LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpTy LinearAlgebra.transpose(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = QuantumObject(transpose(A.data), OpType(), A.dims) LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},KetQuantumObject}) where {T} = - QuantumObject(adjoint(A.data), BraQuantumObject(), A.dims) + QuantumObject(adjoint(A.data), Bra, A.dims) LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},BraQuantumObject}) where {T} = - QuantumObject(adjoint(A.data), KetQuantumObject(), A.dims) + QuantumObject(adjoint(A.data), Ket, A.dims) LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObject}) where {T} = - QuantumObject(adjoint(A.data), OperatorBraQuantumObject(), A.dims) + QuantumObject(adjoint(A.data), OperatorBra, A.dims) LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OperatorBraQuantumObject}) where {T} = - QuantumObject(adjoint(A.data), OperatorKetQuantumObject(), A.dims) + QuantumObject(adjoint(A.data), OperatorKet, A.dims) LinearAlgebra.inv(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = QuantumObject(sparse(inv(Matrix(A.data))), OpType(), A.dims) diff --git a/src/quantum_operators.jl b/src/quantum_operators.jl index b5ea835a..ea525366 100644 --- a/src/quantum_operators.jl +++ b/src/quantum_operators.jl @@ -11,7 +11,7 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr the same function is applied multiple times with a known Hilbert space dimension. """ spre(O::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, Id_cache=I(size(O,1))) where {T} = - QuantumObject(kron(Id_cache, O.data), SuperOperatorQuantumObject(), O.dims) + QuantumObject(kron(Id_cache, O.data), SuperOperator, O.dims) @doc raw""" spost(O::QuantumObject) @@ -26,7 +26,7 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr the same function is applied multiple times with a known Hilbert space dimension. """ spost(O::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, Id_cache=I(size(O,1))) where {T} = - QuantumObject(kron(sparse(transpose(sparse(O.data))), Id_cache), SuperOperatorQuantumObject(), O.dims) # TODO: fix the sparse conversion + QuantumObject(kron(sparse(transpose(sparse(O.data))), Id_cache), SuperOperator, O.dims) # TODO: fix the sparse conversion @doc raw""" sprepost(A::QuantumObject, B::QuantumObject) @@ -38,7 +38,7 @@ Since the density matrix is vectorized, this super-operator is always a matrix, obtained from ``\mathcal{O} \left(\hat{A}, \hat{B}\right) \boldsymbol{\cdot} = \text{spre}(A) * \text{spost}(B)``. """ sprepost(A::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject}, - B::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject}) where {T1,T2} = QuantumObject(kron(sparse(transpose(sparse(B.data))), A.data), SuperOperatorQuantumObject(), A.dims) # TODO: fix the sparse conversion + B::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject}) where {T1,T2} = QuantumObject(kron(sparse(transpose(sparse(B.data))), A.data), SuperOperator, A.dims) # TODO: fix the sparse conversion @doc raw""" lindblad_dissipator(O::QuantumObject, Id_cache=I(size(O,1)) @@ -83,7 +83,7 @@ julia> fock(20, 3)' * a * fock(20, 4) 2.0 + 0.0im ``` """ -destroy(N::Int) = QuantumObject(spdiagm(1 => Array{ComplexF64}(sqrt.(1:N-1))), OperatorQuantumObject(), [N]) +destroy(N::Int) = QuantumObject(spdiagm(1 => Array{ComplexF64}(sqrt.(1:N-1))), Operator, [N]) @doc raw""" create(N::Int) @@ -107,7 +107,7 @@ julia> fock(20, 4)' * a_d * fock(20, 3) 2.0 + 0.0im ``` """ -create(N::Int) = QuantumObject(spdiagm(-1 => Array{ComplexF64}(sqrt.(1:N-1))), OperatorQuantumObject(), [N]) +create(N::Int) = QuantumObject(spdiagm(-1 => Array{ComplexF64}(sqrt.(1:N-1))), Operator, [N]) @doc raw""" sigmap() @@ -149,7 +149,7 @@ sigmaz() = sigmap() * sigmam() - sigmam() * sigmap() Identity operator ``\hat{\mathbb{1}}`` with Hilbert dimension `N`. """ -eye(N::Int; type::ObjType=OperatorQuantumObject(), dims::Vector{Int}=[N]) where +eye(N::Int; type::ObjType=Operator, dims::Vector{Int}=[N]) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = QuantumObject(Diagonal(ones(ComplexF64, N)), type, dims) @doc raw""" @@ -157,7 +157,7 @@ eye(N::Int; type::ObjType=OperatorQuantumObject(), dims::Vector{Int}=[N]) where Identity operator ``\hat{\mathbb{1}}`` with Hilbert dimension `N`. """ -qeye(N::Int; type::ObjType=OperatorQuantumObject(), dims::Vector{Int}=[N]) where +qeye(N::Int; type::ObjType=Operator, dims::Vector{Int}=[N]) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = eye(N, type=type, dims=dims) @doc raw""" @@ -168,11 +168,11 @@ to specify the list of dimensions `dims` if different subsystems are present. """ function fock(N::Int, pos::Int; dims::Vector{Int}=[N], sparse::Bool=false) if sparse - return QuantumObject(sparsevec([pos+1], [1.0+0im], N), KetQuantumObject(), dims) + return QuantumObject(sparsevec([pos+1], [1.0+0im], N), Ket, dims) else array = zeros(ComplexF64, N) array[pos+1] = 1 - return QuantumObject(array, KetQuantumObject(), dims) + return QuantumObject(array, Ket, dims) end end diff --git a/src/time_evolution/time_evolution.jl b/src/time_evolution/time_evolution.jl index e76eaf53..fa8ecc38 100644 --- a/src/time_evolution/time_evolution.jl +++ b/src/time_evolution/time_evolution.jl @@ -194,7 +194,7 @@ function liouvillian_generalized(H::QuantumObject{MT, OperatorQuantumObject}, fi Ω1 = kron(Ω, M1) Ω2 = kron(M1, Ω) Ωdiff = Ω1 .- Ω2 - F2 = QuantumObject(gaussian.(Ωdiff, 0, σ), SuperOperatorQuantumObject(), dims) + F2 = QuantumObject(gaussian.(Ωdiff, 0, σ), SuperOperator, dims) F2 = dense_to_sparse(F2, tol) L = liouvillian(H_d) @@ -239,8 +239,8 @@ function _liouvillian_floquet(L₀::QuantumObject{<:AbstractArray{T1},SuperOpera T = -(L_0 + 1im * n_i * ω * I + L_p * T) \ L_m_dense end - solver.tol == 0 && return QuantumObject(L_0 + L_m * S + L_p * T, SuperOperatorQuantumObject(), L₀.dims) - return QuantumObject(dense_to_sparse(L_0 + L_m * S + L_p * T, solver.tol), SuperOperatorQuantumObject(), L₀.dims) + solver.tol == 0 && return QuantumObject(L_0 + L_m * S + L_p * T, SuperOperator, L₀.dims) + return QuantumObject(dense_to_sparse(L_0 + L_m * S + L_p * T, solver.tol), SuperOperator, L₀.dims) end function steadystate(L::QuantumObject{<:AbstractArray{T},SuperOperatorQuantumObject}; @@ -270,7 +270,7 @@ function _steadystate(L::QuantumObject{<:AbstractArray{T},SuperOperatorQuantumOb ρss_vec = L_tmp \ v0 ρss = reshape(ρss_vec, N, N) ρss = (ρss + ρss') / 2 # Hermitianize - QuantumObject(ρss, OperatorQuantumObject(), L.dims) + QuantumObject(ρss, Operator, L.dims) end @doc raw""" diff --git a/test/quantum_objects.jl b/test/quantum_objects.jl index 8944f9d3..a1bedfc6 100644 --- a/test/quantum_objects.jl +++ b/test/quantum_objects.jl @@ -1,7 +1,7 @@ @testset "Quantum Objects" begin # unsupported size of array for a in [rand(ComplexF64, 3, 2), rand(ComplexF64, 2, 2, 2)] - for t in [nothing, KetQuantumObject(), BraQuantumObject(), OperatorQuantumObject(), SuperOperatorQuantumObject(), OperatorBraQuantumObject(), OperatorKetQuantumObject()] + for t in [nothing, Ket, Bra, Operator, SuperOperator, OperatorBra, OperatorKet] @test_throws DomainError Qobj(a, type=t) end end @@ -9,14 +9,14 @@ N = 10 a = rand(ComplexF64, 10) # @test_logs (:warn, "The norm of the input data is not one.") QuantumObject(a) - @test_throws DomainError Qobj(a, type=BraQuantumObject()) - @test_throws DomainError Qobj(a, type=OperatorQuantumObject()) - @test_throws DomainError Qobj(a, type=SuperOperatorQuantumObject()) - @test_throws DomainError Qobj(a, type=OperatorBraQuantumObject()) - @test_throws DomainError Qobj(a', type=KetQuantumObject()) - @test_throws DomainError Qobj(a', type=OperatorQuantumObject()) - @test_throws DomainError Qobj(a', type=SuperOperatorQuantumObject()) - @test_throws DomainError Qobj(a', type=OperatorKetQuantumObject()) + @test_throws DomainError Qobj(a, type=Bra) + @test_throws DomainError Qobj(a, type=Operator) + @test_throws DomainError Qobj(a, type=SuperOperator) + @test_throws DomainError Qobj(a, type=OperatorBra) + @test_throws DomainError Qobj(a', type=Ket) + @test_throws DomainError Qobj(a', type=Operator) + @test_throws DomainError Qobj(a', type=SuperOperator) + @test_throws DomainError Qobj(a', type=OperatorKet) @test_throws DimensionMismatch Qobj(a, dims=[2]) @test_throws DimensionMismatch Qobj(a', dims=[2]) a2 = Qobj(a') @@ -39,7 +39,7 @@ a = sprand(ComplexF64, 100, 100, 0.1) a2 = Qobj(a) - a3 = Qobj(a, type=SuperOperatorQuantumObject()) + a3 = Qobj(a, type=SuperOperator) @test isket(a2) == false @test isbra(a2) == false @@ -62,7 +62,7 @@ ρ = Qobj(rand(ComplexF64, 2, 2)) ρ_ket = mat2vec(ρ) ρ_bra = ρ_ket' - @test ρ_bra == Qobj(mat2vec(ρ.data)', type=OperatorBraQuantumObject()) + @test ρ_bra == Qobj(mat2vec(ρ.data)', type=OperatorBra) @test ρ == vec2mat(ρ_ket) @test isket(ρ_ket) == false @test isbra(ρ_ket) == false @@ -85,8 +85,8 @@ @test L * ρ_ket ≈ -1im * (+(spre(H) * ρ_ket) - spost(H) * ρ_ket) @test (ρ_bra * L')' == L * ρ_ket @test sum((conj(ρ) .* ρ).data) ≈ dot(ρ_ket, ρ_ket) ≈ ρ_bra * ρ_ket - @test_throws DimensionMismatch Qobj(ρ_ket.data, type=OperatorKetQuantumObject(), dims=[4]) - @test_throws DimensionMismatch Qobj(ρ_bra.data, type=OperatorBraQuantumObject(), dims=[4]) + @test_throws DimensionMismatch Qobj(ρ_ket.data, type=OperatorKet, dims=[4]) + @test_throws DimensionMismatch Qobj(ρ_bra.data, type=OperatorBra, dims=[4]) a = Array(a) a4 = Qobj(a) @@ -197,7 +197,7 @@ ψ_size = size(ψ) @test opstring == "Quantum Object: type=Bra dims=$ψ_dims size=$ψ_size\n$datastring" - ψ2 = Qobj(rand(ComplexF64, 4), type=OperatorKetQuantumObject()) + ψ2 = Qobj(rand(ComplexF64, 4), type=OperatorKet) opstring = sprint((t, s) -> show(t, "text/plain", s), ψ2) datastring = sprint((t, s) -> show(t, "text/plain", s), ψ2.data) ψ2_dims = ψ2.dims