Registers

# Registers

Quantum circuits process quantum states. A quantum state being processing by a quantum circuit will be stored on a quantum register. In Yao we provide several types for registers. The default type for registers is the Yao.Registers.DefaultRegister.

You can directly use factory method register

## Storage

#### LDT format

Concepturely, a wave function $|\psi\rangle$ can be represented in a low dimentional tensor (LDT) format of order-3, L(f, r, b).

• f: focused (i.e. operational) dimensions
• r: remaining dimensions
• b: batch dimension.

For simplicity, let's ignore batch dimension for the momentum, we have

$|\psi\rangle = \sum\limits_{x,y} L(x, y, .) |j\rangle|i\rangle$

Given a configuration x (in operational space), we want get the i-th bit using (x<<i) & 0x1, which means putting the small end the qubit with smaller index. In this representation L(x) will get return $\langle x|\psi\rangle$.

Note

Why not the other convension: Using the convention of putting 1st bit on the big end will need to know the total number of qubits n in order to know such positional information.

#### HDT format

Julia storage is column major, if we reshape the wave function to a shape of $2\times2\times ... \times2$ and get the HDT (high dimensional tensor) format representation H, we can use H($x_1, x_2, ..., x_3$) to get $\langle x|\psi\rangle$.

## Operations

#### Kronecker product of operators

In order to put small bits on little end, the Kronecker product is $O = o_n \otimes \ldots \otimes o_2 \otimes o_1$ where the subscripts are qubit indices.

#### Measurements

Measure means sample 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)).

## Registers

AbstractRegister{B, T}

abstract type that registers will subtype from. B is the batch size, T is the data type.

Required Properties

PropertyDescriptiondefault
viewbatch(reg,i)get the view of slice in batch dimension.
nqubits(reg)get the total number of qubits.
nactive(reg)get the number of active qubits.
state(reg)get the state of this register. It always return the matrix stored inside.
(optional)
nremain(reg)get the number of remained qubits.nqubits - nactive
datatype(reg)get the element type Julia should use to represent amplitude)T
nbatch(reg)get the number of batch.B
length(reg)alias of nbatch, for interfacing.B

Required Methods

Multiply

*(op, reg)

define how operator op act on this register. This is quite useful when there is a special approach to apply an operator on this register. (e.g a register with no batch, or a register with a MPS state, etc.)

Note

be careful, generally, operators can only be applied to a register, thus we should only overload this operation and do not overload *(reg, op).

Pack Address

pack addrs together to the first k-dimensions.

Example

Given a register with dimension [2, 3, 1, 5, 4], we pack [5, 4] to the first 2 dimensions. We will get [5, 4, 2, 3, 1].

Focus Address

focus!(reg, range)

merge address in range together as one dimension (the active space).

Example

Given a register with dimension (2^4)x3 and address [1, 2, 3, 4], we focus address [3, 4], will pack [3, 4] together and merge them as the active space. Then we will have a register with size 2^2x(2^2x3), and address [3, 4, 1, 2].

Initializers

Initializers are functions that provide specific quantum states, e.g zero states, random states, GHZ states and etc.

register(::Type{RT}, raw, nbatch)

an general initializer for input raw state array.

register(::Val{InitMethod}, ::Type{RT}, ::Type{T}, n, nbatch)

init register type RT with InitMethod type (e.g Val{:zero}) with element type T and total number qubits n with nbatch. This will be auto-binded to some shortcuts like zero_state, rand_state.

DefaultRegister{B, T} <: AbstractRegister{B, T}

Default type for a quantum register. It contains a dense array that represents a batched quantum state with batch size B of type T.

DensityMatrix{B, T, MT<:AbstractArray{T, 3}}
DensityMatrix(state) -> DensityMatrix

Density Matrix.

@bit_str -> BitStr

Construct a bit string. such as bit"0000". The bit strings also supports string concat. Just use it like normal strings.

normalize!(r::AbstractRegister) -> AbstractRegister

Return the register with normalized state.

hypercubic(r::DefaultRegister) -> AbstractArray

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

addbit!(r::AbstractRegister, n::Int) -> AbstractRegister
addbit!(n::Int) -> Function

addbit the register by n bits in state |0>. i.e. |psi> -> |000> ⊗ |psi>, addbit bits have higher indices. If only an integer is provided, then perform lazy evaluation.

density_matrix(register)

Returns the density matrix of this register.

fidelity(reg1::AbstractRegister, reg2::AbstractRegister) -> Vector

