Extending Blocks
Extending constant gate
We prepared a macro for you about constant gates like X
, Y
, Z
.
Simply use @const_gate
.
Extending Primitive Block with parameters
First, define your own block type by subtyping PrimitiveBlock
. And import methods you will need to overload
using Yao, Yao.Blocks
import Yao.Blocks: mat, dispatch!, parameters # this is the mimimal methods you will need to overload
mutable struct NewPrimitive{T} <: PrimitiveBlock{1, T}
theta::T
end
Second define its matrix form.
mat(g::NewPrimitive{T}) where T = Complex{T}[sin(g.theta) 0; cos(g.theta) 0]
mat (generic function with 112 methods)
Yao will use this matrix to do the simulation by default. However, if you know how to directly apply your block to a quantum register, you can also overload apply!
to make your simulation become more efficient. But this is not required.
import Yao.Blocks: apply!
apply!(r::AbstractRegister, x::NewPrimitive) = # some efficient way to simulate this block
Third If your block contains parameters, declare which member it is with dispatch!
and how to get them by parameters
dispatch!(g::NewPrimitive, theta) = (g.theta = theta; g)
parameters(x::NewPrimitive) = x.theta
parameters (generic function with 3 methods)
The prototype of dispatch!
is simple, just directly write the parameters as your function argument. e.g
mutable struct MultiParam{N, T} <: PrimitiveBlock{N, Complex{T}}
theta::T
phi::T
end
just write:
dispatch!(x::MultiParam, theta, phi) = (x.theta = theta; x.phi = phi; x)
or maybe your block contains a vector of parameters:
mutable struct VecParam{N, T} <: PrimitiveBlock{N, T}
params::Vector{T}
end
just write:
dispatch!(x::VecParam, params) = (x.params .= params; x)
be careful, the assignment should be in-placed with .=
rather than =
.
If the number of parameters in your new block is fixed, we recommend you to declare this with a type trait nparameters
:
import Yao.Blocks: nparameters
nparameters(::Type{<:NewPrimitive}) = 1
nparameters (generic function with 2 methods)
But it is OK if you do not define this trait, Yao will find out how many parameters you have dynamically.
Fourth If you want to enable cache of this new block, you have to define your own cachekey. usually just use your parameters as the key if you want to cache the matrix form of different parameters, which will accelerate your simulation with a cost of larger memory allocation. You can simply define it with [`cachekey`](@ref)
import Yao.Blocks: cache_key
cache_key(x::NewPrimitive) = x.theta
cache_key (generic function with 16 methods)
Extending Composite Blocks
Composite blocks are blocks that are able to contain other blocks. To define a new composite block you only need to define your new type as a subtype of CompositeBlock
, and define a new method called subblocks
which will provide an iterator that iterates the blocks contained by this composite block.
Custom Pretty Printing
The whole quantum circuit is represented as a tree in the block system. Therefore, we print a block as a tree. To define your own syntax to print, simply overloads the print_block
method. Then it will appears in the block tree syntax automatically.
print_block(io::IO, block::MyBlockType)
Adding Operator Traits to Your Blocks
A gate G
can have following traits
isunitary
- $G^\dagger G = \mathbb{1}$isreflexive
- $GG = \mathbb{1}$ishermitian
- $G^\dagger = G$
If G
is a MatrixBlock
, these traits can fall back to using mat
method albiet not efficient. If you can know these traits of a gate clearly, you can define them by hand to improve performance.
These traits are useful, e.g. a RotationGate
defines an SU(2) rotation, which requires its generator both hermitian a reflexive so that $R_G(\theta) = \cos\frac{\theta}{2} - i\sin\frac{\theta}{2} G$, so that you can use $R_{\rm X}$ and $R_{\rm CNOT}$ but not $R_{\rm R_X(0.3)}$.
Adding Tags to Your Blocks
A tag refers to
Daggered
- $G^\dagger$ We useBase.adjoint(G)
to generate a daggered block.- If a block is hermitian, do nothing,
- For many blocks, e.g.
Rx(0.3)
, we can still define some rule likeBase.adjoint(r::RotationBlock) = (res = copy(r); res.theta = -r.theta; res)
, - if even simple rule does not exist, its
mat
function will fall back tomat(G)'
.
CachedBlock
- the matrix of this block under current parameter will be stored in cache server for future use.G |> cache
can be useful when you are trying to compile a block into a reuseable matrix, to use cache, you should definecache_key
.