Skip to content
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

Introduce rand_ket and generate rand_dm using Ginibre ensemble method #185

Merged
merged 4 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ zero_ket
fock
basis
coherent
rand_ket
fock_dm
coherent_dm
thermal_dm
Expand Down
45 changes: 37 additions & 8 deletions src/qobj/states.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Functions for generating (common) quantum states.
=#

export zero_ket, fock, basis, coherent
export zero_ket, fock, basis, coherent, rand_ket
export fock_dm, coherent_dm, thermal_dm, maximally_mixed_dm, rand_dm
export spin_state, spin_coherent
export bell_state, singlet_state, triplet_states, w_state, ghz_state
Expand Down Expand Up @@ -54,6 +54,22 @@
"""
coherent(N::Int, α::T) where {T<:Number} = displace(N, α) * fock(N, 0)

@doc raw"""
rand_ket(dimensions)

Generate a random normalized [`Ket`](@ref) vector with given argument `dimensions`.

The `dimensions` can be either the following types:
- `dimensions::Int`: Number of basis states in the Hilbert space.
- `dimensions::Vector{Int}`: list of dimensions representing the each number of basis in the subsystems.
"""
rand_ket(dimensions::Int) = rand_ket([dimensions])
function rand_ket(dimensions::Vector{Int})
N = prod(dimensions)
ψ = rand(ComplexF64, N) .- (0.5 + 0.5im)
return QuantumObject(normalize!(ψ); type = Ket, dims = dimensions)

Check warning on line 70 in src/qobj/states.jl

View check run for this annotation

Codecov / codecov/patch

src/qobj/states.jl#L66-L70

Added lines #L66 - L70 were not covered by tests
end

@doc raw"""
fock_dm(N::Int, pos::Int=0; dims::Vector{Int}=[N], sparse::Bool=false)

Expand Down Expand Up @@ -112,17 +128,30 @@
end

@doc raw"""
rand_dm(N::Integer; dims::Vector{Int}=[N])
rand_dm(dimensions; rank::Int=prod(dimensions))

Generates a random density matrix ``\hat{\rho}``, with the property to be positive semi-definite and ``\textrm{Tr} \left[ \hat{\rho} \right] = 1``.
Generate a random density matrix from Ginibre ensemble with given argument `dimensions` and `rank`, ensuring that it is positive semi-definite and trace equals to `1`.

It is also possible to specify the list of dimensions `dims` if different subsystems are present.
The `dimensions` can be either the following types:
- `dimensions::Int`: Number of basis states in the Hilbert space.
- `dimensions::Vector{Int}`: list of dimensions representing the each number of basis in the subsystems.

The default keyword argument `rank = prod(dimensions)` (full rank).

# References
- [J. Ginibre, Statistical ensembles of complex, quaternion, and real matrices, Journal of Mathematical Physics 6.3 (1965): 440-449](https://doi.org/10.1063/1.1704292)
- [K. Życzkowski, et al., Generating random density matrices, Journal of Mathematical Physics 52, 062201 (2011)](http://dx.doi.org/10.1063/1.3595693)
"""
function rand_dm(N::Integer; dims::Vector{Int} = [N])
ρ = rand(ComplexF64, N, N)
ρ *= ρ'
rand_dm(dimensions::Int; rank::Int = prod(dimensions)) = rand_dm([dimensions], rank = rank)
function rand_dm(dimensions::Vector{Int}; rank::Int = prod(dimensions))
N = prod(dimensions)
(rank < 1) && throw(DomainError(rank, "The argument rank must be larger than 1."))
(rank > N) && throw(DomainError(rank, "The argument rank cannot exceed dimensions."))

Check warning on line 149 in src/qobj/states.jl

View check run for this annotation

Codecov / codecov/patch

src/qobj/states.jl#L145-L149

Added lines #L145 - L149 were not covered by tests

X = _Ginibre_ensemble(N, rank)
ρ = X * X'

Check warning on line 152 in src/qobj/states.jl

View check run for this annotation

Codecov / codecov/patch

src/qobj/states.jl#L151-L152

Added lines #L151 - L152 were not covered by tests
ρ /= tr(ρ)
return QuantumObject(ρ; type = Operator, dims = dims)
return QuantumObject(ρ; type = Operator, dims = dimensions)

Check warning on line 154 in src/qobj/states.jl

View check run for this annotation

Codecov / codecov/patch

src/qobj/states.jl#L154

Added line #L154 was not covered by tests
end

@doc raw"""
Expand Down
2 changes: 2 additions & 0 deletions src/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@

_get_dense_similar(A::AbstractArray, args...) = similar(A, args...)
_get_dense_similar(A::AbstractSparseMatrix, args...) = similar(nonzeros(A), args...)

_Ginibre_ensemble(n::Int, rank::Int = n) = (randn(n, rank) + 1.0im * randn(n, rank)) / sqrt(2 * n)

Check warning on line 51 in src/utilities.jl

View check run for this annotation

Codecov / codecov/patch

src/utilities.jl#L51

Added line #L51 was not covered by tests
ytdHuang marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 13 additions & 3 deletions test/states_and_operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,30 @@
@test ρ2.dims == [2, 2]
@test entropy_vn(ρ1, base = 2) ≈ log(2, 4)

# rand_dm
ρ_AB = rand_dm(4, dims = [2, 2])
# rand_ket and rand_dm
ψ = rand_ket(10)
ρ_AB = rand_dm([2, 2])
ρ_A = ptrace(ρ_AB, 1)
ρ_B = ptrace(ρ_AB, 2)
rank = 5
ρ_low_rank = rand_dm(10, rank = rank)
eig_val = eigenenergies(ρ_low_rank)
@test ψ' * ψ ≈ 1.0
@test tr(ρ_AB) ≈ 1.0
@test tr(ρ_A) ≈ 1.0
@test tr(ρ_B) ≈ 1.0
@test tr(ρ_low_rank) ≈ 1.0
@test ishermitian(ρ_AB) == true
@test ishermitian(ρ_A) == true
@test ishermitian(ρ_B) == true
@test ishermitian(ρ_low_rank) == true
@test all(eigenenergies(ρ_AB) .>= 0)
@test all(eigenenergies(ρ_A) .>= 0)
@test all(eigenenergies(ρ_B) .>= 0)
@test_throws DimensionMismatch rand_dm(4, dims = [2])
@test all(isapprox.(eig_val[1:rank], 0.0, atol = 1e-10))
@test all(eig_val[(rank+1):10] .>= 0)
@test_throws DomainError rand_dm(4, rank = rank)
@test_throws DomainError rand_dm(4, rank = 0)

# bell_state, singlet_state, triplet_states, w_state, ghz_state
e0 = basis(2, 0)
Expand Down