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 Yaojulia> ArrayReg(randn(ComplexF64, 2^3)) # a random unnormalized 3-qubit stateArrayReg{2, ComplexF64, Array...} active qubits: 3/3 nlevel: 2julia> zero_state(5) # |00000⟩ArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2julia> rand_state(5) # a random stateArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2julia> product_state(bit"10100") # |10100⟩ArrayReg{2, ComplexF64, Array...} active qubits: 5/5 nlevel: 2julia> ghz_state(5) # (|00000⟩ + |11111⟩)/√2ArrayReg{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 gateA (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 circuitnqubits: 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) └─ Hjulia> mat(circuit) # the matrix representation of the circuit8×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.25imjulia> apply!(zero_state(3), circuit) # apply the circuit to a zero stateArrayReg{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 Hamiltoniannqubits: 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=>Zjulia> mat(h) # the matrix representation of the Hamiltonian32×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.