Array Registers
We provide ArrayReg
as built in register type for simulations. It is a simple wrapper of a Julia array, e.g on CPU, we use Array
by default and on CUDA devices we could use CuArray
. You don't have to define your custom array type if the storage is array based.
Constructors
YaoArrayRegister.ArrayReg
— Type.ArrayReg{B, T, MT <: AbstractMatrix{T}} <: AbstractRegister{B, T}
Simulated full amplitude register type, it uses an array to represent corresponding one or a batch of quantum states. B
is the batch size, T
is the numerical type for each amplitude, it is ComplexF64
by default.
We define some shortcuts to create simulated quantum states easier:
YaoArrayRegister.product_state
— Function.product_state([T=ComplexF64], bit_str; nbatch=1)
Create an ArrayReg
with bit string literal defined with @bit_str
. See also zero_state
, rand_state
, uniform_state
.
Examples
julia> product_state(bit"100"; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 3/3
julia> product_state(ComplexF32, bit"101"; nbatch=2)
ArrayReg{2,Complex{Float32},Array...}
active qubits: 3/3
product_state([T=ComplexF64], total::Int, bit_config::Integer; nbatch=1)
Create an ArrayReg
with bit configuration bit_config
, total number of bits total
. See also zero_state
, rand_state
, uniform_state
.
Examples
julia> product_state(4, 3; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
julia> product_state(4, 0b1001; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
julia> product_state(ComplexF32, 4, 0b101)
ArrayReg{1,Complex{Float32},Array...}
active qubits: 4/4
This interface will not check whether the number of required digits for the bit configuration matches the total number of bits.
YaoArrayRegister.zero_state
— Function.zero_state([T=ComplexF64], n::Int; nbatch::Int=1)
Create an ArrayReg
with total number of bits n
. See also product_state
, rand_state
, uniform_state
.
Examples
julia> zero_state(4)
ArrayReg{1,Complex{Float64},Array...}
active qubits: 4/4
julia> zero_state(ComplexF32, 4)
ArrayReg{1,Complex{Float32},Array...}
active qubits: 4/4
julia> zero_state(ComplexF32, 4; nbatch=3)
ArrayReg{3,Complex{Float32},Array...}
active qubits: 4/4
YaoArrayRegister.rand_state
— Function.rand_state([T=ComplexF64], n::Int; nbatch::Int=1)
Create a random ArrayReg
with total number of qubits n
.
Examples
julia> rand_state(4)
ArrayReg{1,Complex{Float64},Array...}
active qubits: 4/4
julia> rand_state(ComplexF64, 4)
ArrayReg{1,Complex{Float64},Array...}
active qubits: 4/4
julia> rand_state(ComplexF64, 4; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
YaoArrayRegister.uniform_state
— Function.uniform_state([T=ComplexF64], n; nbatch=1)
Create a uniform state: $\frac{1}{2^n} \sum_k |k⟩$. This state can also be created by applying H
(Hadmard gate) on $|00⋯00⟩$ state.
Example
julia> uniform_state(4; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
julia> uniform_state(ComplexF32, 4; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
YaoArrayRegister.oneto
— Function.oneto(r::ArrayReg, n::Int=nqubits(r))
Returns an ArrayReg
with 1:n
qubits activated.
oneto(n::Int) -> f(register)
Like oneto(register, n)
, but the input register
is delayed.
Base.repeat
— Function.repeat(r::AbstractRegister, n::Int) -> register
Repeat register r
for n
times on batch dimension.
repeat(register, n)
Create an ArrayReg
by copying the original register
for n
times on batch dimension.
Example
julia> repeat(ArrayReg{3}(bit"101"), 4)
ArrayReg{12,Complex{Float64},Array...}
active qubits: 3/3
repeat(n, x::AbstractBlock[, locs]) -> RepeatedBlock{n}
Create a RepeatedBlock
with total number of qubits n
and the block to repeat on given location or on all the locations.
Example
This will create a repeat block which puts 4 X gates on each location.
julia> repeat(4, X)
nqubits: 4, datatype: Complex{Float64}
repeat on (1, 2, 3, 4)
└─ X gate
You can also specify the location
julia> repeat(4, X, (1, 2))
nqubits: 4, datatype: Complex{Float64}
repeat on (1, 2)
└─ X gate
But repeat won't copy the gate, thus, if it is a gate with parameter, e.g a phase(0.1)
, the parameter will change simultaneously.
julia> g = repeat(4, phase(0.1))
nqubits: 4, datatype: Complex{Float64}
repeat on (1, 2, 3, 4)
└─ phase(0.1)
julia> g.content
phase(0.1)
julia> g.content.theta = 0.2
0.2
julia> g
nqubits: 4, datatype: Complex{Float64}
repeat on (1, 2, 3, 4)
└─ phase(0.2)
repeat(x::AbstractBlock, locs)
Lazy curried version of repeat
.
Properties
You can access the storage of an ArrayReg
with:
YaoArrayRegister.state
— Function.state(register::ArrayReg) -> raw array
Returns the raw array storage of register
. See also statevec
.
state(ρ::DensityMatrix)
Return the raw state of density matrix ρ
.
YaoArrayRegister.statevec
— Function.statevec(r::ArrayReg) -> array
Return a state matrix/vector by droping the last dimension of size 1. See also state
.
statevec
is not type stable. It may cause performance slow down.
YaoArrayRegister.relaxedvec
— Function.BitBasis.hypercubic
— Function.hypercubic(r::ArrayReg) -> AbstractArray
Return the hypercubic form (high dimensional tensor) of this register, only active qubits are considered. See also rank3
.
hypercubic(A::Array) -> Array
get the hypercubic representation for an array.
YaoArrayRegister.rank3
— Function.rank3(r::ArrayReg)
Return the rank 3 tensor representation of state, the 3 dimensions are (activated space, remaining space, batch dimension). See also rank3
.
Operations
We defined basic arithmatics for ArrayReg
, besides since we do not garantee normalization for some operations on ArrayReg
for simulation, normalize!
and isnormalized
is provided to check and normalize the simulated register.
LinearAlgebra.normalize!
— Function.normalize!(r::ArrayReg)
Normalize the register r
in-place by its 2
-norm.
YaoArrayRegister.isnormalized
— Function.isnormalized(r::ArrayReg) -> Bool
Check if the register is normalized.
Specialized Instructions
We define some specialized instruction by specializing instruct!
to improve the performance for simulation and dispatch them with multiple dispatch.
Implemented instruct!
is listed below:
registers | operators | nqubits | controls |
---|---|---|---|
AbstractVecOrMat | LuxurySparse.IMatrix | multiple, single | none |
AbstractVecOrMat | LuxurySparse.PermMatrix{T,Ti,#s18,#s17} where #s17<:Union{Array{Ti,1}, SArray{Tuple{N},Ti,1,N} where N} where #s18<:Union{Array{T,1}, SArray{Tuple{N},T,1,N} where N} where Ti | single | none |
AbstractVecOrMat | Union{Diagonal{T,V} where V<:AbstractArray{T,1}, Diagonal{T,SArray{Tuple{N},T,1,N}} where N} | single | none |
AbstractVecOrMat | AbstractArray{T,2} | single, multiple | none, multiple |
AbstractVecOrMat | AbstractArray{T2,2} | multiple, single | none, multiple |
AbstractVecOrMat | LuxurySparse.PermMatrix{T2,Ti,#s18,#s17} where #s17<:Union{Array{Ti,1}, SArray{Tuple{N},Ti,1,N} where N} where #s18<:Union{Array{T2,1}, SArray{Tuple{N},T2,1,N} where N} where Ti | single | none |
AbstractVecOrMat | Union{Diagonal{T2,V} where V<:AbstractArray{T2,1}, Diagonal{T2,SArray{Tuple{N},T2,1,N}} where N} | single | none |
AbstractVecOrMat | X | single, multiple | single, none, multiple |
AbstractVecOrMat | Y | single, multiple | single, none, multiple |
AbstractVecOrMat | Z | single, multiple | none, single, multiple |
AbstractVecOrMat | S | single, multiple | none, single, multiple |
AbstractVecOrMat | T | single, multiple | none, single, multiple |
AbstractVecOrMat | Sdag | single, multiple | none, single, multiple |
AbstractVecOrMat | Tdag | single, multiple | none, single, multiple |
AbstractVecOrMat | SWAP | multiple | none |
Measurement
Simulation of measurement is mainly achieved by sampling and projection.
Sample
Suppose we want to measure operational subspace, we can first get
Then we sample an $a\sim p(x)$. If we just sample and don't really measure (change wave function), its over.
Projection
Good! then we can just remove the operational qubit space since x
and y
spaces are totally decoupled and x
is known as in state a
, then we get
where l = L(a:a, :, :)/sqrt(p(a))
.
Others
BitBasis.hypercubic
— Method.hypercubic(r::ArrayReg) -> AbstractArray
Return the hypercubic form (high dimensional tensor) of this register, only active qubits are considered. See also rank3
.
LinearAlgebra.normalize!
— Method.normalize!(r::ArrayReg)
Normalize the register r
in-place by its 2
-norm.
YaoArrayRegister.isnormalized
— Method.isnormalized(r::ArrayReg) -> Bool
Check if the register is normalized.
YaoArrayRegister.oneto
— Method.oneto(n::Int) -> f(register)
Like oneto(register, n)
, but the input register
is delayed.
YaoArrayRegister.oneto
— Method.oneto(r::ArrayReg, n::Int=nqubits(r))
Returns an ArrayReg
with 1:n
qubits activated.
YaoArrayRegister.product_state
— Method.product_state([T=ComplexF64], bit_str; nbatch=1)
Create an ArrayReg
with bit string literal defined with @bit_str
. See also zero_state
, rand_state
, uniform_state
.
Examples
julia> product_state(bit"100"; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 3/3
julia> product_state(ComplexF32, bit"101"; nbatch=2)
ArrayReg{2,Complex{Float32},Array...}
active qubits: 3/3
YaoArrayRegister.product_state
— Method.product_state([T=ComplexF64], total::Int, bit_config::Integer; nbatch=1)
Create an ArrayReg
with bit configuration bit_config
, total number of bits total
. See also zero_state
, rand_state
, uniform_state
.
Examples
julia> product_state(4, 3; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
julia> product_state(4, 0b1001; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
julia> product_state(ComplexF32, 4, 0b101)
ArrayReg{1,Complex{Float32},Array...}
active qubits: 4/4
This interface will not check whether the number of required digits for the bit configuration matches the total number of bits.
YaoArrayRegister.rand_state
— Method.rand_state([T=ComplexF64], n::Int; nbatch::Int=1)
Create a random ArrayReg
with total number of qubits n
.
Examples
julia> rand_state(4)
ArrayReg{1,Complex{Float64},Array...}
active qubits: 4/4
julia> rand_state(ComplexF64, 4)
ArrayReg{1,Complex{Float64},Array...}
active qubits: 4/4
julia> rand_state(ComplexF64, 4; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
YaoArrayRegister.rank3
— Method.rank3(r::ArrayReg)
Return the rank 3 tensor representation of state, the 3 dimensions are (activated space, remaining space, batch dimension). See also rank3
.
YaoArrayRegister.relaxedvec
— Method.YaoArrayRegister.state
— Method.state(register::ArrayReg) -> raw array
Returns the raw array storage of register
. See also statevec
.
YaoArrayRegister.state
— Method.state(ρ::DensityMatrix)
Return the raw state of density matrix ρ
.
YaoArrayRegister.statevec
— Method.statevec(r::ArrayReg) -> array
Return a state matrix/vector by droping the last dimension of size 1. See also state
.
statevec
is not type stable. It may cause performance slow down.
YaoArrayRegister.uniform_state
— Method.uniform_state([T=ComplexF64], n; nbatch=1)
Create a uniform state: $\frac{1}{2^n} \sum_k |k⟩$. This state can also be created by applying H
(Hadmard gate) on $|00⋯00⟩$ state.
Example
julia> uniform_state(4; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
julia> uniform_state(ComplexF32, 4; nbatch=2)
ArrayReg{2,Complex{Float64},Array...}
active qubits: 4/4
YaoArrayRegister.zero_state
— Method.zero_state([T=ComplexF64], n::Int; nbatch::Int=1)
Create an ArrayReg
with total number of bits n
. See also product_state
, rand_state
, uniform_state
.
Examples
julia> zero_state(4)
ArrayReg{1,Complex{Float64},Array...}
active qubits: 4/4
julia> zero_state(ComplexF32, 4)
ArrayReg{1,Complex{Float32},Array...}
active qubits: 4/4
julia> zero_state(ComplexF32, 4; nbatch=3)
ArrayReg{3,Complex{Float32},Array...}
active qubits: 4/4
YaoBase.fidelity
— Method.fidelity(r1::ArrayReg, r2::ArrayReg)
Calcuate the fidelity between r1
and r2
, if r1
or r2
is not pure state (nactive(r) != nqubits(r)
), the fidelity is calcuated by purification. See also pure_state_fidelity
, purification_fidelity
.
YaoBase.probs
— Method.probs(ρ)
Returns the probability distribution from a density matrix ρ
.
Base.join
— Method.join(regs...)
concat a list of registers regs
to a larger register, each register should have the same batch size. See also repeat
.
Base.repeat
— Method.repeat(register, n)
Create an ArrayReg
by copying the original register
for n
times on batch dimension.
Example
julia> repeat(ArrayReg{3}(bit"101"), 4)
ArrayReg{12,Complex{Float64},Array...}
active qubits: 3/3
YaoArrayRegister.contiguous_shape_orders
— Method.contiguous_shape_orders(shape, orders)
Merge the shape and orders if the orders are contiguous. Returns the new merged shape and order.
Example
julia> YaoArrayRegister.contiguous_shape_order((2, 3, 4), (1, 2, 3))
([24], [1])
YaoArrayRegister.is_order_same
— Method.is_order_same(locs) -> Bool
Check if the order specified by locs
is the same as current order.
YaoArrayRegister.matvec
— Function.matvec(x::VecOrMat) -> MatOrVec
Return vector if a matrix is a column vector, else untouched.
YaoArrayRegister.move_ahead
— Method.move_ahead(collection, orders)
Move orders
to the beginning of collection
.
YaoArrayRegister.mulcol!
— Function.mulcol!(v::AbstractVector, i::Int, f) -> VecOrMat
multiply col i of v by f inplace.
YaoArrayRegister.mulrow!
— Function.mulrow!(v::AbstractVector, i::Int, f) -> VecOrMat
multiply row i of v by f inplace.
YaoArrayRegister.sort_unitary
— Method.sort_unitary(U, locations::NTuple{N, Int}) -> U
Return an sorted unitary operator according to the locations.
YaoArrayRegister.swapcols!
— Function.swapcols!(v::VecOrMat, i::Int, j::Int[, f1, f2]) -> VecOrMat
swap col i and col j of v inplace, with f1, f2 factors applied on i and j (before swap).
YaoArrayRegister.swaprows!
— Function.swaprows!(v::VecOrMat, i::Int, j::Int[, f1, f2]) -> VecOrMat
swap row i and row j of v inplace, with f1, f2 factors applied on i and j (before swap).
YaoArrayRegister.u1rows!
— Function.u1rows!(state::VecOrMat, i::Int, j::Int, a, b, c, d) -> VecOrMat
apply u1 on row i and row j of state inplace.