Quick Start
In this quick start, we list several common use cases for Yao before you go deeper into the manual.
Create a quantum register/state
A register is an object that describes a device with an internal state. See Registers for more details. Yao use registers to represent quantum states. The most common register is the ArrayReg
, you can create it by feeding a state vector to it, e.g
julia> using Yao
julia> ArrayReg(randn(ComplexF64, 2^3)) # a random unnormalized 3-qubit state
ArrayReg{2, ComplexF64, Array...} active qubits: 3/3 nlevel: 2
julia> zero_state(5) # |00000⟩
ArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2
julia> rand_state(5) # a random state
ArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2
julia> product_state(bit"10100") # |10100⟩
ArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2
julia> ghz_state(5) # (|00000⟩ + |11111⟩)/√2
ArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2
the internal quantum state can be accessed via statevec
method
julia> statevec(ghz_state(2))
4-element Vector{ComplexF64}: 0.7071067811865476 - 0.0im 0.0 + 0.0im 0.0 + 0.0im 0.7071067811865476 - 0.0im
for more functionalities about registers please refer to the manual of Registers.
Create quantum circuit
Yao introduces an abstract representation for linear maps, called "block"s, which can be used to represent quantum circuits, Hamiltonians, and other quantum operations. The following code creates a 2-qubit circuit
julia> chain(2, put(1=>H), put(2=>X))
nqubits: 2 chain ├─ put on (1) │ └─ H └─ put on (2) └─ X
where H
gate is at 1st qubit, X
gate is at 2nd qubit. A more advanced example is the quantum Fourier transform circuit
julia> A(i, j) = control(i, j=>shift(2π/(1<<(i-j+1)))) # a cphase gate
A (generic function with 1 method)
julia> B(n, k) = chain(n, j==k ? put(k=>H) : A(j, k) for j in k:n)
B (generic function with 1 method)
julia> qft(n) = chain(B(n, k) for k in 1:n)
qft (generic function with 1 method)
julia> circuit = qft(3) # a 3-qubit QFT circuit
nqubits: 3 chain ├─ chain │ ├─ put on (1) │ │ └─ H │ ├─ control(2) │ │ └─ (1,) shift(1.5707963267948966) │ └─ control(3) │ └─ (1,) shift(0.7853981633974483) ├─ chain │ ├─ put on (2) │ │ └─ H │ └─ control(3) │ └─ (2,) shift(1.5707963267948966) └─ chain └─ put on (3) └─ H
julia> mat(circuit) # the matrix representation of the circuit
8×8 SparseMatrixCSC{ComplexF64, Int64} with 64 stored entries: 0.353553+0.0im 0.353553+0.0im … 0.353553+0.0im 0.353553+0.0im -0.353553+0.0im 0.25-0.25im 0.353553+0.0im 0.353553+0.0im -2.16489e-17-0.353553im 0.353553+0.0im -0.353553+0.0im -0.25-0.25im 0.353553+0.0im 0.353553+0.0im -0.353553+0.0im 0.353553+0.0im -0.353553+0.0im … -0.25+0.25im 0.353553+0.0im 0.353553+0.0im 2.16489e-17+0.353553im 0.353553+0.0im -0.353553+0.0im 0.25+0.25im
julia> apply!(zero_state(3), circuit) # apply the circuit to a zero state
ArrayReg{2, ComplexF64, Array...} active qubits: 3/3 nlevel: 2
More details about available blocks can be found in the manual of Blocks.
Create Hamiltonian
We can create a simple Ising Hamiltonian on 1D chain as following
julia> h = sum([kron(5, i=>Z, mod1(i+1, 5)=>Z) for i in 1:5]) # a 5-qubit Ising Hamiltonian
nqubits: 5 + ├─ kron │ ├─ 1=>Z │ └─ 2=>Z ├─ kron │ ├─ 2=>Z │ └─ 3=>Z ├─ kron │ ├─ 3=>Z │ └─ 4=>Z ├─ kron │ ├─ 4=>Z │ └─ 5=>Z └─ kron ├─ 1=>Z └─ 5=>Z
julia> mat(h) # the matrix representation of the Hamiltonian
32×32 Diagonal{ComplexF64, Vector{ComplexF64}}: 5.0+0.0im ⋅ ⋅ … ⋅ ⋅ ⋅ ⋅ 1.0+0.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0+0.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋮ ⋱ ⋮ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ … ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 1.0+0.0im ⋅ ⋅ ⋅ ⋅ ⋅ … ⋅ 1.0+0.0im ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 5.0-0.0im
Differentiating a quantum circuit
Yao has its own automatic differentiation rule implemented, this allows one obtain gradients of a loss function by simply putting a '
mark following expect
or fidelity
, e.g
To obtain the gradient of the quantum Fourier transform circuit with respect to its parameters, one can use the following code
julia> grad_state, grad_circuit_params = expect'(kron(X, X, I2) + kron(I2, X, X), zero_state(3)=>qft(3))
ArrayReg{2, ComplexF64, Array...} active qubits: 3/3 nlevel: 2 => Any[0.0, 0.0, 0.0]
where kron(X, X, I2) + kron(I2, X, X)
is the target Hamiltonian, zero_state(3)
is the initial state, qft(3)
is the quantum Fourier transform circuit. The return value is a vector, each corresponding to the gradient of the loss function with respect to a parameter in the circuit. The list of parameters can be obtained by parameters
function.
julia> parameters(qft(3))
3-element Vector{Float64}: 1.5707963267948966 0.7853981633974483 1.5707963267948966
To obtain the gradient of the fidelity between a state parameterized by a quantum circuit and a target state, one can use the following code
julia> ((grad_state1, grad_circuit1), grad_state2) = fidelity'(zero_state(3)=>qft(3), ghz_state(3))
(ArrayReg{2, ComplexF64, Array...} active qubits: 3/3 nlevel: 2 => Any[0.0, 0.0, 0.0], ArrayReg{2, ComplexF64, Array...} active qubits: 3/3 nlevel: 2)
where zero_state(3)
is the initial state, qft(3)
is the quantum Fourier transform circuit, ghz_state(3)
is the target state.
The automatic differentiation functionality can also be accessed by interfacing with the machine learning libraries Zygote
.
Plot quantum circuits
The component package YaoPlots
provides plotting for quantum circuits and ZX diagrams. You can use it to visualize your quantum circuits in VSCode
, Jupyter
notebook or Pluto
notebook.
using Yao.EasyBuild, Yao.YaoPlots
using Compose
# show a qft circuit
vizcircuit(qft_circuit(5))
More details about the plotting can be found in the manual: Quantum Circuit Visualization.