Skip to content

Commit

Permalink
Use propagation rules for propagating a pauli into a clifford
Browse files Browse the repository at this point in the history
  • Loading branch information
daehiff committed Jul 28, 2024
1 parent 768c730 commit bd704c4
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 121 deletions.
197 changes: 79 additions & 118 deletions pauliopt/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,37 +152,101 @@ def copy(self):
return self.__class__(self.phase, self.qubits)


PROPAGATION_H = {
"X": (Pauli.Z, 1),
"Y": (Pauli.Y, -1),
"Z": (Pauli.X, 1),
"I": (Pauli.I, 1),
}

PROPAGATION_S = {
"X": (Pauli.Y, -1),
"Y": (Pauli.X, 1),
"Z": (Pauli.Z, 1),
"I": (Pauli.I, 1),
}

PROPAGATION_CX = {
"XX": (Pauli.X, Pauli.I, 1),
"XY": (Pauli.Y, Pauli.Z, 1),
"XZ": (Pauli.Y, Pauli.Y, -1),
"XI": (Pauli.X, Pauli.X, 1),
"YX": (Pauli.Y, Pauli.I, 1),
"YY": (Pauli.X, Pauli.Z, -1),
"YZ": (Pauli.X, Pauli.Y, 1),
"YI": (Pauli.Y, Pauli.X, 1),
"ZX": (Pauli.Z, Pauli.X, 1),
"ZY": (Pauli.I, Pauli.Y, 1),
"ZZ": (Pauli.I, Pauli.Z, 1),
"ZI": (Pauli.Z, Pauli.I, 1),
"IX": (Pauli.I, Pauli.X, 1),
"IY": (Pauli.Z, Pauli.Y, 1),
"IZ": (Pauli.Z, Pauli.Z, 1),
"II": (Pauli.I, Pauli.I, 1),
}


class CliffordGate(Gate, ABC):
def __init__(self, *qubits):
super().__init__(*qubits)

@abstractmethod
def propagate_pauli(self, gadget: "pauliopt.pauli.pauli_gadget.PauliGadget"):
pass

@abstractmethod
def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:
"""
Every clifford must me decomposable into a list of H, S and CX gates.
Returns:
"""
pass

def propagate_pauli(self, gadget: "pauliopt.pauli.pauli_gadget.PauliGadget"):
"""
Propagate a pauli gate through a gadget using the H, S, CX decomposition rules.
One can define for H, S and CX propagation rules, which are defined in the dictionaries above.
Args:
gadget:
Returns:
"""
h_s_cx_decomposition = self.get_h_s_cx_decomposition()
for gate in reversed(h_s_cx_decomposition):
if gate.name == "H":
assert isinstance(gate, SingleQubitClifford)
p_string = gadget.paulis[gate.qubit].value
new_p, phase_change = PROPAGATION_H[p_string]
gadget.paulis[gate.qubit] = new_p
if phase_change == -1:
gadget.angle *= phase_change
elif gate.name == "S":
assert isinstance(gate, SingleQubitClifford)
p_string = gadget.paulis[gate.qubit].value
new_p, phase_change = PROPAGATION_S[p_string]
gadget.paulis[gate.qubit] = new_p
if phase_change == -1:
gadget.angle *= phase_change
elif gate.name == "CX":
assert isinstance(gate, TwoQubitClifford)
p_string = gadget.paulis[gate.control].value + gadget.paulis[gate.target].value
p_c, p_t, phase_change = PROPAGATION_CX[p_string]
gadget.paulis[gate.control] = p_c
gadget.paulis[gate.target] = p_t
if phase_change == -1:
gadget.angle *= phase_change

return gadget


class SingleQubitClifford(CliffordGate, ABC):

def __init__(self, qubit: int):
super().__init__((qubit))

@property
def qubit(self):
return self.qubits[0]

def propagate_pauli(self, gadget: "pauliopt.pauli.pauli_gadget.PauliGadget"):
if self.rules is None:
raise Exception(f"{self} has no rules defined for propagation!")
p_string = gadget.paulis[self.qubits[0]].value
new_p, phase_change = self.rules[p_string]
gadget.paulis[self.qubits[0]] = new_p
if phase_change == -1:
gadget.angle *= phase_change
return gadget


