Skip to content

Commit f2f6773

Browse files
authored
BitBasis with number of qubits greater than 64 (#49)
* update * fix tests * update * update shift * update
1 parent 9e49738 commit f2f6773

File tree

9 files changed

+205
-21
lines changed

9 files changed

+205
-21
lines changed

src/BitBasis.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
module BitBasis
22

33
export DitStr, BitStr, @bit_str, @lbit_str, @dit_str, @ldit_str, BitStr64, LongBitStr, DitStr64, LongDitStr, bit_literal
4-
export onehot, buffer
4+
export onehot, buffer, indicator
55
export bitarray, basis, packbits, bfloat, bfloat_r, bint, bint_r, flip
66
export anyone, allone, bmask, baddrs, readbit, setbit, controller
77
export swapbits, ismatch, neg, breflect, btruncate
8+
export LongLongUInt
89

910
include("utils.jl")
11+
include("longlonguint.jl")
1012
include("DitStr.jl")
1113
include("BitStr.jl")
1214
include("bit_operations.jl")

src/BitStr.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ julia> bit"1101"[2]
3636
"""
3737
const BitStr{N,T} = DitStr{2,N,T}
3838
const BitStr64{N} = BitStr{N,Int64}
39-
const LongBitStr{N} = BitStr{N,BigInt}
39+
const LongBitStr{N} = BitStr{N,LongLongUInt{C}} where C
4040
const BitStr{N}(x::T) where {N,T<:IntStorage} = DitStr{2,N}(x)
4141
const BitStr(x::Union{Vector,Tuple}) = DitStr{2}(x)
4242

@@ -99,7 +99,7 @@ end
9999
Long bit string version of `@bit_str` macro.
100100
"""
101101
macro lbit_str(str)
102-
return _parse_dit(Val(2), BigInt, str)
102+
return _parse_dit(Val(2), LongLongUInt, str)
103103
end
104104

105105
"""

src/DitStr.jl

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const UIntStorage = Union{UInt8,UInt16,UInt32,UInt64,UInt128}
1+
const UIntStorage = Union{UInt8,UInt16,UInt32,UInt64,UInt128,LongLongUInt}
22
const IntStorage = Union{Int8,Int16,Int32,Int64,Int128,BigInt,UIntStorage}
33

44
########## DitStr #########
@@ -37,23 +37,27 @@ function DitStr{D,T}(vector::Union{AbstractVector,Tuple}) where {D,T}
3737
val = zero(T)
3838
D_power_k = one(T)
3939
for k in 1:length(vector)
40-
0 <= vector[k] <= D-1 || error("expect 0 or 1, got $(vector[k])")
41-
val += vector[k] * D_power_k
42-
D_power_k *= D
40+
0 <= vector[k] <= D-1 || error("expect 0-$(D-1), got $(vector[k])")
41+
val = accum(Val{D}(), val, vector[k], D_power_k)
42+
D_power_k = _lshift(Val{D}(), D_power_k, 1)
4343
end
4444
return DitStr{D,length(vector),T}(val)
4545
end
46+
# val += x * y
47+
accum(::Val{D}, val, x, y) where D = val + x * y
48+
accum(::Val{2}, val, x, y) = iszero(x) ? val : val y
4649
DitStr{D}(vector::Tuple{T,Vararg{T,N}}) where {N,T,D} = DitStr{D,T}(vector)
4750
DitStr{D}(vector::AbstractVector{T}) where {D,T} = DitStr{D,T}(vector)
4851
DitStr{D,N,T}(val::DitStr) where {D,N,T<:Integer} = convert(DitStr{D,N,T}, val)
4952
DitStr{D,N,T}(val::DitStr{D,N,T}) where {D,N,T<:Integer} = val
5053

5154
const DitStr64{D,N} = DitStr{D,N,Int64}
52-
const LongDitStr{D,N} = DitStr{D,N,BigInt}
55+
const LongDitStr{D,N} = DitStr{D,N,LongLongUInt{C}} where C
56+
LongDitStr{D}(vector::AbstractVector{T}) where {D,T} = DitStr{D,longinttype(length(vector), D)}(vector)
5357

5458
Base.show(io::IO, ditstr::DitStr{D,N,<:Integer}) where {D,N} =
5559
print(io, string(buffer(ditstr), base = D, pad = N), "$(''+D)")
56-
Base.show(io::IO, ditstr::DitStr{D,N,<:BigInt}) where {D,N} =
60+
Base.show(io::IO, ditstr::DitStr{D,N,<:LongLongUInt}) where {D,N} =
5761
print(io, join(map(string, [ditstr[end:-1:1]...])), "$(''+D)")
5862

5963
Base.zero(::Type{DitStr{D,N,T}}) where {D,N,T} = DitStr{D,N,T}(zero(T))
@@ -295,20 +299,29 @@ function parse_dit(::Type{T}, str::String) where {T<:Integer}
295299
end
296300

297301
function _parse_dit(::Val{D}, ::Type{T}, str::AbstractString) where {D, T<:Integer}
302+
TT = T <: LongLongUInt ? longinttype(count(isdigit, str), D) : T
303+
_parse_dit_safe(Val(D), TT, str)
304+
end
305+
306+
function _parse_dit_safe(::Val{D}, ::Type{T}, str::AbstractString) where {D, T<:Integer}
298307
val = zero(T)
299-
k = 1
300-
maxk = T <: BigInt ? Inf : log(typemax(T))/log(D)
308+
k = 0
309+
maxk = max_num_elements(T, D)
301310
for each in reverse(str)
302-
k >= maxk && error("string length is larger than $(maxk), use @ldit_str instead")
311+
k >= maxk-1 && error("string length is larger than $(maxk), use @ldit_str instead")
303312
v = each - '0'
304313
if 0 <= v < D
305-
val += _lshift(Val(D), v, k-1)
314+
val += _lshift(Val(D), T(v), k)
306315
k += 1
307316
elseif each == '_'
308317
continue
309318
else
310-
error("expect char in range 0-$(D-1), got $each at $k-th dit")
319+
error("expect char in range 0-$(D-1), got $each at $(k+1)-th dit")
311320
end
312321
end
313-
return DitStr{D,k-1,T}(val)
322+
return DitStr{D,k,T}(val)
314323
end
324+
325+
max_num_elements(::Type{T}, D::Int) where T<:Integer = floor(Int, log(typemax(T))/log(D))
326+
max_num_elements(::Type{BigInt}, D::Int) = typemax(Int)
327+
max_num_elements(::Type{LongLongUInt{C}}, D::Int) where {C} = max_num_elements(UInt, D) * C

src/bit_operations.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,19 @@ packbits(arr::AbstractArray) = _packbits(arr)
3737
_packbits(arr) =
3838
selectdim(sum(mapslices(x -> x .* (1 .<< (0:size(arr, 1)-1)), arr, dims = 1), dims = 1), 1, 1)
3939

40+
"""
41+
indicator(::Type{T}, k) -> T
42+
43+
Return an integer with `k`-th bit set to 1.
44+
"""
45+
indicator(::Type{T}, k::Int) where T<:Integer = one(T) << (k-1)
46+
4047
"""
4148
btruncate(b, n)
4249
4350
Truncate bits `b` to given length `n`.
4451
"""
45-
btruncate(b::Integer, n) = b & (1 << n - 1)
52+
btruncate(b::T, n) where T<:Integer = b & (one(T) << n - 1)
4653

4754
"""
4855
bfloat(b::Integer; nbits::Int=bit_length(b)) -> Float64
@@ -93,7 +100,7 @@ bmask(args...) = bmask(Int, args...)
93100
bmask(::Type{T}) where {T<:Integer} = zero(T)
94101
bmask(::Type{T}, positions::Int...) where {T<:Integer} = bmask(T, positions)
95102
bmask(::Type{T}, itr) where {T<:Integer} =
96-
isempty(itr) ? 0 : reduce(+, one(T) << (b - 1) for b in itr)
103+
isempty(itr) ? 0 : reduce(+, indicator(T, b) for b in itr)
97104

98105
@inline function bmask(::Type{T}, range::UnitRange{Int})::T where {T<:Integer}
99106
((one(T) << (range.stop - range.start + 1)) - one(T)) << (range.start - 1)
@@ -318,6 +325,6 @@ function controller(::Type{T}, cbits::IntIterator, cvals::IntIterator) where T
318325
do_mask = bmask(T, cbits...)
319326
target =
320327
length(cvals) == 0 ? zero(T) :
321-
mapreduce(xy -> (xy[2] == 1 ? one(T) << T(xy[1] - 1) : zero(T)), |, zip(cbits, cvals))
328+
mapreduce(xy -> (xy[2] == 1 ? indicator(T, xy[1]) : zero(T)), |, zip(cbits, cvals))
322329
return b -> ismatch(b, do_mask, target)
323330
end

src/longlonguint.jl

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
LongLongUInt{C} <: Integer
3+
4+
A `LongLongUInt{C}` is an integer with `C` `UInt` numbers to store the value.
5+
"""
6+
struct LongLongUInt{C} <: Integer
7+
content::NTuple{C, UInt}
8+
function LongLongUInt{C}(content::NTuple{C, UInt}) where {C}
9+
new{C}(content)
10+
end
11+
function LongLongUInt{C}(content::NTuple{C}) where {C}
12+
new{C}(UInt.(content))
13+
end
14+
function LongLongUInt(content::NTuple{C}) where {C}
15+
LongLongUInt{C}(content)
16+
end
17+
end
18+
bsizeof(::LongLongUInt{C}) where C = bsizeof(UInt64) * C
19+
nint(::LongLongUInt{C}) where {C} = C
20+
Base.Int(x::LongLongUInt{1}) = Int(x.content[1])
21+
Base.UInt(x::LongLongUInt{1}) = x.content[1]
22+
Base.zero(::Type{LongLongUInt{C}}) where {C} = LongLongUInt{C}(ntuple(_->UInt(0), C))
23+
Base.zero(::LongLongUInt{C}) where {C} = zero(LongLongUInt{C})
24+
# convert from integers
25+
LongLongUInt{C}(x::T) where {C, T<:Integer} = LongLongUInt{C}(ntuple(i->i==1 ? UInt(x) : zero(UInt), C))
26+
Base.promote_type(::Type{LongLongUInt{C}}, ::Type{Int}) where {C} = LongLongUInt{C}
27+
Base.promote_type(::Type{LongLongUInt{C}}, ::Type{UInt}) where {C} = LongLongUInt{C}
28+
function Base.mod(x::LongLongUInt{C}, D::Int) where {C}
29+
D == 2 ? mod(x.content[end], 2) : error("mod only supports 2")
30+
end
31+
32+
function Base.:(>>)(x::LongLongUInt{C}, y::Int) where {C}
33+
y < 0 && return x << -y
34+
nshift = y ÷ bsizeof(UInt)
35+
nshift_inner = y % bsizeof(UInt)
36+
LongLongUInt(ntuple(C) do k
37+
right = k<=nshift ? zero(UInt) : x.content[k-nshift]
38+
left = k<=(nshift+1) ? zero(UInt) : x.content[k-nshift-1]
39+
(right >> nshift_inner) | (left << (bsizeof(UInt) - nshift_inner))
40+
end
41+
)
42+
end
43+
function Base.:(<<)(x::LongLongUInt{C}, y::Int) where C
44+
y < 0 && return x >> -y
45+
nshift = y ÷ bsizeof(UInt)
46+
nshift_inner = y % bsizeof(UInt)
47+
LongLongUInt(ntuple(C) do k
48+
left = k>C-nshift ? zero(UInt) : x.content[k+nshift]
49+
right = k>C-nshift-1 ? zero(UInt) : x.content[k+nshift+1]
50+
(left << nshift_inner) | (right >> (bsizeof(UInt) - nshift_inner))
51+
end
52+
)
53+
end
54+
function indicator(::Type{LongLongUInt{C}}, i::Int) where C
55+
k = (i-1) ÷ bsizeof(UInt)
56+
LongLongUInt{C}(ntuple(j->j==C-k ? indicator(UInt, i-k*bsizeof(UInt)) : zero(UInt), C))
57+
end
58+
for OP in [:&, :|, :xor]
59+
@eval Base.$OP(x::LongLongUInt{C}, y::LongLongUInt{C}) where {C} = LongLongUInt{C}($OP.(x.content, y.content))
60+
end
61+
Base.:(~)(x::LongLongUInt{C}) where {C} = LongLongUInt{C}((~).(x.content))
62+
function Base.:(+)(x::LongLongUInt{C}, y::LongLongUInt{C}) where {C}
63+
return LongLongUInt(_sadd(x.content, y.content, false))
64+
end
65+
function _sadd(x::NTuple{1,UInt}, y::NTuple{1,UInt}, c::Bool)
66+
return (x[1] + y[1] + c,)
67+
end
68+
function _sadd(x::NTuple{C,UInt}, y::NTuple{C,UInt}, c::Bool) where {C}
69+
v1, c1 = Base.add_with_overflow(x[C], y[C])
70+
if c
71+
v2, c2 = Base.add_with_overflow(v1, c)
72+
c = c1 || c2
73+
return (_sadd(x[1:C-1], y[1:C-1], c)..., v2)
74+
else
75+
return (_sadd(x[1:C-1], y[1:C-1], c1)..., v1)
76+
end
77+
end
78+
Base.count_ones(x::LongLongUInt) = sum(count_ones, x.content)
79+
80+
function longinttype(n::Int, D::Int)
81+
N = ceil(Int, n * log2(D))
82+
C = (N-1) ÷ bsizeof(UInt) + 1
83+
return LongLongUInt{C}
84+
end
85+
Base.hash(x::LongLongUInt) = hash(x.content)

test/BitStr.jl

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ using Test, BitBasis
66
@test bit"10_100_11" == bit"1010011"
77
@test bit"10_100_11" != bit"01010011"
88
@test bit"1011" === bit_literal(Int64.((1, 1, 0, 1))...)
9-
@test BitBasis._parse_dit(Val(2), BigInt, "10101010101010101010101010101010010101010101"^10) isa LongBitStr
9+
@test BitBasis._parse_dit(Val(2), BigInt, "10101010101010101010101010101010010101010101"^10) isa BitStr
10+
@test BitBasis._parse_dit(Val(2), LongLongUInt, "10101010101010101010101010101010010101010101"^10) isa LongBitStr
1011
@test_throws ErrorException BitBasis._parse_dit(Val(2),
1112
Int64,
1213
"10101010101010101010101010101010010101010101"^10,
@@ -44,7 +45,7 @@ end
4445
@test res == 2
4546
@test typeof(res) == T
4647

47-
@test convert(BitStr{2,BigInt}, convert(T, bit"10")) == lbit"10"
48+
@test convert(BitStr{2,LongLongUInt{1}}, convert(T, bit"10")) == lbit"10"
4849
@test convert(BitStr{2,Int64}, convert(T, bit"10")) === bit"10"
4950
end
5051
end
@@ -175,3 +176,10 @@ end
175176
@test bfloat_r(x) === 14 / 2^6
176177
@test btruncate(x, 3) === bit"000110"
177178
end
179+
180+
@testset "LongDitStr" begin
181+
x = LongBitStr(rand(0:1, 64 * 3))
182+
@test bsizeof(x.buf) == 64 * 3
183+
x = LongBitStr(rand(0:1, 64 * 3+1))
184+
@test bsizeof(x.buf) == 64 * 4
185+
end

test/DitStr.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ using BitBasis, Test
2424
@test_throws ErrorException BitBasis.parse_dit(Int64, "12341111111111111111111111111111111111111111111111111111111;5")
2525

2626
@test hash(x) isa UInt64
27-
end
27+
end

test/longlonguint.jl

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Test, BitBasis
2+
3+
@testset "longlonguint" begin
4+
x = LongLongUInt((3,))
5+
@test Int(x) === 3
6+
@test bsizeof(x) == 64
7+
@test BitBasis.nint(x) == 1
8+
9+
x = LongLongUInt((3, 6))
10+
@test zero(x) == LongLongUInt((0, 0))
11+
@test x << 1 == LongLongUInt((6, 12))
12+
@test x >> 1 == LongLongUInt((UInt(1), UInt(3) + UInt(1)<<63))
13+
@test count_ones(x) == 4
14+
@test ~x == LongLongUInt((~UInt(3), ~UInt(6)))
15+
16+
y = LongLongUInt((5, 7))
17+
@test x & y == LongLongUInt((1, 6))
18+
@test x | y == LongLongUInt((7, 7))
19+
@test x y == LongLongUInt((6, 1))
20+
@test x + y == LongLongUInt((8, 13))
21+
22+
# add with overflow
23+
z = LongLongUInt((UInt(17), typemax(UInt)-1))
24+
@test z + x == LongLongUInt((21, 4))
25+
@test (@allocated z + x) == 0
26+
27+
# maximum number of elements
28+
BitBasis.max_num_elements(LongLongUInt{2}, 2) == 80
29+
BitBasis.max_num_elements(LongLongUInt{2}, 4) == 40
30+
BitBasis.max_num_elements(UInt, 2) == 64
31+
BitBasis.max_num_elements(Int, 2) == 63
32+
end
33+
34+
@testset "shift" begin
35+
x = LongLongUInt((3, 6))
36+
@test x << 0 == LongLongUInt((3, 6))
37+
@test x << 64 == LongLongUInt((6, 0))
38+
@test x << 65 == LongLongUInt((6<<1, 0))
39+
@test x >> 64 == LongLongUInt((0, 3))
40+
@test x >> 65 == LongLongUInt((0, 1))
41+
@test x << -65 == LongLongUInt((0, 1))
42+
@test x >> -65 == LongLongUInt((6<<1, 0))
43+
44+
x = LongLongUInt((3, 6, 7))
45+
@test x >> 66 == LongLongUInt((UInt(0), UInt(0), UInt(3) << 62 + UInt(1)))
46+
@test x << 63 == LongLongUInt((UInt(1) << 63 + UInt(6) >> 1, UInt(7) >> 1, UInt(1)<<63))
47+
end
48+
49+
@testset "longinttype" begin
50+
@test BitBasis.longinttype(1, 2) == LongLongUInt{1}
51+
@test BitBasis.longinttype(64, 2) == LongLongUInt{1}
52+
@test BitBasis.longinttype(65, 2) == LongLongUInt{2}
53+
end
54+
55+
@testset "hash" begin
56+
@test hash(LongLongUInt((3, 6))) == hash(LongLongUInt((3, 6)))
57+
@test hash(LongLongUInt((3, 6))) != hash(LongLongUInt((6, 3)))
58+
end
59+
60+
@testset "indicator" begin
61+
@test indicator(LongLongUInt{1}, 1) == LongLongUInt((1,))
62+
@test indicator(LongLongUInt{1}, 64) == LongLongUInt((UInt(1)<<63,))
63+
@test indicator(LongLongUInt{2}, 64) == LongLongUInt((zero(UInt), UInt(1)<<63))
64+
@test indicator(LongLongUInt{2}, 65) == LongLongUInt((1, 0))
65+
end

test/runtests.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,9 @@ end
3030
include("iterate_control.jl")
3131
end
3232

33+
@testset "longlonguint" begin
34+
include("longlonguint.jl")
35+
end
36+
3337
DocMeta.setdocmeta!(BitBasis, :DocTestSetup, :(using BitBasis); recursive=true)
3438
Documenter.doctest(BitBasis; manual=false)

0 commit comments

Comments
 (0)