diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fbea819e..84bc5139e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased](https://github.com/qutip/QuantumToolbox.jl/tree/main) +- Add error message for bad input in state/operator generating functions ([#603]) ## [v0.39.1] Release date: 2025-11-19 @@ -381,3 +382,4 @@ Release date: 2024-11-13 [#589]: https://github.com/qutip/QuantumToolbox.jl/issues/589 [#591]: https://github.com/qutip/QuantumToolbox.jl/issues/591 [#596]: https://github.com/qutip/QuantumToolbox.jl/issues/596 +[#603]: https://github.com/qutip/QuantumToolbox.jl/issues/603 diff --git a/src/qobj/operators.jl b/src/qobj/operators.jl index fb0c2041f..b921e349a 100644 --- a/src/qobj/operators.jl +++ b/src/qobj/operators.jl @@ -477,7 +477,7 @@ _Jordan_Wigner(N::Int, j::Int, op::QuantumObject{Operator}) = _Jordan_Wigner(Val function _Jordan_Wigner(::Val{N}, j::Int, op::QuantumObject{Operator}) where {N} (N < 1) && throw(ArgumentError("The total number of sites (N) cannot be less than 1")) - ((j > N) || (j < 1)) && throw(ArgumentError("The site index (j) should satisfy: 1 ≤ j ≤ N")) + (1 <= j <= N) || throw(ArgumentError("The site index (j) should satisfy: 1 ≤ j ≤ N")) σz = sigmaz().data Z_tensor = kron(1, 1, fill(σz, j - 1)...) @@ -493,8 +493,12 @@ end Generates the projection operator ``\hat{O} = |i \rangle\langle j|`` with Hilbert space dimension `N`. """ -projection(N::Int, i::Int, j::Int) = - QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator(), dims = N) +function projection(N::Int, i::Int, j::Int) + (0 <= i < N) || throw(ArgumentError("Invalid argument i, must satisfy: 0 ≤ i ≤ N-1")) + (0 <= j < N) || throw(ArgumentError("Invalid argument j, must satisfy: 0 ≤ j ≤ N-1")) + + return QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator(), dims = N) +end @doc raw""" tunneling(N::Int, m::Int=1; sparse::Union{Bool,Val{<:Bool}}=Val(false)) diff --git a/src/qobj/states.jl b/src/qobj/states.jl index dd79bee8d..38cddd6c9 100644 --- a/src/qobj/states.jl +++ b/src/qobj/states.jl @@ -2,7 +2,7 @@ Functions for generating (common) quantum states. =# -export zero_ket, fock, basis, coherent, rand_ket +export zero_ket, fock, 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 @@ -25,6 +25,7 @@ zero_ket(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}) = @doc raw""" fock(N::Int, j::Int=0; dims::Union{Int,AbstractVector{Int},Tuple}=N, sparse::Union{Bool,Val}=Val(false)) + basis(N::Int, j::Int=0; dims::Union{Int,AbstractVector{Int},Tuple}=N, sparse::Union{Bool,Val}=Val(false)) Generates a fock state ``\ket{\psi}`` of dimension `N`. @@ -32,8 +33,12 @@ It is also possible to specify the list of dimensions `dims` if different subsys !!! warning "Beware of type-stability!" If you want to keep type stability, it is recommended to use `fock(N, j, dims=dims, sparse=Val(sparse))` instead of `fock(N, j, dims=dims, sparse=sparse)`. Consider also to use `dims` as a `Tuple` or `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) instead of `Vector`. See [this link](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-value-type) and the [related Section](@ref doc:Type-Stability) about type stability for more details. + +!!! note + `basis(N, j; dims = dims, sparse = sparse)` is a synonym of `fock(N, j; dims = dims, sparse = sparse)`. """ function fock(N::Int, j::Int = 0; dims::Union{Int,AbstractVector{Int},Tuple} = N, sparse::Union{Bool,Val} = Val(false)) + (0 <= j < N) || throw(ArgumentError("Invalid argument j, must satisfy: 0 ≤ j ≤ N-1")) if getVal(sparse) array = sparsevec([j + 1], [1.0 + 0im], N) else @@ -42,18 +47,6 @@ function fock(N::Int, j::Int = 0; dims::Union{Int,AbstractVector{Int},Tuple} = N return QuantumObject(array; type = Ket(), dims = dims) end -@doc raw""" - basis(N::Int, j::Int = 0; dims::Union{Int,AbstractVector{Int},Tuple}=N) - -Generates a fock state like [`fock`](@ref). - -It is also possible to specify the list of dimensions `dims` if different subsystems are present. - -!!! warning "Beware of type-stability!" - If you want to keep type stability, it is recommended to use `basis(N, j, dims=dims)` with `dims` as a `Tuple` or `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) instead of `Vector`. See [this link](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-value-type) and the [related Section](@ref doc:Type-Stability) about type stability for more details. -""" -basis(N::Int, j::Int = 0; dims::Union{Int,AbstractVector{Int},Tuple} = N) = fock(N, j, dims = dims) - @doc raw""" coherent(N::Int, α::Number) @@ -203,7 +196,7 @@ function spin_state(j::Real, m::Real) throw(ArgumentError("Invalid eigenvalue m: (j - m) must be a non-negative integer.")) (m < (-j)) && throw(ArgumentError("Invalid eigenvalue m, must satisfy: -j ≤ m ≤ j")) - return basis(Int(J), Int(Δ)) + return fock(Int(J), Int(Δ)) end @doc raw""" @@ -318,6 +311,8 @@ Returns the `n`-qubit [W-state](https://en.wikipedia.org/wiki/W_state): If you want to keep type stability, it is recommended to use `w_state(Val(n))` instead of `w_state(n)`. See [this link](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-value-type) and the [related Section](@ref doc:Type-Stability) for more details. """ function w_state(::Val{n}) where {n} + (n >= 2) || throw(ArgumentError("Invalid argument n, must satisfy: n ≥ 2")) + nzind = 2 .^ (0:(n-1)) .+ 1 nzval = fill(ComplexF64(1 / sqrt(n)), n) data = zeros(ComplexF64, 2^n) @@ -341,6 +336,9 @@ Here, `d` specifies the dimension of each qudit. Default to `d=2` (qubit). If you want to keep type stability, it is recommended to use `ghz_state(Val(n))` instead of `ghz_state(n)`. See [this link](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-value-type) and the [related Section](@ref doc:Type-Stability) for more details. """ function ghz_state(::Val{n}; d::Int = 2) where {n} + (n >= 2) || throw(ArgumentError("Invalid argument n, must satisfy: n ≥ 2")) + (d >= 2) || throw(ArgumentError("Invalid argument d, must satisfy: d ≥ 2")) + nzind = collect((0:(d-1)) .* Int((d^n - 1) / (d - 1)) .+ 1) nzval = fill(ComplexF64(1 / sqrt(d)), d) data = zeros(ComplexF64, d^n) diff --git a/src/qobj/synonyms.jl b/src/qobj/synonyms.jl index 224c73b59..fa73ad396 100644 --- a/src/qobj/synonyms.jl +++ b/src/qobj/synonyms.jl @@ -4,6 +4,7 @@ Synonyms of the functions for QuantumObject export Qobj, QobjEvo, shape, isherm export trans, dag, matrix_element, unit +export basis export tensor, ⊗ export qeye, qeye_like, qzero_like export vector_to_operator, operator_to_vector @@ -33,6 +34,8 @@ const trans = transpose const dag = adjoint +const basis = fock + @doc raw""" matrix_element(i::QuantumObject, A::QuantumObject, j::QuantumObject) diff --git a/test/core-test/states_and_operators.jl b/test/core-test/states_and_operators.jl index 6d0aef108..ad2814233 100644 --- a/test/core-test/states_and_operators.jl +++ b/test/core-test/states_and_operators.jl @@ -20,6 +20,7 @@ # fock, basis, and fock_dm @test fock_dm(4; dims = (2, 2), sparse = true) ≈ ket2dm(basis(4; dims = (2, 2))) @test_throws DimensionMismatch fock(4; dims = 2) + @test_throws ArgumentError fock(4, 4) end @testset "coherent state" begin @@ -121,16 +122,21 @@ @test_throws ArgumentError bell_state(0, 2) @test_throws ArgumentError bell_state(3, 1) @test_throws ArgumentError bell_state(2, 3) + @test_throws ArgumentError w_state(1) + @test_throws ArgumentError ghz_state(1) + @test_throws ArgumentError ghz_state(2; d = 1) end @testset "bosonic operators" begin # destroy, create, num, position, momentum n = 10 + i, j = rand(0:(n-1), 2) a = destroy(n) ad = create(n) N = num(n) x = position(n) p = momentum(n) + Pij = projection(n, i, j) @test isoper(x) @test isoper(p) @test a.dims == ad.dims == N.dims == x.dims == p.dims == [n] @@ -138,6 +144,9 @@ @test commutator(N, a) ≈ -a @test commutator(N, ad) ≈ ad @test all(diag(commutator(x, p))[1:(n-1)] .≈ 1.0im) + @test fock(n, i) == Pij * fock(n, j) + @test_throws ArgumentError projection(n, n, 0) + @test_throws ArgumentError projection(n, 0, n) end @testset "displacement and squeezing operators" begin