Array Registers

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

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:

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
Warning

This interface will not check whether the number of required digits for the bit configuration matches the total number of bits.

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
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
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
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.repeatFunction.
repeat(r::AbstractRegister, n::Int) -> register

Repeat register r for n times on batch dimension.

source
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:

state(register::ArrayReg) -> raw array

Returns the raw array storage of register. See also statevec.

state(ρ::DensityMatrix)

Return the raw state of density matrix ρ.

statevec(r::ArrayReg) -> array

Return a state matrix/vector by droping the last dimension of size 1. See also state.

Warning

statevec is not type stable. It may cause performance slow down.

relaxedvec(r::ArrayReg) -> AbstractArray

Return a matrix (vector) for B>1 (B=1) as a vector representation of state, with all qubits activated. See also state, statevec.

BitBasis.hypercubicFunction.
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.

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.

normalize!(r::ArrayReg)

Normalize the register r in-place by its 2-norm.

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:

registersoperatorsnqubitscontrols
AbstractVecOrMatLuxurySparse.IMatrixmultiple, singlenone
AbstractVecOrMatLuxurySparse.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 Tisinglenone
AbstractVecOrMatUnion{Diagonal{T,V} where V<:AbstractArray{T,1}, Diagonal{T,SArray{Tuple{N},T,1,N}} where N}singlenone
AbstractVecOrMatAbstractArray{T,2}single, multiplenone, multiple
AbstractVecOrMatAbstractArray{T2,2}multiple, singlenone, multiple
AbstractVecOrMatLuxurySparse.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 Tisinglenone
AbstractVecOrMatUnion{Diagonal{T2,V} where V<:AbstractArray{T2,1}, Diagonal{T2,SArray{Tuple{N},T2,1,N}} where N}singlenone
AbstractVecOrMatXsingle, multiplesingle, none, multiple
AbstractVecOrMatYsingle, multiplesingle, none, multiple
AbstractVecOrMatZsingle, multiplenone, single, multiple
AbstractVecOrMatSsingle, multiplenone, single, multiple
AbstractVecOrMatTsingle, multiplenone, single, multiple
AbstractVecOrMatSdagsingle, multiplenone, single, multiple
AbstractVecOrMatTdagsingle, multiplenone, single, multiple
AbstractVecOrMatSWAPmultiplenone

Measurement

Simulation of measurement is mainly achieved by sampling and projection.

Sample

Suppose we want to measure operational subspace, we can first get

\[p(x) = \|\langle x|\psi\rangle\|^2 = \sum\limits_{y} \|L(x, y, .)\|^2.\]

Then we sample an $a\sim p(x)$. If we just sample and don't really measure (change wave function), its over.

Projection

\[|\psi\rangle' = \sum_y L(a, y, .)/\sqrt{p(a)} |a\rangle |y\rangle\]

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

\[|\psi\rangle'_r = \sum_y l(0, y, .) |y\rangle\]

where l = L(a:a, :, :)/sqrt(p(a)).

Others

hypercubic(r::ArrayReg) -> AbstractArray

Return the hypercubic form (high dimensional tensor) of this register, only active qubits are considered. See also rank3.

normalize!(r::ArrayReg)

Normalize the register r in-place by its 2-norm.

isnormalized(r::ArrayReg) -> Bool

Check if the register is normalized.

oneto(n::Int) -> f(register)

Like oneto(register, n), but the input register is delayed.

oneto(r::ArrayReg, n::Int=nqubits(r))

Returns an ArrayReg with 1:n qubits activated.

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
Warning

This interface will not check whether the number of required digits for the bit configuration matches the total number of bits.

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
rank3(r::ArrayReg)

Return the rank 3 tensor representation of state, the 3 dimensions are (activated space, remaining space, batch dimension). See also rank3.

relaxedvec(r::ArrayReg) -> AbstractArray

Return a matrix (vector) for B>1 (B=1) as a vector representation of state, with all qubits activated. See also state, statevec.

state(register::ArrayReg) -> raw array

Returns the raw array storage of register. See also statevec.

state(ρ::DensityMatrix)

Return the raw state of density matrix ρ.

statevec(r::ArrayReg) -> array

Return a state matrix/vector by droping the last dimension of size 1. See also state.

Warning

statevec is not type stable. It may cause performance slow down.

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
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.fidelityMethod.
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.probsMethod.
probs(ρ)

Returns the probability distribution from a density matrix ρ.

Base.joinMethod.
join(regs...)

concat a list of registers regs to a larger register, each register should have the same batch size. See also repeat.

Base.repeatMethod.
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
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])
is_order_same(locs) -> Bool

Check if the order specified by locs is the same as current order.

matvec(x::VecOrMat) -> MatOrVec

Return vector if a matrix is a column vector, else untouched.

move_ahead(collection, orders)

Move orders to the beginning of collection.

mulcol!(v::AbstractVector, i::Int, f) -> VecOrMat

multiply col i of v by f inplace.

mulrow!(v::AbstractVector, i::Int, f) -> VecOrMat

multiply row i of v by f inplace.

sort_unitary(U, locations::NTuple{N, Int}) -> U

Return an sorted unitary operator according to the locations.

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).

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).

u1rows!(state::VecOrMat, i::Int, j::Int, a, b, c, d) -> VecOrMat

apply u1 on row i and row j of state inplace.