FLOYao
FLOYao.FLOYao
— ModuleA Yao.jl backend to efficiently simulate fermionic linear optics (FLO) circuits based on Classical simulation of noninteracting-fermion quantum circuits and Disorder-assisted error correction in Majorana chains. FLO circuits are a class of quantum circuits that are closely related to non-interacting fermions and can be efficiently simulated on classical computers, similar to the way Clifford circuits can be efficiently classically simulated, as is done in YaoClifford.jl.
The goal of FLOYao.jl
is that if you have code written in Yao.jl
that only uses FLO gates and other primitives that are efficiently simulatable in polynomial time and space, that you can simply replace your AbstractArrayReg
with a MajoranaReg
and run exactly the same simulation, with the same code but exponentially faster.
A brief introduction to fermionic linear optics circuits is found in the Documentation and a more in-depth introduction in e.g. the two papers linked above.
Installation
FLOYao
can be simply installed from the REPL via
pkg> add FLOYao
Quickstart
First import FLOYao
and Yao
using FLOYao, Yao
# output
then build a (here somewhat arbitrary) circuit consisting only of Supported gates
nq = 4
θ = π/8
circuit = chain(nq)
push!(circuit, put(nq, 3=>Rz(0.5)))
xxg1 = kron(nq, 1 => X, 2 => X)
rg = rot(xxg1, θ)
push!(circuit, rg)
xxg2 = kron(nq, 2 => X, 3 => Z, 4 => X)
rg = rot(xxg2, θ)
push!(circuit, rg)
push!(circuit, put(nq, 3=>Rz(0.5)))
push!(circuit, put(nq, 1=>Z))
xxg3 = kron(nq, 2 => X, 3 => X)
rg = rot(xxg3, θ)
circuit
and define an observable that is a sum of squares of Majorana operators
hamiltonian = xxg1 + xxg2 + xxg3 + kron(nq, 2=>Z) + kron(nq, 3=>Z)
and finally create a register in the computational zero state via
reg = FLOYao.zero_state(nq)
# output
MajoranaReg{Float64} with 4 qubits:
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
Applying the circuit to the register works then exactly the same way as for a normal ArrayReg
register:
apply(reg, circuit)
# output
MajoranaReg{Float64} with 4 qubits:
-1.0 -0.0 -0.0 … -0.0 -0.0
-0.0 -0.9238795325112867 0.3826834323650898 -0.0 -0.0
0.0 0.3826834323650898 0.9238795325112867 0.0 0.0
0.0 0.0 0.0 0.3826834323650898 0.0
0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 … 0.0 0.0
0.0 0.0 0.0 0.9238795325112867 0.0
0.0 0.0 0.0 0.0 1.0
and the same goes for expectation values of observables
expect(hamiltonian, reg => circuit)
# output
1.8535533905932737
or even gradients of these expectation values with respect to the circuit parameters
state_grad, params_grad = expect'(hamiltonian, reg => circuit)
# output
MajoranaReg{Float64}(4) => [0.0, -0.3535533905932738, -0.3535533905932738, 0.0]