class TwoQubitClifford(CliffordGate, ABC):
def __init__(self, control, target):
Expand All @@ -197,33 +261,11 @@ def control(self):
def target(self):
return self.qubits[1]

def propagate_pauli(self, gadget: "pauliopt.pauli.pauli_gadget.PauliGadget"):
if self.rules is None:
raise Exception(f"{self} has no rules defined for propagation!")
pauli_size = len(gadget)
if self.control >= pauli_size or self.target >= pauli_size:
raise Exception(
f"Control: {self.control} or Target {self.target} out of bounds: {pauli_size}"
)
p_string = gadget.paulis[self.control].value + gadget.paulis[self.target].value
p_c, p_t, phase_change = self.rules[p_string]
gadget.paulis[self.control] = p_c
gadget.paulis[self.target] = p_t
if phase_change == -1:
gadget.angle *= phase_change
return gadget

def copy(self):
return self.__class__(self.control, self.target)


class H(SingleQubitClifford):
rules = {
"X": (Pauli.Z, 1),
"Y": (Pauli.Y, -1),
"Z": (Pauli.X, 1),
"I": (Pauli.I, 1),
}
n_qubits = 1
width = 40

Expand Down Expand Up @@ -257,7 +299,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:
return [H(*self.qubits)]


# TODO rules!, inverse
class X(SingleQubitClifford):
n_qubits = 1
draw_as_zx = True
Expand All @@ -281,7 +322,6 @@ def get_h_s_cx_decomposition(self):
return [H(*self.qubits), S(*self.qubits), S(*self.qubits), H(*self.qubits)]


# TODO rules!, inverse
class Z(SingleQubitClifford):
n_qubits = 1
draw_as_zx = True
Expand All @@ -305,7 +345,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:
return [S(*self.qubits), S(*self.qubits)]


# TODO rules!, inverse
class Y(SingleQubitClifford):
n_qubits = 1
draw_as_zx = True
Expand Down Expand Up @@ -333,12 +372,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:


class S(SingleQubitClifford):
rules = {
"X": (Pauli.Y, -1),
"Y": (Pauli.X, 1),
"Z": (Pauli.Z, 1),
"I": (Pauli.I, 1),
}
n_qubits = 1
draw_as_zx = True

Expand All @@ -362,12 +395,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:


class Sdg(SingleQubitClifford):
rules = {
"X": (Pauli.Y, 1),
"Y": (Pauli.X, -1),
"Z": (Pauli.Z, 1),
"I": (Pauli.I, 1),
}
n_qubits = 1
draw_as_zx = True

Expand All @@ -391,12 +418,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:


class V(SingleQubitClifford):
rules = {
"X": (Pauli.X, 1),
"Y": (Pauli.Z, -1),
"Z": (Pauli.Y, 1),
"I": (Pauli.I, 1),
}
n_qubits = 1
draw_as_zx = True

Expand All @@ -420,12 +441,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:


class Vdg(SingleQubitClifford):
rules = {
"X": (Pauli.X, 1),
"Y": (Pauli.Z, 1),
"Z": (Pauli.Y, -1),
"I": (Pauli.I, 1),
}
n_qubits = 1
draw_as_zx = True

Expand Down Expand Up @@ -488,7 +503,7 @@ def inverse(self):
return T(*self.qubits)


class SWAP(Gate):
class SWAP(CliffordGate):
n_qubits = 2
draw_as_zx = True

Expand All @@ -514,24 +529,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:


class CX(TwoQubitClifford):
rules = {
"XX": (Pauli.X, Pauli.I, 1),
"XY": (Pauli.Y, Pauli.Z, 1),
"XZ": (Pauli.Y, Pauli.Y, -1),
"XI": (Pauli.X, Pauli.X, 1),
"YX": (Pauli.Y, Pauli.I, 1),
"YY": (Pauli.X, Pauli.Z, -1),
"YZ": (Pauli.X, Pauli.Y, 1),
"YI": (Pauli.Y, Pauli.X, 1),
"ZX": (Pauli.Z, Pauli.X, 1),
"ZY": (Pauli.I, Pauli.Y, 1),
"ZZ": (Pauli.I, Pauli.Z, 1),
"ZI": (Pauli.Z, Pauli.I, 1),
"IX": (Pauli.I, Pauli.X, 1),
"IY": (Pauli.Z, Pauli.Y, 1),
"IZ": (Pauli.Z, Pauli.Z, 1),
"II": (Pauli.I, Pauli.I, 1),
}
n_qubits = 2
draw_as_zx = True
width = 40
Expand Down Expand Up @@ -572,24 +569,6 @@ def inverse(self):