Return the fidelity between two states.

focus!(reg::DefaultRegister, bits::Ints) -> DefaultRegister
focus!(locs::Int...) -> Function

Focus register on specified active bits.

focus!(func, reg::DefaultRegister, locs) -> DefaultRegister
invorder!(reg::AbstractRegister) -> AbstractRegister

Inverse the order of lines inplace.

isnormalized(reg::AbstractRegister) -> Bool

Return true if a register is normalized else false.

measure!(reg::AbstractRegister; [locs]) -> Int

measure and collapse to result state.

measure(register, [locs]; [nshot=1]) -> Vector

measure active qubits for nshot times.

measure_remove!(register; [locs]) -> Int

measure the active qubits of this register and remove them.

measure_and_reset!(reg::AbstractRegister; [locs], [val=0]) -> Int

measure and set the register to specific value.

oneto({reg::DefaultRegister}, n::Int=nqubits(reg)) -> DefaultRegister

Return a register with first 1:n bits activated, reg here can be lazy.

probs(r::AbstractRegister)

Returns the probability distribution in computation basis $|<x|ψ>|^2$.

probs(dm::DensityMatrix{B, T}) where {B,T}

Return probability from density matrix.

product_state([::Type{T}], n::Int, config::Int, nbatch::Int=1) -> DefaultRegister

a product state on given configuration config, e.g. product_state(ComplexF64, 5, 0) will give a zero state on a 5 qubit register.

rand_state([::Type{T}], n::Int, nbatch::Int=1) -> DefaultRegister

here, random complex numbers are generated using randn(ComplexF64).

rank3(reg::DefaultRegister) -> Array{T, 3}

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

register([type], bit_str, [nbatch=1]) -> DefaultRegister

Returns a DefaultRegister by inputing a bit string, e.g

using Yao
register(bit"0000")
register(raw) -> DefaultRegister

Returns a DefaultRegister from a raw dense array (Vector or Matrix).

relax!(reg::DefaultRegister; nbit::Int=nqubits(reg)) -> DefaultRegister
relax!(reg::DefaultRegister, bits::Ints; nbit::Int=nqubits(reg)) -> DefaultRegister
relax!(bits::Ints...; nbit::Int=-1) -> Function

Inverse transformation of focus, with nbit is the number of active bits of target register.

relaxedvec(r::DefaultRegister) -> AbstractArray

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

reorder!(reg::AbstractRegister, order) -> AbstractRegister
reorder!(orders::Int...) -> Function    # currified

Reorder the lines of qubits, it also works for array.

reset!(reg::AbstractRegister, val::Integer=0) -> AbstractRegister

reset! reg to default value.

select!(reg::AbstractRegister, b::Integer) -> AbstractRegister
select!(b::Integer) -> Function

select specific component of qubit, the inplace version, the currified version will return a Function.

e.g. select!(reg, 0b110) will select the subspace with (focused) configuration 110. After selection, the focused qubit space is 0, so you may want call relax! manually.

select(reg::AbstractRegister, b::Integer) -> AbstractRegister

the non-inplace version of select! function.

state(reg) -> AbstractMatrix

get the state of this register. It always return the matrix stored inside.

statevec(r::DefaultRegister) -> AbstractArray

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

tracedist(reg1::AbstractRegister, reg2::AbstractRegister) -> Vector
tracedist(reg1::DensityMatrix, reg2::DensityMatrix) -> Vector

trace distance.

tracedist(dm1::DensityMatrix{B}, dm2::DensityMatrix{B}) -> Vector

Return trace distance between two density matrices.

uniform_state([::Type{T}], n::Int, nbatch::Int=1) -> DefaultRegister

uniform state, the state after applying H gates on |0> state.

viewbatch(r::AbstractRegister, i::Int) -> AbstractRegister{1}

Return a view of a slice from batch dimension.

zero_state([::Type{T}], n::Int, nbatch::Int=1) -> DefaultRegister
ρ(register)

Returns the density matrix of this register.

nactive(x::AbstractRegister) -> Int

Return the number of active qubits.

note!!!

Operatiors always apply on active qubits.
BitStr

String literal for qubits.

join(reg1::AbstractRegister, reg2::AbstractRegister) -> Register

Merge two registers together with kronecker tensor product.

repeat(reg::AbstractRegister{B}, n::Int) -> AbstractRegister

Repeat register in batch dimension for n times.

Get the compact shape and order for permutedims.