Block Basics
Table of Contents
- Construction and Matrix Representation
- Block Tree Architecture
- Tagging System
- Parameter System
- Differentiable Blocks
- Time Evolution and Hamiltonian
using Yao, Yao.Blocks
using LinearAlgebra
Construction and Matrix Representation
Blocks are operations on registers, we call those with matrix representation (linear) MatrixBlock
.
A MatrixBlock
can be
- isunitary, $O^\dagger O=I$
- ishermitian, $O^\dagger = O$
- isreflexive, $O^2 = 1$
@show X
@show X |> typeof
@show isunitary(X)
@show ishermitian(X)
@show isreflexive(X);
X = X gate
X |> typeof = XGate{Complex{Float64}}
isunitary(X) = true
ishermitian(X) = true
isreflexive(X) = true
matrix representation
mat(X)
2×2 LuxurySparse.PermMatrix{Complex{Float64},Int64,Array{Complex{Float64},1},Array{Int64,1}}:
0 1.0+0.0im
1.0+0.0im 0
composite gates Embeding an X gate into larger Hilbert space, the first parameter of most non-primitive constructors are always qubit numbers
@show X2 = put(3, 2=>X)
@show isunitary(X2)
@show ishermitian(X2)
@show isreflexive(X2);
X2 = put(3, 2 => X) = Total: 3, DataType: Complex{Float64}
put on (2)
└─ X gate
isunitary(X2) = true
ishermitian(X2) = true
isreflexive(X2) = true
mat(X2)
8×8 LuxurySparse.PermMatrix{Complex{Float64},Int64,Array{Complex{Float64},1},Array{Int64,1}}:
0 0 1.0+0.0im … 0 0 0
0 0 0 0 0 0
1.0+0.0im 0 0 0 0 0
0 1.0+0.0im 0 0 0 0
0 0 0 0 1.0+0.0im 0
0 0 0 … 0 0 1.0+0.0im
0 0 0 0 0 0
0 0 0 1.0+0.0im 0 0
@show cx = control(3, 3, 1=>X)
@show isunitary(cx)
@show ishermitian(cx)
@show isreflexive(cx);
cx = control(3, 3, 1 => X) = Total: 3, DataType: Complex{Float64}
control(3)
└─ (1,)=>X gate
isunitary(cx) = true
ishermitian(cx) = true
isreflexive(cx) = true
mat(cx)
8×8 LuxurySparse.PermMatrix{Complex{Float64},Int64,Array{Complex{Float64},1},Array{Int64,1}}:
1.0+0.0im 0 0 … 0 0 0
0 1.0+0.0im 0 0 0 0
0 0 1.0+0.0im 0 0 0
0 0 0 0 0 0
0 0 0 1.0+0.0im 0 0
0 0 0 … 0 0 0
0 0 0 0 0 1.0+0.0im
0 0 0 0 1.0+0.0im 0
hermitian and reflexive blocks can be used to construct rotation gates
@show rx = rot(X, π/4)
@show isunitary(rx)
@show ishermitian(rx)
@show isreflexive(rx);
rx = rot(X, π / 4) = Rot X gate: 0.7853981633974483
isunitary(rx) = true
ishermitian(rx) = false
isreflexive(rx) = false
mat(rx)
2×2 Array{Complex{Float64},2}:
0.92388+0.0im 0.0-0.382683im
0.0-0.382683im 0.92388+0.0im
now let's build a random circuit for following demos
using Yao.Intrinsics: rand_unitary
circuit = chain(5, control(5, 3=>Rx(0.25π)), put(5, (2,3)=>matrixgate(rand_unitary(4))), swap(5, 3, 4), repeat(5, H, 2:5), put(5, 2=>Ry(0.6)))
Total: 5, DataType: Complex{Float64}
chain
├─ control(5)
│ └─ (3,)=>Rot X gate: 0.7853981633974483
├─ put on (2, 3)
│ └─ GeneralMatrixGate(2^2 × 2^2; Array{Complex{Float64},2})
├─ swap(3, 4)
├─ repeat on (2, 3, 4, 5)
│ └─ H gate
└─ put on (2)
└─ Rot Y gate: 0.6
to apply it on some register, we can use
reg = zero_state(10)
focus!(reg, 1:5) do reg_focused
apply!(reg_focused, circuit)
end
@show reg ≈ zero_state(10); # reg is changed!
reg ≈ zero_state(10) = false
then we reverse the process and check the correctness
focus!(reg, 1:5) do reg_focused
reg_focused |> circuit'
end
@show reg ≈ zero_state(10); # reg is restored!
reg ≈ zero_state(10) = true
Here, we have used the pip "eye candy" reg |> block
to represent applying a block on register, which is equivalent to apply!(reg, block)
Type Tree To see a full list of block types
using InteractiveUtils: subtypes
function subtypetree(t, level=1, indent=4)
level == 1 && println(t)
for s in subtypes(t)
println(join(fill(" ", level * indent)) * string(s))
subtypetree(s, level+1, indent)
end
end
subtypetree(Yao.Blocks.AbstractBlock);
AbstractBlock
AbstractMeasure
Measure
MeasureAndRemove
MeasureAndReset
FunctionBlock
MatrixBlock
AbstractContainer
Concentrator
ControlBlock
PutBlock
RepeatedBlock
TagBlock
AbstractDiff
BPDiff
QDiff
AbstractScale
Scale
StaticScale
CachedBlock
Daggered
CompositeBlock
AddBlock
ChainBlock
KronBlock
PauliString
Roller
PrimitiveBlock
ConstantGate
CNOTGate
HGate
I2Gate
P0Gate
P1Gate
PdGate
PuGate
SGate
SWAPGate
SdagGate
TGate
TdagGate
ToffoliGate
XGate
YGate
ZGate
GeneralMatrixGate
MathBlock
PhaseGate
ReflectBlock
RotationGate
ShiftGate
Swap
TimeEvolution
Sequential
In the top level, we have
MatrixBlock
, linear operatorsAbstractMeasure
, measurement operationsFunctionBlock
, a wrapper for register function that take register as input, change the register inplace and return the register.Sequential
, a container for block tree, which is similar toChainBlock
, but has less constraints.
Block Tree Architecture
A block tree is specified the following two APIs
- subblocks(block), siblings of a block.
- chsubblocks, change siblings of a node.
crx = circuit[1]
@show crx
@show subblocks(crx)
@show chsubblocks(crx, (Y,));
crx = Total: 5, DataType: Complex{Float64}
control(5)
└─ (3,)=>Rot X gate: 0.7853981633974483
subblocks(crx) = (Rot X gate: 0.7853981633974483,)
chsubblocks(crx, (Y,)) = Total: 5, DataType: Complex{Float64}
control(5)
└─ (3,)=>Y gate
if we want to define a function that travals over the tree in depth first order, we can write something like
function print_block_tree(root, depth=0)
println(" "^depth * "- $(typeof(root).name)")
print_block_tree.(root |> subblocks, depth+1)
end
print_block_tree(circuit);
- ChainBlock
- ControlBlock
- RotationGate
- PutBlock
- GeneralMatrixGate
- Swap
- RepeatedBlock
- HGate
- PutBlock
- RotationGate
there are some functions defined using this strategy, like collect(circuit, block_type)
, it can filter out any type of blocks
rg = collect(circuit, RotationGate)
Sequence
├─ Rot X gate: 0.7853981633974483
└─ Rot Y gate: 0.6
Tagging System
We proudly introduced our tag system here. In previous sections, we have introduced the magic operation circuit'
to get the dagger a circuit, its realization is closely related to the tagging mechanism of Yao
.
@show X' # hermitian gate
@show Pu' # special gate
@show Rx(0.5)'; # rotation gate
X' = X gate
Pu' = Pd gate
(Rx(0.5))' = Rot X gate: -0.5
The dagger of above gates can be translated to other gates easily. but some blocks has no predefined dagger operations, then we put a tag for it as a default behavior, e.g.
daggered_gate = matrixgate(randn(4, 4))'
@show daggered_gate |> typeof
daggered_gate
daggered_gate |> typeof = Daggered{GeneralMatrixGate{2,2,Float64,Array{Float64,2}},2,Float64}
Total: 2, DataType: Float64
GeneralMatrixGate(2^2 × 2^2; Array{Float64,2}) [†]
Here, Daggered
is a subtype of TagBlock
.
Other tag blocks include
Scale, static scaling
2X
Total: 1, DataType: Complex{Float64}
[2] X gate
CachedBlock, get the matrix representation of a block when applying it on registers, and cache it in memory (or CacheServer
more precisely). This matrix can be useful in future calculation, like boosting time evolution.
put(5, 2=>X) |> cache
Total: 5, DataType: Complex{Float64}
[↺] put on (2)
└─ X gate
AbstactDiff, marks a block as differentiable, either in classical back propagation
mode (with extra memory cost to store intermediate data)
put(5, 2=>Rx(0.3)) |> autodiff(:BP)
Total: 5, DataType: Complex{Float64}
[∂] put on (2)
└─ Rot X gate: 0.3
or non-cheating quantum circuit simulation
put(5, 2=>Rx(0.3)) |> autodiff(:QC)
Total: 5, DataType: Complex{Float64}
put on (2)
└─ [̂∂] Rot X gate: 0.3
Parameter System
using the depth first searching strategy, we can find all parameters in a tree or subtree. Two relevant APIs are
- parameters(block), get all parameters in a (sub)tree rooted on block
- dispatch!([func], block, params), dispatch
params
into (sub)tree rooted onblock
, optional parameterfunc
can be used to custom parameter update rule.
@show parameters(circuit)
dispatch!(circuit, [0.1, 0.9])
@show parameters(circuit)
dispatch!(+, circuit, [0.1, 0.1])
@show parameters(circuit)
dispatch!(circuit, :zero)
@show parameters(circuit)
dispatch!(circuit, :random)
@show parameters(circuit);
parameters(circuit) = [0.785398, 0.6]
parameters(circuit) = [0.1, 0.9]
parameters(circuit) = [0.2, 1.0]
parameters(circuit) = [0.0, 0.0]
parameters(circuit) = [5.40807, 1.96619]
Intrinsic parameters
Intrinsic parameters are block's net contribution to total paramters, normally, we define these two APIs for subtyping blocks
iparameters(block)
,setiparameters!(block, params...)
,
@show iparameters(Rx(0.3))
@show setiparameters!(Rx(0.3), 1.2)
@show chain(Rx(0.3), Ry(0.5)) |> iparameters;
iparameters(Rx(0.3)) = 0.3
setiparameters!(Rx(0.3), 1.2) = Rot X gate: 1.2
chain(Rx(0.3), Ry(0.5)) |> iparameters = ()
Differentiable Blocks
see the independant chapter Automatic Differentiation
Time Evolution and Hamiltonian
docs are under preparation
This page was generated using Literate.jl.