diff --git a/.gitignore b/.gitignore index 483c0b92..edb4c3a5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ Manifest.toml docs/build/ .vscode -*.json \ No newline at end of file +*.json + +.ipynb_checkpoints +*.ipynb \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index 41b8b7a6..4e7a2963 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,3 +1,6 @@ +#! format: off +# turns off the julia formatting of this file + using QuantumToolbox using Documenter @@ -17,7 +20,7 @@ const PAGES = [ "Manipulating States and Operators" => "users_guide/states_and_operators.md", "Tensor Products and Partial Traces" => "users_guide/tensor.md", "Time Evolution and Dynamics" => [ - "Introduction" => "users_guide/time_evolution/intro.md" + "Introduction" => "users_guide/time_evolution/intro.md", ], "Solving for Steady-State Solutions" => [], "Symmetries" => [], diff --git a/docs/src/api.md b/docs/src/api.md index f6691afb..8d2e1634 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -26,7 +26,6 @@ OperatorKet SuperOperatorQuantumObject SuperOperator QuantumObject -Qobj size eltype length @@ -36,18 +35,34 @@ isoper isoperbra isoperket issuper +LinearAlgebra.ishermitian +LinearAlgebra.issymmetric +LinearAlgebra.isposdef ``` ## [Qobj arithmetic and attributes](@id doc-API:Qobj-arithmetic-and-attributes) ```@docs -sqrtm -sinm -cosm +Base.conj +LinearAlgebra.transpose +LinearAlgebra.adjoint +LinearAlgebra.dot +LinearAlgebra.sqrt +LinearAlgebra.log +LinearAlgebra.exp +LinearAlgebra.sin +LinearAlgebra.cos LinearAlgebra.tr LinearAlgebra.svdvals LinearAlgebra.norm +LinearAlgebra.normalize +LinearAlgebra.normalize! +LinearAlgebra.inv +LinearAlgebra.diag +proj ptrace +purity +permute tidyup tidyup! get_data @@ -73,9 +88,6 @@ eigsolve_al ket2dm expect LinearAlgebra.kron -tensor -⊗ -permute sparse_to_dense dense_to_sparse vec2mat @@ -106,7 +118,6 @@ create fdestroy fcreate eye -qeye projection commutator spre @@ -115,6 +126,25 @@ sprepost lindblad_dissipator ``` +## [Synonyms of functions for Qobj](@id doc-API:Synonyms-of-functions-for-Qobj) +```@docs +Qobj +shape +isherm +trans +dag +matrix_element +unit +sqrtm +logm +expm +sinm +cosm +tensor +⊗ +qeye +``` + ## [Time evolution](@id doc-API:Time-evolution) ```@docs diff --git a/src/QuantumToolbox.jl b/src/QuantumToolbox.jl index a4c97b51..f7d5b0fa 100644 --- a/src/QuantumToolbox.jl +++ b/src/QuantumToolbox.jl @@ -49,6 +49,7 @@ include("qobj/eigsolve.jl") include("qobj/functions.jl") include("qobj/states.jl") include("qobj/operators.jl") +include("qobj/synonyms.jl") # time evolution include("time_evolution/time_evolution.jl") diff --git a/src/qobj/arithmetic_and_attributes.jl b/src/qobj/arithmetic_and_attributes.jl index 7c2bdf58..492dd3fa 100644 --- a/src/qobj/arithmetic_and_attributes.jl +++ b/src/qobj/arithmetic_and_attributes.jl @@ -4,11 +4,9 @@ Arithmetic and Attributes for QuantumObject - export most of the attribute functions in "Python Qobj class" =# -export sqrtm, sinm, cosm -export ptrace +export proj, ptrace, purity, permute export tidyup, tidyup! export get_data, get_coherence -export permute # Broadcasting Base.broadcastable(x::QuantumObject) = x.data @@ -42,7 +40,8 @@ for op in (:(+), :(-), :(*)) A::QuantumObject{<:AbstractArray{T1},OpType}, B::QuantumObject{<:AbstractArray{T2},OpType}, ) where {T1,T2,OpType<:QuantumObjectType} - A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) + A.dims != B.dims && + throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return QuantumObject($(op)(A.data, B.data), A.type, A.dims) end LinearAlgebra.$op(A::QuantumObject{<:AbstractArray{T}}) where {T} = QuantumObject($(op)(A.data), A.type, A.dims) @@ -58,56 +57,56 @@ 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.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return 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.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return 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.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return QuantumObject(A.data * B.data, Operator, A.dims) end function LinearAlgebra.:(*)( A::QuantumObject{<:AbstractArray{T1},BraQuantumObject}, B::QuantumObject{<:AbstractArray{T2},KetQuantumObject}, ) where {T1,T2} - A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return A.data * B.data 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.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return 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} - A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return A.data * B.data 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.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return 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.")) + A.dims != B.dims && throw(DimensionMismatch("The two quantum objects are not of the same Hilbert dimension.")) return QuantumObject(A.data * B.data, OperatorBra, A.dims) end @@ -115,21 +114,77 @@ LinearAlgebra.:(^)(A::QuantumObject{<:AbstractArray{T}}, n::T1) where {T,T1<:Num QuantumObject(^(A.data, n), A.type, A.dims) LinearAlgebra.:(/)(A::QuantumObject{<:AbstractArray{T}}, n::T1) where {T,T1<:Number} = QuantumObject(/(A.data, n), A.type, A.dims) + +@doc raw""" + dot(A::QuantumObject, B::QuantumObject) + +Compute the dot product between two [`QuantumObject`]: ``\langle A | B \rangle`` + +Note that `A` and `B` should be [`Ket`](@ref) or [`OperatorKet`](@ref) + +`A ⋅ B` (where `⋅` can be typed by tab-completing `\\cdot` in the REPL) is a synonym for `dot(A, B)` +""" function LinearAlgebra.dot( A::QuantumObject{<:AbstractArray{T1},OpType}, B::QuantumObject{<:AbstractArray{T2},OpType}, ) where {T1<:Number,T2<:Number,OpType<:Union{KetQuantumObject,OperatorKetQuantumObject}} - A.dims != B.dims && throw(ErrorException("The two operators are not of the same Hilbert dimension.")) + A.dims != B.dims && throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) return LinearAlgebra.dot(A.data, B.data) end +@doc raw""" + dot(i::QuantumObject, A::QuantumObject j::QuantumObject) + +Compute the generalized dot product `dot(i, A*j)` between three [`QuantumObject`](@ref): ``\langle i | A | j \rangle`` + +Supports the following inputs: +- `A` is in the type of [`Operator`](@ref), with `i` and `j` are both [`Ket`](@ref). +- `A` is in the type of [`SuperOperator`](@ref), with `i` and `j` are both [`OperatorKet`](@ref) +""" +function LinearAlgebra.dot( + i::QuantumObject{<:AbstractArray{T1},KetQuantumObject}, + A::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject}, + j::QuantumObject{<:AbstractArray{T3},KetQuantumObject}, +) where {T1<:Number,T2<:Number,T3<:Number} + ((i.dims != A.dims) || (A.dims != j.dims)) && + throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + return LinearAlgebra.dot(i.data, A.data, j.data) +end +function LinearAlgebra.dot( + i::QuantumObject{<:AbstractArray{T1},OperatorKetQuantumObject}, + A::QuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject}, + j::QuantumObject{<:AbstractArray{T3},OperatorKetQuantumObject}, +) where {T1<:Number,T2<:Number,T3<:Number} + ((i.dims != A.dims) || (A.dims != j.dims)) && + throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + return LinearAlgebra.dot(i.data, A.data, j.data) +end + +@doc raw""" + conj(A::QuantumObject) + +Return the element-wise complex conjugation of the [`QuantumObject`](@ref). +""" Base.conj(A::QuantumObject{<:AbstractArray{T}}) where {T} = QuantumObject(conj(A.data), A.type, A.dims) +@doc raw""" + transpose(A::QuantumObject) + +Lazy matrix transpose of the [`QuantumObject`](@ref). +""" LinearAlgebra.transpose( A::QuantumObject{<:AbstractArray{T},OpType}, ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = QuantumObject(transpose(A.data), A.type, A.dims) +@doc raw""" + A' + adjoint(A::QuantumObject) + +Lazy adjoint (conjugate transposition) of the [`QuantumObject`](@ref) + +Note that `A'` is a synonym for `adjoint(A)` +""" LinearAlgebra.adjoint( A::QuantumObject{<:AbstractArray{T},OpType}, ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = @@ -143,6 +198,11 @@ LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObje LinearAlgebra.adjoint(A::QuantumObject{<:AbstractArray{T},OperatorBraQuantumObject}) where {T} = QuantumObject(adjoint(A.data), OperatorKet, A.dims) +@doc raw""" + inv(A::QuantumObject) + +Matrix inverse of the [`QuantumObject`](@ref) +""" LinearAlgebra.inv( A::QuantumObject{<:AbstractArray{T},OpType}, ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = @@ -154,7 +214,7 @@ LinearAlgebra.Hermitian( ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = QuantumObject(Hermitian(A.data, uplo), A.type, A.dims) -""" +@doc raw""" tr(A::QuantumObject}) Returns the trace of `A`. @@ -180,22 +240,20 @@ LinearAlgebra.tr( ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = ishermitian(A) ? real(tr(A.data)) : tr(A.data) -""" +@doc raw""" svdvals(A::QuantumObject) Return the singular values of a [`QuantumObject`](@ref) in descending order """ LinearAlgebra.svdvals(A::QuantumObject{<:AbstractVector}) = svdvals(A.data) -LinearAlgebra.svdvals(A::QuantumObject{<:DenseMatrix}) = svdvals(A.data) +LinearAlgebra.svdvals(A::QuantumObject{<:AbstractMatrix}) = svdvals(A.data) LinearAlgebra.svdvals(A::QuantumObject{<:AbstractSparseMatrix}) = svdvals(sparse_to_dense(A.data)) -""" - norm(A::QuantumObject, p::Real=2) - -If `A` is either [`Ket`](@ref), [`Bra`](@ref), [`OperatorKet`](@ref), or [`OperatorBra`](@ref), returns the standard vector `p`-norm of `A`. -If `A` is either [`Operator`](@ref) or [`SuperOperator`](@ref), returns [Schatten](https://en.wikipedia.org/wiki/Schatten_norm) `p`-norm of `A`. +@doc raw""" + norm(A::QuantumObject, p::Real) -Note that the default value of `p=2` +If `A` is either [`Ket`](@ref), [`Bra`](@ref), [`OperatorKet`](@ref), or [`OperatorBra`](@ref), returns the standard vector `p`-norm (default `p=2`) of `A`. +If `A` is either [`Operator`](@ref) or [`SuperOperator`](@ref), returns [Schatten](https://en.wikipedia.org/wiki/Schatten_norm) `p`-norm (default `p=1`) of `A`. # Examples @@ -225,14 +283,47 @@ LinearAlgebra.norm( norm(A.data, p) function LinearAlgebra.norm( A::QuantumObject{<:AbstractArray{T},OpType}, - p::Real = 2, + p::Real = 1, ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} p == 2.0 && return norm(A.data, 2) return norm(svdvals(A), p) end -LinearAlgebra.normalize(A::QuantumObject{<:AbstractArray{T}}) where {T} = - QuantumObject(normalize(A.data), A.type, A.dims) -LinearAlgebra.normalize!(A::QuantumObject{<:AbstractArray{T}}) where {T} = (normalize!(A.data); A) + +@doc raw""" + normalize(A::QuantumObject, p::Real) + +Return normalized [`QuantumObject`](@ref) so that its `p`-norm equals to unity, i.e. `norm(A, p) == 1`. + +Support for the following types of [`QuantumObject`](@ref): +- If `A` is [`Ket`](@ref) or [`Bra`](@ref), default `p = 2` +- If `A` is [`Operator`](@ref), default `p = 1` + +Also, see [`norm`](@ref) about its definition for different types of [`QuantumObject`](@ref). +""" +LinearAlgebra.normalize( + A::QuantumObject{<:AbstractArray{T},ObjType}, + p::Real = 2, +) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject}} = QuantumObject(A.data / norm(A, p), A.type, A.dims) +LinearAlgebra.normalize(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, p::Real = 1) where {T} = + QuantumObject(A.data / norm(A, p), A.type, A.dims) + +@doc raw""" + normalize!(A::QuantumObject, p::Real) + +Normalize [`QuantumObject`](@ref) in-place so that its `p`-norm equals to unity, i.e. `norm(A, p) == 1`. + +Support for the following types of [`QuantumObject`](@ref): +- If `A` is [`Ket`](@ref) or [`Bra`](@ref), default `p = 2` +- If `A` is [`Operator`](@ref), default `p = 1` + +Also, see [`norm`](@ref) about its definition for different types of [`QuantumObject`](@ref). +""" +LinearAlgebra.normalize!( + A::QuantumObject{<:AbstractArray{T},ObjType}, + p::Real = 2, +) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject}} = (rmul!(A.data, 1 / norm(A)); A) +LinearAlgebra.normalize!(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, p::Real = 1) where {T} = + (rmul!(A.data, 1 / norm(A)); A) LinearAlgebra.triu!( A::QuantumObject{<:AbstractArray{T},OpType}, @@ -259,21 +350,41 @@ LinearAlgebra.rmul!(B::QuantumObject{<:AbstractArray}, a::Number) = (rmul!(B.dat @inline LinearAlgebra.mul!(y::AbstractVector{Ty}, A::QuantumObject{<:AbstractMatrix{Ta}}, x, α, β) where {Ty,Ta} = mul!(y, A.data, x, α, β) +@doc raw""" + sqrt(A::QuantumObject) + +Square root of [`QuantumObject`](@ref) +""" LinearAlgebra.sqrt(A::QuantumObject{<:AbstractArray{T}}) where {T} = QuantumObject(sqrt(sparse_to_dense(A.data)), A.type, A.dims) @doc raw""" - sqrtm(A::QuantumObject) + log(A::QuantumObject) -Matrix square root of [`Operator`](@ref) type of [`QuantumObject`](@ref) +Matrix logarithm of [`QuantumObject`](@ref) -Note that for other types of [`QuantumObject`](@ref) use `sprt(A)` instead. +Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -sqrtm(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = sqrt(A) +LinearAlgebra.log( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = + QuantumObject(log(sparse_to_dense(A.data)), A.type, A.dims) -LinearAlgebra.exp(A::QuantumObject{<:AbstractMatrix{T}}) where {T} = +@doc raw""" + exp(A::QuantumObject) + +Matrix exponential of [`QuantumObject`](@ref) + +Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +LinearAlgebra.exp( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = QuantumObject(dense_to_sparse(exp(A.data)), A.type, A.dims) -LinearAlgebra.exp(A::QuantumObject{<:AbstractSparseMatrix{T}}) where {T} = QuantumObject(_spexp(A.data), A.type, A.dims) +LinearAlgebra.exp( + A::QuantumObject{<:AbstractSparseMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = + QuantumObject(_spexp(A.data), A.type, A.dims) function _spexp(A::SparseMatrixCSC{T,M}; threshold = 1e-14, nonzero_tol = 1e-20) where {T,M} m = checksquare(A) # Throws exception if not square @@ -307,22 +418,50 @@ function _spexp(A::SparseMatrixCSC{T,M}; threshold = 1e-14, nonzero_tol = 1e-20) end @doc raw""" - sinm(O::QuantumObject) + sin(A::QuantumObject) + +Matrix sine of [`QuantumObject`](@ref), defined as -Generates the sine of the operator `O`, defined as +``\sin \left( \hat{A} \right) = \frac{e^{i \hat{A}} - e^{-i \hat{A}}}{2 i}`` -``\sin \left( \hat{O} \right) = \frac{e^{i \hat{O}} - e^{-i \hat{O}}}{2 i}`` +Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -sinm(O::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = -0.5im * (exp(1im * O) - exp(-1im * O)) +LinearAlgebra.sin( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (exp(1im * A) - exp(-1im * A)) / 2im @doc raw""" - cosm(O::QuantumObject) + cos(A::QuantumObject) -Generates the cosine of the operator `O`, defined as +Matrix cosine of [`QuantumObject`](@ref), defined as -``\cos \left( \hat{O} \right) = \frac{e^{i \hat{O}} + e^{-i \hat{O}}}{2}`` +``\cos \left( \hat{A} \right) = \frac{e^{i \hat{A}} + e^{-i \hat{A}}}{2}`` + +Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +LinearAlgebra.cos( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (exp(1im * A) + exp(-1im * A)) / 2 + +@doc raw""" + diag(A::QuantumObject, k::Int=0) + +Return the `k`-th diagonal elements of a matrix-type [`QuantumObject`](@ref) + +Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +LinearAlgebra.diag( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, + k::Int = 0, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = diag(A.data, k) + +@doc raw""" + proj(ψ::QuantumObject) + +Return the projector for a [`Ket`](@ref) or [`Bra`](@ref) type of [`QuantumObject`](@ref) """ -cosm(O::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = 0.5 * (exp(1im * O) + exp(-1im * O)) +proj(ψ::QuantumObject{<:AbstractArray{T},KetQuantumObject}) where {T} = ψ * ψ' +proj(ψ::QuantumObject{<:AbstractArray{T},BraQuantumObject}) where {T} = ψ' * ψ @doc raw""" ptrace(QO::QuantumObject, sel::Vector{Int}) @@ -426,7 +565,18 @@ function _ptrace_oper(QO::AbstractArray{T1}, dims::Vector{<:Integer}, sel::Vecto return res, dkeep end +@doc raw""" + purity(ρ::QuantumObject) + +Calculate the purity of a [`QuantumObject`](@ref): ``\textrm{Tr}(\rho^2)`` + +Note that this function only supports for [`Ket`](@ref), [`Bra`](@ref), and [`Operator`](@ref) """ +purity(ρ::QuantumObject{<:AbstractArray{T},ObjType}) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject}} = + sum(abs2, ρ.data) +purity(ρ::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = real(tr(ρ.data^2)) + +@doc raw""" tidyup(A::QuantumObject, tol::Real=1e-14) Removes those elements of a QuantumObject `A` whose absolute value is less than `tol`. @@ -436,17 +586,17 @@ tidyup(A::QuantumObject{<:AbstractArray{T}}, tol::T2 = 1e-14) where {T,T2<:Real} tidyup(A::AbstractArray{T}, tol::T2 = 1e-14) where {T,T2<:Real} = @. T(abs(A) > tol) * A tidyup(A::AbstractSparseMatrix{T}, tol::T2 = 1e-14) where {T,T2<:Real} = droptol!(copy(A), tol) -""" +@doc raw""" tidyup!(A::QuantumObject, tol::Real=1e-14) Removes those elements of a QuantumObject `A` whose absolute value is less than `tol`. -In-place version of [`tidyup`](#tidyup). +In-place version of [`tidyup`](@ref). """ tidyup!(A::QuantumObject{<:AbstractArray{T}}, tol::T2 = 1e-14) where {T,T2<:Real} = (tidyup!(A.data, tol); A) tidyup!(A::AbstractArray{T}, tol::T2 = 1e-14) where {T,T2<:Real} = @. A = T(abs(A) > tol) * A tidyup!(A::AbstractSparseMatrix{T}, tol::T2 = 1e-14) where {T,T2<:Real} = droptol!(A, tol) -""" +@doc raw""" get_data(A::QuantumObject) Returns the data of a QuantumObject. diff --git a/src/qobj/boolean_functions.jl b/src/qobj/boolean_functions.jl index a09ebbdd..37820297 100644 --- a/src/qobj/boolean_functions.jl +++ b/src/qobj/boolean_functions.jl @@ -4,21 +4,21 @@ All boolean functions for checking the data or type in `QuantumObject` export isket, isbra, isoper, isoperbra, isoperket, issuper -""" +@doc raw""" isbra(A::QuantumObject) Checks if the [`QuantumObject`](@ref) `A` is a [`BraQuantumObject`](@ref) state. """ isbra(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:QuantumObjectType} = OpType <: BraQuantumObject -""" +@doc raw""" isket(A::QuantumObject) Checks if the [`QuantumObject`](@ref) `A` is a [`KetQuantumObject`](@ref) state. """ isket(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:QuantumObjectType} = OpType <: KetQuantumObject -""" +@doc raw""" isoper(A::QuantumObject) Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorQuantumObject`](@ref) state. @@ -26,7 +26,7 @@ Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorQuantumObject`](@ref) s isoper(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:QuantumObjectType} = OpType <: OperatorQuantumObject -""" +@doc raw""" isoperbra(A::QuantumObject) Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorBraQuantumObject`](@ref) state. @@ -34,7 +34,7 @@ Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorBraQuantumObject`](@ref isoperbra(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:QuantumObjectType} = OpType <: OperatorBraQuantumObject -""" +@doc raw""" isoperket(A::QuantumObject) Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorKetQuantumObject`](@ref) state. @@ -42,7 +42,7 @@ Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorKetQuantumObject`](@ref isoperket(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:QuantumObjectType} = OpType <: OperatorKetQuantumObject -""" +@doc raw""" issuper(A::QuantumObject) Checks if the [`QuantumObject`](@ref) `A` is a [`SuperOperatorQuantumObject`](@ref) state. @@ -50,6 +50,23 @@ Checks if the [`QuantumObject`](@ref) `A` is a [`SuperOperatorQuantumObject`](@r issuper(A::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:QuantumObjectType} = OpType <: SuperOperatorQuantumObject +@doc raw""" + ishermitian(A::QuantumObject) + +Test whether the [`QuantumObject`](@ref) is Hermitian. +""" LinearAlgebra.ishermitian(A::QuantumObject{<:AbstractArray{T}}) where {T} = ishermitian(A.data) + +@doc raw""" + issymmetric(A::QuantumObject) + +Test whether the [`QuantumObject`](@ref) is symmetric. +""" LinearAlgebra.issymmetric(A::QuantumObject{<:AbstractArray{T}}) where {T} = issymmetric(A.data) + +@doc raw""" + isposdef(A::QuantumObject) + +Test whether the [`QuantumObject`](@ref) is positive definite (and Hermitian) by trying to perform a Cholesky factorization of `A`. +""" LinearAlgebra.isposdef(A::QuantumObject{<:AbstractArray{T}}) where {T} = isposdef(A.data) diff --git a/src/qobj/functions.jl b/src/qobj/functions.jl index 2fb5749c..46a025e5 100644 --- a/src/qobj/functions.jl +++ b/src/qobj/functions.jl @@ -5,7 +5,6 @@ Functions which manipulates QuantumObject export ket2dm export expect export sparse_to_dense, dense_to_sparse -export tensor, ⊗ export vec2mat, mat2vec @doc raw""" @@ -61,7 +60,7 @@ function expect( return ishermitian(O) ? real(tr(O * ρ)) : tr(O * ρ) end -""" +@doc raw""" sparse_to_dense(A::QuantumObject) Converts a sparse QuantumObject to a dense QuantumObject. @@ -83,7 +82,7 @@ end sparse_to_dense(::Type{M}) where {M<:AbstractMatrix} = M -""" +@doc raw""" dense_to_sparse(A::QuantumObject) Converts a dense QuantumObject to a sparse QuantumObject. @@ -104,9 +103,9 @@ function dense_to_sparse(A::VT, tol::Real = 1e-10) where {VT<:AbstractVector} end @doc raw""" - kron(A::QuantumObject, B::QuantumObject) + kron(A::QuantumObject, B::QuantumObject, ...) -Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ``\hat{A} \otimes \hat{B}``. +Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ``\hat{A} \otimes \hat{B} \otimes \cdots``. # Examples @@ -154,82 +153,6 @@ function LinearAlgebra.kron( end @doc raw""" - tensor(A1::QuantumObject, A2::QuantumObject, ...) - -Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ``\hat{A}_1 \otimes \hat{A}_2 \otimes \cdots``. - -# Examples - -``` -julia> x = sigmax() -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true -2×2 SparseMatrixCSC{ComplexF64, Int64} with 2 stored entries: - ⋅ 1.0+0.0im - 1.0+0.0im ⋅ - -julia> x_list = fill(x, 3); - -julia> tensor(x_list...) -Quantum Object: type=Operator dims=[2, 2, 2] size=(8, 8) ishermitian=true -8×8 SparseMatrixCSC{ComplexF64, Int64} with 8 stored entries: - ⋅ ⋅ ⋅ … ⋅ ⋅ 1.0+0.0im - ⋅ ⋅ ⋅ ⋅ 1.0+0.0im ⋅ - ⋅ ⋅ ⋅ 1.0+0.0im ⋅ ⋅ - ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ - ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ - ⋅ ⋅ 1.0+0.0im … ⋅ ⋅ ⋅ - ⋅ 1.0+0.0im ⋅ ⋅ ⋅ ⋅ - 1.0+0.0im ⋅ ⋅ ⋅ ⋅ ⋅ -``` -""" -tensor(A::QuantumObject...) = kron(A...) - -@doc raw""" - ⊗(A::QuantumObject, B::QuantumObject) - -Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ``\hat{A} \otimes \hat{B}``. - -# Examples - -``` -julia> a = destroy(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false -20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: -⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢ - -julia> a ⊗ a -Quantum Object: type=Operator dims=[20, 20] size=(400, 400) ishermitian=false -400×400 SparseMatrixCSC{ComplexF64, Int64} with 361 stored entries: -⠀⠀⠘⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠦ -``` -""" -⊗(A::QuantumObject, B::QuantumObject) = kron(A, B) - -""" vec2mat(A::AbstractVector) Converts a vector to a matrix. @@ -255,7 +178,7 @@ Convert a quantum object from matrix ([`OperatorQuantumObject`](@ref)-type) to v mat2vec(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = QuantumObject(mat2vec(A.data), OperatorKet, A.dims) -""" +@doc raw""" mat2vec(A::AbstractMatrix) Converts a matrix to a vector. diff --git a/src/qobj/operators.jl b/src/qobj/operators.jl index 385a73c5..3756090d 100644 --- a/src/qobj/operators.jl +++ b/src/qobj/operators.jl @@ -4,7 +4,7 @@ Functions for generating (common) quantum operators. export jmat, spin_Jx, spin_Jy, spin_Jz, spin_Jm, spin_Jp, spin_J_set export sigmam, sigmap, sigmax, sigmay, sigmaz -export destroy, create, eye, qeye, projection +export destroy, create, eye, projection export fdestroy, fcreate export commutator export spre, spost, sprepost, lindblad_dissipator @@ -317,27 +317,20 @@ See also [`jmat`](@ref). sigmaz() = rmul!(jmat(0.5, Val(:z)), 2) @doc raw""" - eye(N::Int; type=OperatorQuantumObject, dims=[N]) + eye(N::Int; type=Operator, dims=nothing) -Identity operator ``\hat{\mathbb{1}}`` with Hilbert dimension `N`. -""" -eye( - N::Int; - type::ObjType = Operator, - dims::Vector{Int} = [N], -) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(Diagonal(ones(ComplexF64, N)), type, dims) +Identity operator ``\hat{\mathbb{1}}`` with size `N`. -@doc raw""" - qeye(N::Int; type=OperatorQuantumObject, dims=[N]) +It is also possible to specify the list of Hilbert dimensions `dims` if different subsystems are present. -Identity operator ``\hat{\mathbb{1}}`` with Hilbert dimension `N`. +Note that `type` can only be either [`Operator`](@ref) or [`SuperOperator`](@ref) """ -qeye( +eye( N::Int; type::ObjType = Operator, - dims::Vector{Int} = [N], -) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = eye(N, type = type, dims = dims) + dims = nothing, +) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = + QuantumObject(Diagonal(ones(ComplexF64, N)); type = type, dims = dims) @doc raw""" fdestroy(N::Int, j::Int) diff --git a/src/qobj/quantum_object.jl b/src/qobj/quantum_object.jl index aa32344a..b25b94f6 100644 --- a/src/qobj/quantum_object.jl +++ b/src/qobj/quantum_object.jl @@ -7,7 +7,7 @@ Also support for fundamental functions in Julia standard library: - SparseArrays: sparse, nnz, nonzeros, rowvals, droptol!, dropzeros, dropzeros!, SparseVector, SparseMatrixCSC =# -export AbstractQuantumObject, QuantumObject, Qobj +export AbstractQuantumObject, QuantumObject export QuantumObjectType, BraQuantumObject, KetQuantumObject, @@ -269,31 +269,27 @@ function QuantumObject( end @doc raw""" - Qobj(A::AbstractArray; type::QuantumObjectType, dims::Vector{Int}) - -Generate `QuantumObject` -""" -Qobj(A; kwargs...) = QuantumObject(A; kwargs...) - -""" size(A::QuantumObject) + size(A::QuantumObject, idx::Int) + +Returns a tuple containing each dimensions of the array in the [`QuantumObject`](@ref). -Returns the size of the matrix or vector corresponding to the [`QuantumObject`](@ref) `A`. +Optionally, you can specify an index (`idx`) to just get the corresponding dimension of the array. """ Base.size(A::QuantumObject{<:AbstractArray{T}}) where {T} = size(A.data) -Base.size(A::QuantumObject{<:AbstractArray{T}}, inds...) where {T} = size(A.data, inds...) +Base.size(A::QuantumObject{<:AbstractArray{T}}, idx::Int) where {T} = size(A.data, idx) Base.getindex(A::QuantumObject{<:AbstractArray{T}}, inds...) where {T} = getindex(A.data, inds...) Base.setindex!(A::QuantumObject{<:AbstractArray{T}}, val, inds...) where {T} = setindex!(A.data, val, inds...) -""" +@doc raw""" eltype(A::QuantumObject) Returns the elements type of the matrix or vector corresponding to the [`QuantumObject`](@ref) `A`. """ Base.eltype(A::QuantumObject) = eltype(A.data) -""" +@doc raw""" length(A::QuantumObject) Returns the length of the matrix or vector corresponding to the [`QuantumObject`](@ref) `A`. diff --git a/src/qobj/synonyms.jl b/src/qobj/synonyms.jl new file mode 100644 index 00000000..6b212c80 --- /dev/null +++ b/src/qobj/synonyms.jl @@ -0,0 +1,250 @@ +#= +Synonyms of the functions for QuantumObject +=# + +export Qobj, shape, isherm +export trans, dag, matrix_element, unit +export sqrtm, logm, expm, sinm, cosm +export tensor, ⊗ +export qeye + +@doc raw""" + Qobj(A::AbstractArray; type::QuantumObjectType, dims::Vector{Int}) + +Generate `QuantumObject` + +Note that this functions is same as `QuantumObject(A; type=type, dims=dims)` +""" +Qobj(A; kwargs...) = QuantumObject(A; kwargs...) + +@doc raw""" + shape(A::QuantumObject) + +Returns a tuple containing each dimensions of the array in the [`QuantumObject`](@ref). + +Note that this function is same as `size(A)` +""" +shape(A::QuantumObject{<:AbstractArray{T}}) where {T} = size(A.data) + +@doc raw""" + isherm(A::QuantumObject) + +Test whether the [`QuantumObject`](@ref) is Hermitian. + +Note that this functions is same as `ishermitian(A)` +""" +isherm(A::QuantumObject{<:AbstractArray{T}}) where {T} = ishermitian(A) + +@doc raw""" + trans(A::QuantumObject) + +Lazy matrix transpose of the [`QuantumObject`](@ref). + +Note that this function is same as `transpose(A)` +""" +trans( + A::QuantumObject{<:AbstractArray{T},OpType}, +) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = transpose(A) + +@doc raw""" + dag(A::QuantumObject) + +Lazy adjoint (conjugate transposition) of the [`QuantumObject`](@ref) + +Note that this function is same as `adjoint(A)` +""" +dag(A::QuantumObject{<:AbstractArray{T}}) where {T} = adjoint(A) + +@doc raw""" + matrix_element(i::QuantumObject, A::QuantumObject j::QuantumObject) + +Compute the generalized dot product `dot(i, A*j)` between three [`QuantumObject`](@ref): ``\langle i | A | j \rangle`` + +Note that this function is same as `dot(i, A, j)` + +Supports the following inputs: +- `A` is in the type of [`Operator`](@ref), with `i` and `j` are both [`Ket`](@ref). +- `A` is in the type of [`SuperOperator`](@ref), with `i` and `j` are both [`OperatorKet`](@ref) +""" +matrix_element( + i::QuantumObject{<:AbstractArray{T1},KetQuantumObject}, + A::QuantumObject{<:AbstractArray{T2},OperatorQuantumObject}, + j::QuantumObject{<:AbstractArray{T3},KetQuantumObject}, +) where {T1<:Number,T2<:Number,T3<:Number} = dot(i, A, j) +matrix_element( + i::QuantumObject{<:AbstractArray{T1},OperatorKetQuantumObject}, + A::QuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject}, + j::QuantumObject{<:AbstractArray{T3},OperatorKetQuantumObject}, +) where {T1<:Number,T2<:Number,T3<:Number} = dot(i, A, j) + +@doc raw""" + unit(A::QuantumObject, p::Real) + +Return normalized [`QuantumObject`](@ref) so that its `p`-norm equals to unity, i.e. `norm(A, p) == 1`. + +Support for the following types of [`QuantumObject`](@ref): +- If `A` is [`Ket`](@ref) or [`Bra`](@ref), default `p = 2` +- If `A` is [`Operator`](@ref), default `p = 1` + +Note that this function is same as `normalize(A, p)` + +Also, see [`norm`](@ref) about its definition for different types of [`QuantumObject`](@ref). +""" +unit( + A::QuantumObject{<:AbstractArray{T},ObjType}, + p::Real = 2, +) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject}} = normalize(A, p) +unit(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, p::Real = 1) where {T} = normalize(A, p) + +@doc raw""" + sqrtm(A::QuantumObject) + +Matrix square root of [`Operator`](@ref) type of [`QuantumObject`](@ref) + +Note that for other types of [`QuantumObject`](@ref) use `sprt(A)` instead. +""" +sqrtm(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = sqrt(A) + +@doc raw""" + logm(A::QuantumObject) + +Matrix logarithm of [`QuantumObject`](@ref) + +Note that this function is same as `log(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +logm( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = log(A) + +@doc raw""" + expm(A::QuantumObject) + +Matrix exponential of [`QuantumObject`](@ref) + +Note that this function is same as `exp(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +expm( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = exp(A) + +@doc raw""" + sinm(A::QuantumObject) + +Matrix sine of [`QuantumObject`](@ref), defined as + +``\sin \left( \hat{A} \right) = \frac{e^{i \hat{A}} - e^{-i \hat{A}}}{2 i}`` + +Note that this function is same as `sin(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +sinm( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = sin(A) + +@doc raw""" + cosm(A::QuantumObject) + +Matrix cosine of [`QuantumObject`](@ref), defined as + +``\cos \left( \hat{A} \right) = \frac{e^{i \hat{A}} + e^{-i \hat{A}}}{2}`` + +Note that this function is same as `cos(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) +""" +cosm( + A::QuantumObject{<:AbstractMatrix{T},ObjType}, +) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = cos(A) + +@doc raw""" + tensor(A::QuantumObject, B::QuantumObject, ...) + +Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ``\hat{A} \otimes \hat{B} \otimes \cdots``. + +Note that this function is same as `kron(A, B, ...)` + +# Examples + +``` +julia> x = sigmax() +Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +2×2 SparseMatrixCSC{ComplexF64, Int64} with 2 stored entries: + ⋅ 1.0+0.0im + 1.0+0.0im ⋅ + +julia> x_list = fill(x, 3); + +julia> tensor(x_list...) +Quantum Object: type=Operator dims=[2, 2, 2] size=(8, 8) ishermitian=true +8×8 SparseMatrixCSC{ComplexF64, Int64} with 8 stored entries: + ⋅ ⋅ ⋅ … ⋅ ⋅ 1.0+0.0im + ⋅ ⋅ ⋅ ⋅ 1.0+0.0im ⋅ + ⋅ ⋅ ⋅ 1.0+0.0im ⋅ ⋅ + ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ + ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ + ⋅ ⋅ 1.0+0.0im … ⋅ ⋅ ⋅ + ⋅ 1.0+0.0im ⋅ ⋅ ⋅ ⋅ + 1.0+0.0im ⋅ ⋅ ⋅ ⋅ ⋅ +``` +""" +tensor(A::QuantumObject...) = kron(A...) + +@doc raw""" + ⊗(A::QuantumObject, B::QuantumObject) + +Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ``\hat{A} \otimes \hat{B}``. + +Note that this function is same as `kron(A, B)` + +# Examples + +``` +julia> a = destroy(20) +Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: +⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢ + +julia> a ⊗ a +Quantum Object: type=Operator dims=[20, 20] size=(400, 400) ishermitian=false +400×400 SparseMatrixCSC{ComplexF64, Int64} with 361 stored entries: +⠀⠀⠘⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀ +⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠦ +``` +""" +⊗(A::QuantumObject, B::QuantumObject) = kron(A, B) + +@doc raw""" + qeye(N::Int; type=Operator, dims=nothing) + +Identity operator ``\hat{\mathbb{1}}`` with size `N`. + +It is also possible to specify the list of Hilbert dimensions `dims` if different subsystems are present. + +Note that this function is same as `eye(N, type=type, dims=dims)`, and `type` can only be either [`Operator`](@ref) or [`SuperOperator`](@ref) +""" +qeye( + N::Int; + type::ObjType = Operator, + dims = nothing, +) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = + QuantumObject(Diagonal(ones(ComplexF64, N)); type = type, dims = dims) diff --git a/src/utilities.jl b/src/utilities.jl index 56954b8b..3c74899e 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -6,7 +6,7 @@ Utilities: export gaussian, n_th export row_major_reshape, meshgrid -""" +@doc raw""" row_major_reshape(Q::AbstractArray, shapes...) Reshapes `Q` in the row-major order, as numpy. @@ -14,7 +14,7 @@ Reshapes `Q` in the row-major order, as numpy. row_major_reshape(Q::AbstractArray{T}, shapes...) where {T} = PermutedDimsArray(reshape(Q, reverse(shapes)...), (length(shapes):-1:1)) -""" +@doc raw""" meshgrid(x::AbstractVector, y::AbstractVector) Equivalent to [numpy meshgrid](https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html). diff --git a/test/quantum_objects.jl b/test/quantum_objects.jl index 0e4129ac..e47d3e1d 100644 --- a/test/quantum_objects.jl +++ b/test/quantum_objects.jl @@ -21,6 +21,7 @@ @test_throws DimensionMismatch Qobj(a', dims = [2]) a2 = Qobj(a') a3 = Qobj(a) + @test dag(a3) == a2 @test isket(a2) == false @test isbra(a2) == true @test isoper(a2) == false @@ -88,6 +89,17 @@ @test_throws DimensionMismatch Qobj(ρ_ket.data, type = OperatorKet, dims = [4]) @test_throws DimensionMismatch Qobj(ρ_bra.data, type = OperatorBra, dims = [4]) + # matrix element + s0 = Qobj(basis(4, 0).data; type = OperatorKet) + s1 = Qobj(basis(4, 1).data; type = OperatorKet) + s_wrong = Qobj(basis(9, 0).data; type = OperatorKet) + @test matrix_element(basis(2, 0), H, basis(2, 1)) == H[1, 2] + @test matrix_element(s0, L, s1) == L[1, 2] + @test_throws DimensionMismatch matrix_element(basis(3, 0), H, basis(2, 1)) + @test_throws DimensionMismatch matrix_element(basis(2, 0), H, basis(3, 1)) + @test_throws DimensionMismatch matrix_element(s0, L, s_wrong) + @test_throws DimensionMismatch matrix_element(s_wrong, L, s0) + a = Array(a) a4 = Qobj(a) a5 = sparse(a4) @@ -102,19 +114,19 @@ @test (a2 + 2).data == a2.data + 2 * I @test a2 * 2 == 2 * a2 - @test transpose(transpose(a2)) == a2 - @test transpose(a2).data == transpose(a2.data) - @test adjoint(a2) ≈ transpose(conj(a2)) + @test trans(trans(a2)) == a2 + @test trans(a2).data == transpose(a2.data) + @test adjoint(a2) ≈ trans(conj(a2)) @test adjoint(adjoint(a2)) == a2 @test adjoint(a2).data == adjoint(a2.data) N = 10 a = fock(N, 3) - @test sparse(ket2dm(a)) ≈ projection(N, 3, 3) + @test proj(a) ≈ proj(a') ≈ sparse(ket2dm(a)) ≈ projection(N, 3, 3) @test isket(a') == false @test isbra(a') == true - @test size(a) == (N,) - @test size(a') == (1, N) + @test shape(a) == (N,) + @test shape(a') == (1, N) @test norm(a) ≈ 1 @test norm(a') ≈ 1 @@ -122,23 +134,37 @@ @test dot(ψ, ψ) ≈ norm(ψ) @test dot(ψ, ψ) ≈ ψ' * ψ + # normalize, normalize!, unit a = Qobj(rand(ComplexF64, N)) + M = a * a' @test (norm(a) ≈ 1) == false - @test (norm(normalize(a)) ≈ 1) == true + @test (norm(M) ≈ 1) == false + @test (norm(unit(a)) ≈ 1) == true + @test (norm(unit(M)) ≈ 1) == true @test (norm(a) ≈ 1) == false # Again, to be sure that it is still non-normalized + @test (norm(M) ≈ 1) == false # Again, to be sure that it is still non-normalized normalize!(a) + normalize!(M) @test (norm(a) ≈ 1) == true + @test (norm(M) ≈ 1) == true + @test M ≈ a * a' + @test (unit(qeye(N)) ≈ (qeye(N) / N)) == true a = destroy(N) a_d = a' X = a + a_d Y = 1im * (a - a_d) - Z = a + transpose(a) - @test ishermitian(X) == true - @test ishermitian(Y) == true + Z = a + trans(a) + @test isherm(X) == true + @test isherm(Y) == true @test issymmetric(Y) == false @test issymmetric(Z) == true + # diag + @test diag(a, 1) ≈ [sqrt(i) for i in 1:(N-1)] + @test diag(a_d, -1) == [sqrt(i) for i in 1:(N-1)] + @test diag(a_d * a) ≈ collect(0:(N-1)) + @test Y[1, 2] == conj(Y[2, 1]) @test triu(X) == a @@ -173,7 +199,7 @@ datastring = sprint((t, s) -> show(t, "text/plain", s), a.data) a_dims = a.dims a_size = size(a) - a_isherm = ishermitian(a) + a_isherm = isherm(a) @test opstring == "Quantum Object: type=Operator dims=$a_dims size=$a_size ishermitian=$a_isherm\n$datastring" @@ -182,7 +208,7 @@ datastring = sprint((t, s) -> show(t, "text/plain", s), a.data) a_dims = a.dims a_size = size(a) - a_isherm = ishermitian(a) + a_isherm = isherm(a) @test opstring == "Quantum Object: type=SuperOperator dims=$a_dims size=$a_size\n$datastring" opstring = sprint((t, s) -> show(t, "text/plain", s), ψ) @@ -227,6 +253,17 @@ @test norm(Md, 1) ≈ sum(sqrt, abs.(eigenenergies(Md' * Md))) atol = 1e-6 @test norm(Ms, 1) ≈ sum(sqrt, abs.(eigenenergies(Ms' * Ms))) atol = 1e-6 + # purity + ψ = normalize!(Qobj(rand(ComplexF64, N))) + ψd = ψ' + ρ1 = ψ * ψ' + M2 = rand(ComplexF64, N, N) + ρ2 = normalize!(Qobj(M2 * M2')) + @test purity(ψ) ≈ norm(ψ)^2 ≈ 1.0 + @test purity(ψd) ≈ norm(ψd)^2 ≈ 1.0 + @test purity(ρ1) ≈ 1.0 + @test (1.0 / N) <= purity(ρ2) <= 1.0 + # trace distance ψz0 = basis(2, 0) ψz1 = basis(2, 1) @@ -248,6 +285,16 @@ @test isapprox(fidelity(M0, M1), fidelity(ψ1, M0); atol = 1e-6) @test isapprox(fidelity(ψ1, ψ2), fidelity(ket2dm(ψ1), ket2dm(ψ2)); atol = 1e-6) + # logm (log), expm (exp), sinm, cosm + M0 = rand(ComplexF64, 4, 4) + Md = Qobj(M0 * M0') + Ms = dense_to_sparse(Md) + e_p = expm(1im * Md) + e_m = expm(-1im * Md) + @test logm(expm(Ms)) ≈ expm(logm(Md)) + @test cosm(Ms) ≈ (e_p + e_m) / 2 + @test sinm(Ms) ≈ (e_p - e_m) / 2im + # Broadcasting a = destroy(20) for op in ((+), (-), (*), (^)) diff --git a/test/states_and_operators.jl b/test/states_and_operators.jl index 396defc6..15bc596c 100644 --- a/test/states_and_operators.jl +++ b/test/states_and_operators.jl @@ -72,4 +72,20 @@ @test_throws ArgumentError fdestroy(0, 0) @test_throws ArgumentError fdestroy(sites, -1) @test_throws ArgumentError fdestroy(sites, sites) + + # identity operator + I_op1 = qeye(4) + I_op2 = qeye(4, dims = [2, 2]) + I_su1 = qeye(4, type = SuperOperator) + I_su2 = qeye(4, type = SuperOperator, dims = [2]) + @test I_op1.data == I_op2.data == I_su1.data == I_su2.data + @test (I_op1 == I_op2) == false + @test (I_op1 == I_su1) == false + @test (I_op1 == I_su2) == false + @test (I_op2 == I_su1) == false + @test (I_op2 == I_su2) == false + @test (I_su1 == I_su2) == true + @test_throws DimensionMismatch qeye(4, dims = [2]) + @test_throws DimensionMismatch qeye(2, type = SuperOperator) + @test_throws DimensionMismatch qeye(4, type = SuperOperator, dims = [2, 2]) end