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 stateArrayReg{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 stateArrayReg{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⟩)/√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) └─ H
julia> 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.25im
julia> 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=>Z
julia> 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))
Example block output

More details about the plotting can be found in the manual: Quantum Circuit Visualization.