import sys
sys.path.insert(0, '..')
import numpy as np
import qutip as qt
import src
from src import (utils, paulialg, stabilizer, circuit)

3.1. Clifford Gates#

CliffordGate class represents the Clifford gates.

  • Attribute:

    • qubits: a tuple containing the number of qubits that it acts on

    • n: number of qubits it acts on

    • generator(Pauli): the generator for \(C_{\pi/4}\) rotation. Default: None

    • forward_map(CliffordMap): forward Clifford Map. Default: None

    • backward_map(CliffordMap): backward Clifford Map. Default: None

  • Methods:

    • CliffordGate.set_generator(Pauli)

    • CliffordGate.set_forward_map(CliffordMap)

    • CliffordGate.set_backward_map(CliffordMap) TODO: we need to add a handle to prevent user set conflict Clifford maps

    • CliffordGate.copy(): return a copy of the Clifford gate

    • CliffordGate.independent_from(Other_gate): if there is no overlap between acting qubits, then they are independent.

    • CliffordGate.compile(): build (forward&backward) Clifford map representation. If generator is given, then it will be converted to forward/backward map; if forward map is given, the backward map will be calculated; if backward map is given, the forward map will be calculated

    • CliffordGate.forward(obj): forward transformation of the object. obj can be Pauli, PauliList, PauliPolynomial, StabilizerState.

CliffordGate is the low-level API(class) for Clifford gates. One can create a null Clifford gate by sepecifying what are the qubits this gate will act on:

Example: circuit.CliffordGate(1,2) will create a null gate acting on qubit 1, and 2. Null gate does not contain any forward_map or backward_map

gate = circuit.CliffordGate(1,2)
print("gate acts on: {}, and there are {} qubits the gates will act on.".format(
gate.qubits, gate.n
))
gate acts on: (1, 2), and there are 2 qubits the gates will act on.

There are three ways to equip the null gate with Clifford map:

  1. One can use gate.set_generator(Pauli). This will create a \(\pi/2\) rotation generated by the pauli string.

  2. One can use gate.set_forward_map(CliffordMap)

  3. One can use gate.set_backward_map(CliffordMap)

gate.set_generator(paulialg.pauli("XX"))
gate.set_forward_map(stabilizer.random_clifford_map(2))
gate.set_backward_map(stabilizer.random_clifford_map(2))

gate.compile() will construct both forward_clifford_map and backward_clifford_map. Compilation is not necessary!

gate.copy() will return a copy of the CliffordGate

One can apply Clifford Gate \(U\) or \(U^{\dagger}\) to: Pauli strings, Stabilizer States by gate.forward(obj) and gate.backward(obj)

psi = stabilizer.random_clifford_state(5)
gate = circuit.CliffordGate(1,2)
gate.set_generator(paulialg.pauli("XX"))
gate.forward(psi)
StabilizerState(
   +YIZZY
   +XYYXX
   +YZXYI
   +XZZYY
   -IYIZY)

We see the wavefunction \(|\psi\rangle\) has been changed in-place. And the gate only changes qubit 1&2.

If two Clifford gate acting on different qubits, then they are independent. We can check dependency by gate.independent_from(other_gate)

gate1 = circuit.CliffordGate(0,1)
gate2 = circuit.CliffordGate(2,3)
print(gate1.independent_from(gate2))
True
gate1 = circuit.CliffordGate(0,1)
gate2 = circuit.CliffordGate(1,2)
print(gate1.independent_from(gate2))
False