# Register Basics

## Table of Contents

- Construction and Storage
- Basics Arithmatics
- Fidelity and DensityMatrix
- Batched Registers

```
using Yao
using LinearAlgebra
```

## Construction and Storage

`AbstractRegister{B, T}`

is abstract type that registers will subtype from. B is the batch size, T is the data type. Normally, we use a matrix as the `state`

(with columns the batch and environment dimension) of a register, which is called `DefaultRegister{B, T}`

.

To initialize a quantum register, all you need is

`register(vec)`

,`zero_state(nbit)`

,`rand_state(nbit)`

, both real and imaginary parts are random normal distributions,`product_state(nbit, val=0)`

, where val is an`Integer`

as bitstring, e.g.`0b10011`

or`19`

,`uniform_state(nbit)`

, evenly distributed state, i.e. H|0>.

e.g.

```
ψ1 = zero_state(5)
@show ψ1
@show nqubits(ψ1)
@show nactive(ψ1) # number of activated qubits
@show nremain(ψ1) # number of remaining qubits
ψ2 = ψ1 |> focus!(3,2,4) # set activated qubits
@show ψ2
@show nqubits(ψ2)
@show nactive(ψ2)
@show nremain(ψ2)
@assert relax!(ψ2, (3,2,4)) == ψ1
```

```
ψ1 = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 5/5
nqubits(ψ1) = 5
nactive(ψ1) = 5
nremain(ψ1) = 0
ψ2 = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 3/5
nqubits(ψ2) = 5
nactive(ψ2) = 3
nremain(ψ2) = 2
```

The total number of qubits here is 5, they are all acitve by default. `active`

qubits are also called `system`

qubits that are visible to operations, `remaining`

qubits are the `environment`

. `nremain == nqubits-nactive`

always holds.

**focus! & relax!** `focus!(reg, (3,2,4))`

is equivalent to `reg |> focus!(3,2,4)`

, which changes focused bits to `(3,2,4)`

. Here from ψ1 -> ψ2, qubit line numbers change as `(active)(remaining): (1,2,3,4,5)() -> (3,2,4)(1,5)`

`focus!`

uses *relative positions*, which means it sees only active qubits and does not memorize original qubits positions. We take this convension to support **modulized design**. For example, if we want to insert a `QFT`

blocks into some parent module, both the `QFT`

and its parent do not need to know `original position`

, which provides flexibility.

`relax!`

is the inverse process of `focus!`

, `relax!(reg, (3,2,4))`

will cancel the above operation. Here we have a second parameter since a register does not memorize original positions. This annoying feature can be circumvented using `focus!(reg, (3,2,4)) do ... end`

, which will automatically restore your focus operation, see an example here.

Please also notice **APIs for changing lines order**

- reorder!(reg, order), change lines order
- reg |> invorder!, inverse lines order

and

- reg |> oneto(n), return a register
`view`

, with first`n`

bits focused.

**Extending Registers** We can extend registers by either joining two registers or adding bits.

`@assert product_state(3, 0b110) ⊗ product_state(3, 0b001) == product_state(6, 0b110001)`

```
reg = product_state(5, 0b11100)
@assert addbit!(copy(reg), 2) == product_state(7, 0b0011100) == zero_state(2) ⊗ reg
```

**Storage** Let's dive into the storage of a register, there are three types `representation`

s

`reg |> state`

, matrix format, size =`(2^nactive, 2^nremain * nbatch)`

`reg |> rank3`

, rank 3 tensor format, size =`(2^nactive, 2^nremain, nbatch)`

`reg |> hypercubic`

, hypercubic format, size =`(2, 2, 2, ..., nbatch)`

Here, we add a dimension `nbatch`

to support parallism among registers. They are all different views of same memory. Please also check `statevec`

and `relaxedvec`

format, which prefer vectors whenever possible.

```
@show ψ1 |> state |> size
@show ψ1 |> rank3 |> size
@show ψ1 |> hypercubic |> size
@show ψ1 |> statevec |> size
@show ψ1 |> relaxedvec |> size;
```

```
(ψ1 |> state) |> size = (32, 1)
(ψ1 |> rank3) |> size = (32, 1, 1)
(ψ1 |> hypercubic) |> size = (2, 2, 2, 2, 2, 1)
(ψ1 |> statevec) |> size = (32,)
(ψ1 |> relaxedvec) |> size = (32,)
```

### Example

multiply `|0>`

by a random unitary operator on qubits `(3, 1, 5)`

(relax the register afterwards).

```
using Yao.Intrinsics: rand_unitary
reg = zero_state(5)
focus!(reg, [3,1,5]) do r
r.state = rand_unitary(8) * r.state
r
end
@show reg.state;
```

`reg.state = Complex{Float64}[-0.302894-0.1309im; -0.230708-0.317541im; 0.0+0.0im; 0.0+0.0im; 0.340651-0.147317im; 0.157962+0.385621im; 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.125243+0.0986739im; -0.0595983-0.145877im; 0.0+0.0im; 0.0+0.0im; 0.507097-0.268703im; 0.160987-0.141923im; 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]`

## Basic Arithmatics

`+, -, *, /, ⊗, '`

are implemented.

The adjoint of a register is also called `bra`

, it can be used in calculating state overlap

```
ψ1 = rand_state(5)
ψ2 = rand_state(5)
```

