Bit strings
This package provides a string literal @bit_str
to represent bit strings.
julia> b = bit"11100"
11100 ₍₂₎
julia> BitStr{5}(0b11100) # convert from integer
11100 ₍₂₎
julia> bit_literal(0, 0, 1, 1, 1) # from bit literals
11100 ₍₂₎
julia> [b...] # convert to a vector of bits
5-element Vector{Int64}: 0 0 1 1 1
julia> b[2] # indexing the 2nd bit
0
julia> b.buf # the storage type is `Int64` by default
28
julia> typeof(b)
DitStr{2, 5, Int64}
julia> bit_length(b) # the length of bit string
5
The type of b
is DitStr{2, 5, Int64}
, which means it is a bit string with 5 bits and the storage type is Int64
. The buf
field is the integer representation of the bit string. Bit strings are represented as integers in the little-endian order, e.g. integer 28
represents the bit string 11100
.

The bit_literal
function uses the array order to represent the bit string, which is different from the bit string literal, i.e. the leftmost bit is the least significant bit in the bit_literal
function.
To represent bit strings with more than 64 bits, one can specify the storage type as Int128
or BigInt
.
julia> b = bit_literal(rand(BigInt[0, 1], 200)...)
00011011110111110000000001100000101010011000111010101011011101000010110111000010001011001111010000111001110000110010011100011100000000000001010010100000000100111110101110011010101111010011111010111101 ₍₂₎
julia> typeof(b)
BitStr{200, BigInt} (alias for DitStr{2, 200, BigInt})
Dit Strings
A nary basis is a generalization of a binary basis by changing the base from 2 to n. . A dit string is a nary basis with a given base. The @dit_str
string literal is used to represent dit strings. For example, to represent a dit string with base 3, one can use the following code.
julia> d = dit"12210;3"
12210 ₍₃₎
julia> typeof(d)
DitStr64{3, 5} (alias for DitStr{3, 5, Int64})
The operations on dit strings are similar to those on bit strings.
Concatenation and Repetition
To concatenate and repeat bit strings, one can use join
and repeat
functions.
julia> join([bit"101" for i in 1:10]...) # concatenate bit strings
101101101101101101101101101101 ₍₂₎
julia> repeat(bit"101", 2) # repeat bit string
101101 ₍₂₎
Readout
To readout bits, one can use readbit
and baddrs
functions.

julia> readbit(bit"11100", 2, 3) # read the 2nd and 3rd bits as `x₃x₂`
00010 ₍₂₎
julia> baddrs(bit"11100") # locations of one bits
3-element Vector{Int64}: 3 4 5
A bit string can be read out as numbers in the following ways:
bint
, the integer itselfbint_r
, the integer with bits small-big end reflected.bfloat
, the float point number $0.σ₁σ₂ \cdots σ_n$.bfloat_r
, the float point number $0.σ_n \cdots σ₂σ₁$.
These functions are useful in quantum computing algorithms such as phase estimation and HHL.

julia> bint(bit"010101")
21
julia> bint_r(bit"010101")
42
julia> bfloat(bit"010101")
0.65625
julia> bfloat_r(bit"010101")
0.328125
Modification
To flip all bits, one can use the neg
function.
julia> neg(bit"1011") # flip all bits
0100 ₍₂₎
To truncate bits, one can use the btruncate
function.
julia> btruncate(bit"1011", 2) # only keep the first 2 qubits
0011 ₍₂₎
To change the order of bits, one can use breflect
function.

julia> breflect(bit"1011") # reflect little end and big end
1101 ₍₂₎
Masked Operations
One can use bmask
to generate a mask for bit strings, and then use the mask to perform operations like allone
, anyone
, ismatch
, flip
, setbit
, swapbits
, etc.
julia> mask = bmask(BitStr{4, Int}, 1,3,4) # mask bits 1, 3, 4
1101 ₍₂₎
By coloring the masked positions in light blue, we have

julia> allone(bit"1011", mask) # true if all masked positions are 1
false
julia> anyone(bit"1011", mask) # true if any masked positions is 1
true
julia> ismatch(bit"1011", mask, bit"1001") # true if masked part matches `1001`
true
julia> flip(bit"1011", mask) # flip masked positions: 1, 3, 4
0110 ₍₂₎
julia> setbit(bit"1011", bit"1100") # set masked positions to 1
1111 ₍₂₎
julia> swapbits(bit"1011", bit"1100") # swap masked positions
0111 ₍₂₎
Hamming Distance
One can calculate the Hamming distance between two bit strings by bdistance
function.
julia> bdistance(bit"11100", bit"10101") # Hamming distance
2
Hilbert Space
The basis
function is used to iterate over the basis of a given number of bits.
julia> itr = basis(BitStr{4, Int})
0000 ₍₂₎:1111 ₍₂₎
julia> collect(itr)
16-element Vector{DitStr{2, 4, Int64}}: 0000 ₍₂₎ 0001 ₍₂₎ 0010 ₍₂₎ 0011 ₍₂₎ 0100 ₍₂₎ 0101 ₍₂₎ 0110 ₍₂₎ 0111 ₍₂₎ 1000 ₍₂₎ 1001 ₍₂₎ 1010 ₍₂₎ 1011 ₍₂₎ 1100 ₍₂₎ 1101 ₍₂₎ 1110 ₍₂₎ 1111 ₍₂₎
Iterating over basis in a controlled way plays an important role in quantum simulation. The itercontrol
function is used to iterate over basis in a controlled way. For example, if we want to iterate over the basis of 7 qubits, and we only want to iterate over the basis with the 1st, 3rd, 4th, and 7th qubits being 1, 0, 1, and 0, respectively, we can use the following code.
julia> for each in itercontrol(7, [1, 3, 4, 7], (1, 0, 1, 0)) println(string(each, base=2, pad=7)) end
0001001 0001011 0011001 0011011 0101001 0101011 0111001 0111011
julia> v = onehot(bit"11100") # the one hot vector representation of given bits
32-element Vector{ComplexF64}: 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im ⋮ 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 1.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.0 + 0.0im
julia> reorder(v, (3,2,1,5,4)) ≈ onehot(bit"11001") # change the order of bits
true
julia> invorder(v) ≈ onehot(bit"00111") # change the order of bits
true
BitArray Utilities
Utilities are provided to cast between integers and the BitArray
type in Julia standard library.
bitarray(integers, nbits)
, transform integers toBitArray
.packabits(bitstring)
, transformBitArray
to integers.
julia> barr = bitarray(28, 5)
5-element BitVector: 0 0 1 1 1
julia> packbits(barr)
28
julia> barr_mult = bitarray([4, 5, 6], 5)
5×3 BitMatrix: 0 1 0 0 0 1 1 1 1 0 0 0 0 0 0
julia> packbits(barr_mult)
3-element view(::Matrix{Int64}, 1, :) with eltype Int64: 4 5 6