Pauli Basis and Clifford group

In this section, we introduce the definition of Pauli strings and basic operations on them. We also introduce the Clifford group and how to simulate a Clifford circuit applied on Pauli strings.

Pauli Strings

A pauli string is a tensor product of Pauli operators acting on different qubits. PauliString is a subtype of [CompositeBlock] with a field ids storing the Pauli operators. We can define pauli string with PauliString or paulistring.

using TensorQEC, TensorQEC.Yao
PauliString(X, I2, Z, Y) # X_1Z_3Y_4

paulistring(4, X, (1, 2, 4)) # X_1X_2X_4
XXIX

We can use Yao.mat to get the matrix representation of a Pauli string.

mat(ComplexF64, PauliString(X, Z)) # X_1Z_2
4×4 LuxurySparse.SDPermMatrix{ComplexF64, Int64, Vector{ComplexF64}, Vector{Int64}}:
 0.0+0.0im  1.0+0.0im   0.0+0.0im   0.0+0.0im
 1.0+0.0im  0.0+0.0im   0.0+0.0im   0.0+0.0im
 0.0+0.0im  0.0+0.0im   0.0+0.0im  -1.0+0.0im
 0.0+0.0im  0.0+0.0im  -1.0+0.0im   0.0+0.0im

Pauli Basis

pauli_basis generates all the Pauli strings of a given length. Those Pauli strings are stored in a high-dimensional array.

pauli_basis(2)
4×4 Matrix{PauliString{2}}:
 II  IX  IY  IZ
 XI  XX  XY  XZ
 YI  YX  YY  YZ
 ZI  ZX  ZY  ZZ

pauli_decomposition returns the coefficients of a matrix in the Pauli basis.

pauli_decomposition(ConstGate.CNOT)
4×4 Matrix{ComplexF64}:
 0.5+0.0im   0.5+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im   0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im   0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.5+0.0im  -0.5+0.0im  0.0+0.0im  0.0+0.0im

That implies that $CNOT = \frac{1}{2} (I \otimes I + I \otimes X + Z \otimes I - Z \otimes X)$. We can check this by

0.5*(mat(kron(I2,I2) + kron(I2,X) + kron(Z,I2) - kron(Z,X))) == mat(ConstGate.CNOT)
true

pauli_mapping returns the matrix representation of a quantum gate in the Pauli basis. For Hadamard gate H, we know that $HIH = I, HXH = Z, HYH = -Y, HZH = X$. We can convert $H$ into the Pauli basis.

pauli_mapping(H)
4×4 Matrix{Float64}:
 1.0  0.0   0.0  0.0
 0.0  0.0   0.0  1.0
 0.0  0.0  -1.0  0.0
 0.0  1.0   0.0  0.0

Clifford Group

Clifford group can be generated by Hadamard gate, S gate and CNOT gate[Bravyi2022]. We can generate the Clifford group with clifford_group.

clifford_group(1)
24-element Vector{LuxurySparse.PermMatrix{Int8, UInt8, StaticArraysCore.SVector{4, Int8}, StaticArraysCore.SVector{4, UInt8}}}:
 [1 0 0 0; 0 0 0 1; 0 0 -1 0; 0 1 0 0]
 [1 0 0 0; 0 0 1 0; 0 -1 0 0; 0 0 0 1]
 [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]
 [1 0 0 0; 0 0 0 1; 0 1 0 0; 0 0 1 0]
 [1 0 0 0; 0 0 -1 0; 0 0 0 -1; 0 1 0 0]
 [1 0 0 0; 0 -1 0 0; 0 0 -1 0; 0 0 0 1]
 [1 0 0 0; 0 1 0 0; 0 0 0 -1; 0 0 1 0]
 [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 -1 0]
 [1 0 0 0; 0 0 0 1; 0 0 1 0; 0 -1 0 0]
 [1 0 0 0; 0 0 1 0; 0 0 0 1; 0 1 0 0]
 ⋮
 [1 0 0 0; 0 1 0 0; 0 0 -1 0; 0 0 0 -1]
 [1 0 0 0; 0 0 1 0; 0 1 0 0; 0 0 0 -1]
 [1 0 0 0; 0 -1 0 0; 0 0 0 1; 0 0 1 0]
 [1 0 0 0; 0 -1 0 0; 0 0 0 -1; 0 0 -1 0]
 [1 0 0 0; 0 0 0 -1; 0 0 -1 0; 0 -1 0 0]
 [1 0 0 0; 0 0 -1 0; 0 -1 0 0; 0 0 0 -1]
 [1 0 0 0; 0 0 -1 0; 0 0 0 1; 0 -1 0 0]
 [1 0 0 0; 0 -1 0 0; 0 0 1 0; 0 0 0 -1]
 [1 0 0 0; 0 0 0 -1; 0 1 0 0; 0 0 -1 0]

Each element in the Clifford group acts on pauli basis as a permutation matrix. For $n= 1, 2$, and $3$, this group contains $24$, $11520$, and $92897280$ elements, respectively. We can use to_perm_matrix to convert a matrix into a permutation matrix.

pm = to_perm_matrix(H)
pm.perm, pm.vals
([1, 4, 3, 2], Int8[1, 1, -1, 1])

With the permutation matrix, we can apply a Clifford gate to a Pauli string by perm_of_paulistring. Here we apply the Hadamard gate to the second qubit of Pauli string $I_1X_2$ and get $I_1Z_2$ with a phase $1$.

ps1 = PauliString(I2, X)
ps2, phase = perm_of_paulistring(ps1, [2]=>pm)
ps1, ps2, phase
(IX, IZ, 1)

Put those all together, we can apply a Clifford circuit to a Pauli string by clifford_simulate.

qc = chain(put(5, 1 => H), control(5, 1, 2 => Z), control(5, 3, 4 => X), control(5, 5, 3 => X), put(5, 1 => X))
vizcircuit(qc)
Example block output

Apply the circuit to Pauli string $Z_1Y_2I_3Y_4X_5$, we get $Y_1X_2Y_3Y_4Y_5$ with a phase $1$.

ps = PauliString(Z, Y, I2, Y, X)
res = clifford_simulate(ps, qc)
ps2 = res.output
YXYYY

where res.output is the Pauli string after the Clifford circuit and res.phase is the phase factor. It corresponds to the following quantum circuit.

clifford_simulation_circuit = chain(qc', ps, qc)
CircuitStyles.barrier_for_chain[] = true  # setup barrier for better visualization
vizcircuit(clifford_simulation_circuit)
Example block output

We can check the result by

CircuitStyles.barrier_for_chain[] = false  # disable barrier
res.phase * mat(clifford_simulation_circuit) ≈ mat(ps2)
true

We can also visualize the history of Pauli strings by annotate_history.

annotate_history(res)
Example block output

This page was generated using Literate.jl.

  • Bravyi2022Bravyi, S., Latone, J.A., Maslov, D., 2022. 6-qubit optimal Clifford circuits. npj Quantum Inf 8, 1–12. https://doi.org/10.1038/s41534-022-00583-7