class CY(TwoQubitClifford):
rules = {
"XX": (Pauli.Y, Pauli.Z, -1),
"XY": (Pauli.X, Pauli.I, 1),
"XZ": (Pauli.Y, Pauli.X, 1),
"XI": (Pauli.X, Pauli.Y, 1),
"YX": (Pauli.X, Pauli.Z, 1),
"YY": (Pauli.Y, Pauli.I, 1),
"YZ": (Pauli.X, Pauli.X, -1),
"YI": (Pauli.Y, Pauli.Y, 1),
"ZX": (Pauli.I, Pauli.X, 1),
"ZY": (Pauli.Z, Pauli.Y, 1),
"ZZ": (Pauli.I, Pauli.Z, 1),
"ZI": (Pauli.Z, Pauli.I, 1),
"IX": (Pauli.Z, Pauli.X, 1),
"IY": (Pauli.I, Pauli.Y, 1),
"IZ": (Pauli.Z, Pauli.Z, 1),
"II": (Pauli.I, Pauli.I, 1),
}
n_qubits = 2
draw_as_zx = True

Expand All @@ -615,24 +594,6 @@ def get_h_s_cx_decomposition(self) -> List[Union["H", "S", "CX"]]:


class CZ(TwoQubitClifford):
rules = {
"XX": (Pauli.Y, Pauli.Y, 1),
"XY": (Pauli.Y, Pauli.X, -1),
"XZ": (Pauli.X, Pauli.I, 1),
"XI": (Pauli.X, Pauli.Z, 1),
"YX": (Pauli.X, Pauli.Y, -1),
"YY": (Pauli.X, Pauli.X, 1),
"YZ": (Pauli.Y, Pauli.I, 1),
"YI": (Pauli.Y, Pauli.Z, 1),
"ZX": (Pauli.I, Pauli.X, 1),
"ZY": (Pauli.I, Pauli.Y, 1),
"ZZ": (Pauli.Z, Pauli.Z, 1),
"ZI": (Pauli.Z, Pauli.I, 1),
"IX": (Pauli.Z, Pauli.X, 1),
"IY": (Pauli.Z, Pauli.Y, 1),
"IZ": (Pauli.I, Pauli.Z, 1),
"II": (Pauli.I, Pauli.I, 1),
}
n_qubits = 2
draw_as_zx = True

Expand Down
6 changes: 3 additions & 3 deletions tests/pauli/test_pauli_propagation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
generate_random_depth_1_clifford,
)
from pauliopt.circuits import CX, CY, CZ, H, S, V, Circuit
from pauliopt.gates import Vdg, Sdg
from pauliopt.gates import Vdg, Sdg, Y, X, Z, SWAP
from pauliopt.topologies import Topology


class TestPauliPropagation(unittest.TestCase):
def test_circuit_construction(self):
for num_qubits in [2, 3, 4, 5, 6]:
for num_qubits in [2, 3, 4]:
for topo_creation in [Topology.line, Topology.complete]:
pp = generate_all_combination_pauli_polynomial(n_qubits=num_qubits)

Expand All @@ -41,7 +41,7 @@ def test_gate_propagation(self):
for num_qubits in [2, 3, 4]:
pp = generate_all_combination_pauli_polynomial(n_qubits=num_qubits)
inital_qc = pp.to_qiskit()
for gate_class in [CX, CY, CZ, H, S, V, Vdg, Sdg]:
for gate_class in [H, S, CX, V, Vdg, Sdg, X, Y, Z, CY, CZ, SWAP]:
gate = generate_random_depth_1_clifford(gate_class, num_qubits)
gate_circ = Circuit(num_qubits)
gate_circ.add_gate(gate)
Expand Down

0 comments on commit bd704c4

Please sign in to comment.