```
DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 5/5
```

arithmatics

```
@show ψ1
@show ψ2
@show ψ3 = (0.3ψ1 + 2ψ2)/2 ⊗ ψ1
@assert ψ3 ≈ 0.15ψ1 ⊗ ψ1 + ψ2 ⊗ ψ1
```

```
ψ1 = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 5/5
ψ2 = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 5/5
ψ3 = ((0.3ψ1 + 2ψ2) / 2) ⊗ ψ1 = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 10/10
```

normalize ψ3

```
@assert ψ1 |> isnormalized && ψ2 |> isnormalized
@assert ψ3 |> isnormalized == false
@show ψ3 |> normalize! |> isnormalized
@show ψ3' * ψ3;
```

```
(ψ3 |> normalize!) |> isnormalized = true
ψ3' * ψ3 = 1.0 + 0.0im
```

## Measure

`measure(reg; nshot=1)`

, measure without collapsing state,`measure!(reg)`

, measure and collapse,`measure_remove!(reg)`

, measure focused bits and remove them,`measure_reset!(reg, val=0)`

, measure focused bits and reset them to some value,`reset!(reg)`

, collapse to specific value directly.`select(reg, x)`

, select subspace projected on specific basis, i.e. $|\phi\rangle = |x\rangle\langle x|\psi\rangle$.

**measure**

```
@show product_state(5, 0b11001) |> measure # please notice binary number `0b11001` is equivalent to `25`!
reg = rand_state(7)
@show measure(reg; nshot=5); # measure multiple times
```

```
product_state(5, 0x19) |> measure = [25]
measure(reg; nshot=5) = [5, 41, 60, 92, 7]
```

**measure!**

```
reg = rand_state(7)
@show [measure!(reg) for i=1:5]; # measure! will collapse state
```

`[measure!(reg) for i = 1:5] = Array{Int64,1}[[64], [64], [64], [64], [64]]`

**measure_reset!**

```
reg = rand_state(7)
@show [measure_reset!(reg, val=i*10) for i=1:5]; # measure_reset! will reset the measured bit to target state (default is `0`)
```

`[measure_reset!(reg, val=i * 10) for i = 1:5] = Array{Int64,1}[[120], [10], [20], [30], [40]]`

**measure_remove!**

```
reg = rand_state(7)
@show measure_remove!(reg)
@show reg;
reg = rand_state(7)
@show measure_remove!(reg |> focus!(2,3))
@show reg;
```

```
measure_remove!(reg) = [22]
reg = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 0/0
measure_remove!(reg |> focus!(2, 3)) = [1]
reg = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 0/5
```

**select**

select will allow you to get the disired measurement result, and collapse to that state. It is equivalent to calculating $|\phi\rangle = |x\rangle\langle x|\psi\rangle$.

```
reg = rand_state(9) |> focus!(1, 2, 3, 4)
@show ψ = select(reg, 0b1110)
@show ψ |> relax!;
# Fidelity and Density Matrix
ψ1 = rand_state(6)
ψ2 = rand_state(6)
@show fidelity(ψ1, ψ2)
@show tracedist(ψ1, ψ2)
@show ψ1 |> ρ
@show tracedist(ψ1 |> ρ, ψ2|> ρ); # calculate trace distance using density matrix
@assert ψ1 |> probs ≈ dropdims(ψ1 |> ρ |> probs, dims=2)
```

```
ψ = select(reg, 0x0e) = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 0/5
ψ |> relax! = DefaultRegister{1, Array{Complex{Float64},2}}
active qubits: 5/5
fidelity(ψ1, ψ2) = [0.0542046]
tracedist(ψ1, ψ2) = [0.99853]
ψ1 |> ρ = DensityMatrix{1, Complex{Float64}}
nqubits: 6
tracedist(ψ1 |> ρ, ψ2 |> ρ) = [0.99853]
```

## Batched Registers

Most operations support batched register, which means running multiple registers in parallel.

```
ψ = rand_state(6, 3)
@show ψ
@show nbatch(ψ)
@show viewbatch(ψ, 2) # this is a view of register at 2nd column of the batch dimension
@show repeat(ψ, 3); # repeat registers in batch dimension
```

```
ψ = DefaultRegister{3, Array{Complex{Float64},2}}
active qubits: 6/6
nbatch(ψ) = 3
viewbatch(ψ, 2) = DefaultRegister{1, SubArray{Complex{Float64},2,Array{Complex{Float64},3},Tuple{Base.Slice{Base.OneTo{Int64}},Base.Slice{Base.OneTo{Int64}},Int64},true}}
active qubits: 6/6
repeat(ψ, 3) = DefaultRegister{9, Array{Complex{Float64},2}}
active qubits: 6/6
```

**broadcasting along batch dimension**

`@. ψ * 5 - 4 * ψ ≈ ψ`

```
3-element BitArray{1}:
true
true
true
```

```
X2 = put(5, 2=>X) # X operator on 2nd bit, with total number of bit 5.
direct = copy(ψ) |> X2 # applying X2 directly
map(reg->reg |> X2, ψ) # applying X2 using broadcasting, here X2 operator is applied inplace!
ψ .≈ direct
```

```
3-element BitArray{1}:
true
true
true
```

*This page was generated using Literate.jl.*