diff --git a/src/BitStringAddresses/bosefs.jl b/src/BitStringAddresses/bosefs.jl index 8f7bfe410..8ac94c04e 100644 --- a/src/BitStringAddresses/bosefs.jl +++ b/src/BitStringAddresses/bosefs.jl @@ -24,7 +24,7 @@ automatically based on the properties of the address. particles in `bs` is equal to `N`. * [`@fs_str`](@ref): Addresses are sometimes printed in a compact manner. This - representation can also be used as a constructor. See the last example below. + representation can also be used as a constructor. See the examples below. # Examples @@ -32,7 +32,7 @@ automatically based on the properties of the address. julia> BoseFS{6,5}(0, 1, 2, 3, 0) BoseFS{6,5}(0, 1, 2, 3, 0) -julia> BoseFS([abs(i - 3) ≤ 1 ? i - 1 : 0 for i in 1:5]) +julia> BoseFS(abs(i - 3) ≤ 1 ? i - 1 : 0 for i in 1:5) BoseFS{6,5}(0, 1, 2, 3, 0) julia> BoseFS(5, 2 => 1, 3 => 2, 4 => 3) @@ -97,7 +97,8 @@ function BoseFS{N,M}(onr::Union{AbstractArray{<:Integer},NTuple{M,<:Integer}}) w end return BoseFS{N,M,S}(from_bose_onr(S, onr)) end -function BoseFS(onr::Union{AbstractArray,Tuple}) +function BoseFS(onr) # single argument constructor + onr = Tuple(onr) M = length(onr) N = sum(onr) return BoseFS{N,M}(onr) @@ -106,10 +107,13 @@ BoseFS(vals::Integer...) = BoseFS(vals) # specify occupation numbers BoseFS(val::Integer) = BoseFS((val,)) # single mode address BoseFS{N,M}(vals::Integer...) where {N,M} = BoseFS{N,M}(vals) +# Sparse constructors BoseFS(M::Integer, pairs::Pair...) = BoseFS(M, pairs) BoseFS(M::Integer, pairs) = BoseFS(sparse_to_onr(M, pairs)) BoseFS{N,M}(pairs::Pair...) where {N,M} = BoseFS{N,M}(pairs) BoseFS{N,M}(pairs) where {N,M} = BoseFS{N,M}(sparse_to_onr(M, pairs)) +BoseFS(pairs::Pair...) = throw(ArgumentError("number of modes must be provided")) + function print_address(io::IO, b::BoseFS{N,M}; compact=false) where {N,M} if compact && b.bs isa SortedParticleList @@ -259,7 +263,7 @@ end Compute the new address of a hopping event for the Hubbard model. Returns the new address and the square root of product of occupation numbers of the involved modes -multiplied by a term consistent with boundary condition as the `value`. +multiplied by a term consistent with boundary condition as the `value`. The following boundary conditions are supported: * `:periodic`: hopping over the boundary gives does not change the `value`. diff --git a/src/BitStringAddresses/fermifs.jl b/src/BitStringAddresses/fermifs.jl index 43f9dc3a6..94a548292 100644 --- a/src/BitStringAddresses/fermifs.jl +++ b/src/BitStringAddresses/fermifs.jl @@ -24,7 +24,7 @@ chosen automatically based on the properties of the address. particles in `bs` is equal to `N`, or whether each mode only contains one particle. * [`@fs_str`](@ref): Addresses are sometimes printed in a compact manner. This - representation can also be used as a constructor. See the last example below. + representation can also be used as a constructor. See the examples below. # Examples @@ -95,7 +95,8 @@ function FermiFS{N,M}(onr::Union{AbstractArray{<:Integer},NTuple{M,<:Integer}}) end return FermiFS{N,M,S}(from_fermi_onr(S, onr)) end -function FermiFS(onr::Union{AbstractArray,Tuple}) +function FermiFS(onr) + onr = Tuple(onr) M = length(onr) N = sum(onr) return FermiFS{N,M}(onr) @@ -109,6 +110,7 @@ FermiFS(M::Integer, pairs::Pair...) = FermiFS(M, pairs) FermiFS(M::Integer, pairs) = FermiFS(sparse_to_onr(M, pairs)) FermiFS{N,M}(pairs::Vararg{Pair,N}) where {N,M} = FermiFS{N,M}(pairs) FermiFS{N,M}(pairs) where {N,M} = FermiFS{N,M}(sparse_to_onr(M, pairs)) +FermiFS(pairs::Pair...) = throw(ArgumentError("number of modes must be provided")) function print_address(io::IO, f::FermiFS{N,M}; compact=false) where {N,M} if compact && f.bs isa SortedParticleList diff --git a/src/BitStringAddresses/occupationnumberfs.jl b/src/BitStringAddresses/occupationnumberfs.jl index 5383c3300..33a2349de 100644 --- a/src/BitStringAddresses/occupationnumberfs.jl +++ b/src/BitStringAddresses/occupationnumberfs.jl @@ -2,16 +2,19 @@ OccupationNumberFS{M,T} <: SingleComponentFockAddress Address type that stores the occupation numbers of a single component bosonic Fock state with `M` modes. The occupation numbers must fit into the type `T <: Unsigned`. The number of -particles is runtime data, and can be retrieved with `num_particles(address)`. +particles is runtime data, and can be retrieved with [`num_particles(address)`](@ref). # Constructors - `OccupationNumberFS(val::Integer...)`: Construct from occupation numbers. Must be < 256 to fit into `UInt8`. +- `OccupationNumberFS(M, pairs::Pair...)`: Construct from a sparse representation with + `M` modes and pairs of mode index and occupation number. - `OccupationNumberFS{[M,T]}(onr)`: Construct from collection `onr` with `M` occupation - numbers with type `T`. If unspecified, the type `T` of the occupation numbers is inferred - from the type of the arguments. + numbers with optional type `T`. `onr` may be a tuple, an array, or a generator. +- `OccupationNumberFS{M[,T]}()`: Construct a vacuum state with `M` modes. If `T` is + unspecified, `UInt8` is used. - `OccupationNumberFS(fs::BoseFS)`: Construct from [`BoseFS`](@ref). -- With shortform macro [`@fs_str`](@ref). Specify the number of +- With short form macro [`@fs_str`](@ref). Specify the number of significant bits in braces. See example below. # Examples @@ -24,36 +27,78 @@ true julia> num_particles(ofs) 6 + +julia> OccupationNumberFS{5}() # vacuum state with 5 modes +OccupationNumberFS{5, UInt8}(0, 0, 0, 0, 0) + +julia> OccupationNumberFS(i for i in 1:3) # use list comprehension +OccupationNumberFS{3, UInt8}(1, 2, 3) + +julia> OccupationNumberFS(4, 1=>2, 3=>4) # sparse constructor +OccupationNumberFS{4, UInt8}(2, 0, 4, 0) + +julia> OccupationNumberFS(5, i=>i^2 for i in 2:4) # sparse with list comprehension +OccupationNumberFS{5, UInt8}(0, 4, 9, 16, 0) ``` """ struct OccupationNumberFS{M,T<:Unsigned} <: SingleComponentFockAddress{missing,M} onr::SVector{M,T} + + function OccupationNumberFS{M,T}(args...) where {M,T<:Unsigned} + return new(SVector{M,T}(args...)) + end end -function OccupationNumberFS{M,T}(args...) where {M,T} - return OccupationNumberFS(SVector{M,T}(args...)) +function OccupationNumberFS(sv::SVector{M,T}) where {M,T<:Unsigned} + return OccupationNumberFS{M,T}(sv) end -function OccupationNumberFS(args...) - sv = SVector(args...) - all(isinteger, sv) || throw(ArgumentError("all arguments must be integers")) - all(x -> x ≥ 0, sv) || throw(ArgumentError("all arguments must be non-negative")) - all(x -> x < 256, sv) || throw(ArgumentError("arguments don't fit in a byte, specify type")) - return OccupationNumberFS(SVector{length(sv),UInt8}(args...)) +function OccupationNumberFS(arg) + t = Tuple(arg) + return OccupationNumberFS{length(t)}(t) +end + +function OccupationNumberFS(args::Number...) + return OccupationNumberFS(SVector(args)) +end +OccupationNumberFS(arg::Number) = OccupationNumberFS{1}(tuple(arg)) # to resolve ambiguity +function OccupationNumberFS{M}(args::Number...) where {M} + return OccupationNumberFS{M}(SVector{M}(args)) end function OccupationNumberFS{M}(args...) where M - sv = SVector{M}(args...) + t = Tuple(args...) + if all(x -> isa(x, Pair), t) + return OccupationNumberFS{M}(t) + end + sv = SVector{M}(t) all(isinteger, sv) || throw(ArgumentError("all arguments must be integers")) all(x -> x ≥ 0, sv) || throw(ArgumentError("all arguments must be non-negative")) all(x -> x < 256, sv) || throw(ArgumentError("arguments don't fit in a byte, specify type")) - return OccupationNumberFS(SVector{M,UInt8}(args...)) + return OccupationNumberFS{M,UInt8}(args...) end function OccupationNumberFS(fs::BoseFS{N,M}) where {N,M} return OccupationNumberFS{M,select_int_type(N)}(onr(fs)) end +# convenience constructors for vacuum state +function OccupationNumberFS{M,T}() where {M,T<:Unsigned} + return OccupationNumberFS(SVector{M,T}(zero(T) for _ in 1:M)) +end +OccupationNumberFS{M}() where {M} = OccupationNumberFS{M,UInt8}() + +# Sparse constructors +OccupationNumberFS(M::Number, pairs::Pair...) = OccupationNumberFS(Int(M), pairs) +OccupationNumberFS(M::Number, pairs) = OccupationNumberFS(sparse_to_onr(Int(M), pairs)) +OccupationNumberFS{M}(pairs::Pair...) where {M} = OccupationNumberFS{M}(pairs) + +function OccupationNumberFS{M}(pairs::NTuple{<:Any,Pair}) where {M} + OccupationNumberFS{M}(sparse_to_onr(M, pairs)) +end + +OccupationNumberFS(pairs::Pair...) = throw(ArgumentError("number of modes must be provided")) + function print_address(io::IO, ofs::OccupationNumberFS{M,T}; compact=false) where {M,T} if compact BITS = sizeof(T) * 8 @@ -68,13 +113,13 @@ Base.reverse(ofs::OccupationNumberFS) = OccupationNumberFS(reverse(ofs.onr)) onr(ofs::OccupationNumberFS) = ofs.onr function Base.isless(a::OccupationNumberFS{M}, b::OccupationNumberFS{M}) where {M} # equivalent to `isless(reverse(a.onr), reverse(b.onr))` + # reversing the order here to make it consistent with BoseFS i = length(a.onr) while i > 1 && a.onr[i] == b.onr[i] i -= 1 end return isless(a.onr[i], b.onr[i]) end -# reversing the order here to make it consistent with BoseFS Base.:(==)(a::OccupationNumberFS, b::OccupationNumberFS) = a.onr == b.onr Base.hash(ofs::OccupationNumberFS, h::UInt) = hash(ofs.onr, h) diff --git a/src/BitStringAddresses/sortedparticlelist.jl b/src/BitStringAddresses/sortedparticlelist.jl index 2e094dc0d..d5ac18551 100644 --- a/src/BitStringAddresses/sortedparticlelist.jl +++ b/src/BitStringAddresses/sortedparticlelist.jl @@ -1,16 +1,16 @@ """ - select_int_type(M) + select_int_type(n) -Select unsigned integer type that can hold values up to `M`. +Select unsigned integer type that can hold values up to `n`. """ -function select_int_type(M) - if M ≤ 0 - throw(ArgumentError("`M` must be positive!")) - elseif M ≤ typemax(UInt8) +function select_int_type(n) + if n < 0 + throw(ArgumentError("`n` must be a non-negative integer!")) + elseif n ≤ typemax(UInt8) return UInt8 - elseif M ≤ typemax(UInt16) + elseif n ≤ typemax(UInt16) return UInt16 - elseif M ≤ typemax(UInt32) + elseif n ≤ typemax(UInt32) return UInt32 else return UInt64 @@ -20,7 +20,7 @@ end """ SortedParticleList{N,M,T<:Unsigned} -Type for storing sparse fock states. Stores the mode number of each particle as an entry +Type for storing sparse Fock states. Stores the mode number of each particle as an entry with only its mode stored. The entries are always kept sorted. Iterating over `SortedParticleList`s yields occupied modes as a tuple of occupation number, diff --git a/test/BitStringAddresses.jl b/test/BitStringAddresses.jl index efe35896d..666977baa 100644 --- a/test/BitStringAddresses.jl +++ b/test/BitStringAddresses.jl @@ -184,7 +184,7 @@ end @test_throws ArgumentError BoseFS(10, 11 => 1) @test_throws ArgumentError BoseFS(10, 10 => -1) - @test_throws MethodError BoseFS(10 => 1) + @test_throws ArgumentError BoseFS(10 => 1) end @testset "onr" begin middle_full_onr = onr(middle_full) @@ -461,7 +461,9 @@ end end @testset "OccupationNumberFS with multiple arguments" begin - @test isa(OccupationNumberFS(1, 2, 3), OccupationNumberFS{3, UInt8}) + @test OccupationNumberFS(i for i in 1:3) == OccupationNumberFS(1, 2, 3) + @test isa(OccupationNumberFS{3,UInt32}(i for i in 1:3), OccupationNumberFS{3,UInt32}) + @test isa(OccupationNumberFS(1, 2, 3), OccupationNumberFS{3,UInt8}) @test_throws ArgumentError OccupationNumberFS(1.1, 2, 3) @test_throws ArgumentError OccupationNumberFS(-1, 2, 3) @test_throws ArgumentError OccupationNumberFS(1, 2, 300) @@ -479,6 +481,20 @@ end @test isa(OccupationNumberFS(fs), OccupationNumberFS{2, UInt8}) fs = BoseFS(1, 333) @test isa(OccupationNumberFS(fs), OccupationNumberFS{2,UInt16}) + fs = BoseFS(0, 0) + @test isa(OccupationNumberFS(fs), OccupationNumberFS{2,UInt8}) + end + + @testset "OccupationNumberFS with sparse constructor" begin + @test OccupationNumberFS(2, 2=>4) == OccupationNumberFS(0, 4) + @test OccupationNumberFS{2}(2 => 4) == OccupationNumberFS(2, 2 => 4) + @test OccupationNumberFS(5, i => i + 1 for i in 1:3) == + OccupationNumberFS{5}(i => i + 1 for i in 1:3) == + OccupationNumberFS{5}(Tuple(i => i + 1 for i in 1:3)) == + OccupationNumberFS(5, 1 => 2, 2 => 3, 3 => 4) == + OccupationNumberFS{5,UInt8}(2, 3, 4, 0, 0) + @test OccupationNumberFS{5}(i => i^2 for i in 1:5) == + OccupationNumberFS(5, i => i^2 for i in 1:5) end @testset "Printing and parsing OccupationNumberFS" begin