Source code for graphix.transpiler

"""Gate-to-MBQC transpiler

accepts desired gate operations and transpile into MBQC measurement patterns.

"""
import numpy as np
from graphix.ops import Ops
from copy import deepcopy
from graphix.pattern import Pattern
from graphix.sim.statevec import Statevec


[docs]class Circuit: """Gate-to-MBQC transpiler. Holds gate operations and translates into MBQC measurement patterns. Attributes ---------- width : int Number of logical qubits (for gate network) instruction : list List containing the gate sequence applied. """
[docs] def __init__(self, width): """ Parameters ---------- width : int number of logical qubits for the gate network """ self.width = width self.instruction = []
[docs] def cnot(self, control, target): """CNOT gate Prameters --------- control : int control qubit target : int target qubit """ assert control in np.arange(self.width) assert target in np.arange(self.width) assert control != target self.instruction.append(["CNOT", [control, target]])
[docs] def h(self, qubit): """Hadamard gate Prameters --------- qubit : int target qubit """ assert qubit in np.arange(self.width) self.instruction.append(["H", qubit])
[docs] def s(self, qubit): """S gate Prameters --------- qubit : int target qubit """ assert qubit in np.arange(self.width) self.instruction.append(["S", qubit])
[docs] def x(self, qubit): """Pauli X gate Prameters --------- qubit : int target qubit """ assert qubit in np.arange(self.width) self.instruction.append(["X", qubit])
[docs] def y(self, qubit): """Pauli Y gate Prameters --------- qubit : int target qubit """ assert qubit in np.arange(self.width) self.instruction.append(["Y", qubit])
[docs] def z(self, qubit): """Pauli Z gate Prameters --------- qubit : int target qubit """ assert qubit in np.arange(self.width) self.instruction.append(["Z", qubit])
[docs] def rx(self, qubit, angle): """X rotation gate Prameters --------- qubit : int target qubit angle : float rotation angle in radian """ assert qubit in np.arange(self.width) self.instruction.append(["Rx", qubit, angle])
[docs] def ry(self, qubit, angle): """Y rotation gate Prameters --------- qubit : int target qubit angle : float angle in radian """ assert qubit in np.arange(self.width) self.instruction.append(["Ry", qubit, angle])
[docs] def rz(self, qubit, angle): """Z rotation gate Prameters --------- qubit : int target qubit angle : float rotation angle in radian """ assert qubit in np.arange(self.width) self.instruction.append(["Rz", qubit, angle])
def rzz(self, control, target, angle): r"""ZZ-rotation gate. Equivalent to the sequence CNOT(control, target), Rz(target, angle), CNOT(control, target) and realizes rotation expressed by :math:`e^{-i \frac{\theta}{2} Z_c Z_t}`. Prameters --------- qubit : int target qubit angle : float rotation angle in radian """ assert control in np.arange(self.width) assert target in np.arange(self.width) self.instruction.append(["Rzz", [control, target], angle]) def i(self, qubit): """identity (teleportation) gate Prameters --------- qubit : int target qubit """ assert qubit in np.arange(self.width) self.instruction.append(["I", qubit])
[docs] def transpile(self, opt=False): """gate-to-MBQC transpile function. Parameters ---------- opt : bool Whether or not to use pre-optimized gateset with local-Clifford decoration. Returns -------- pattern : :class:`graphix.pattern.Pattern` object """ Nnode = self.width out = [j for j in range(self.width)] pattern = Pattern(self.width) for instr in self.instruction: if instr[0] == "CNOT": ancilla = [Nnode, Nnode + 1] out[instr[1][0]], out[instr[1][1]], seq = self._cnot_command( out[instr[1][0]], out[instr[1][1]], ancilla ) pattern.seq.extend(seq) Nnode += 2 elif instr[0] == "I": pass elif instr[0] == "H": ancilla = Nnode out[instr[1]], seq = self._h_command(out[instr[1]], ancilla) pattern.seq.extend(seq) Nnode += 1 elif instr[0] == "S": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._s_command(out[instr[1]], ancilla) pattern.seq.extend(seq) Nnode += 2 elif instr[0] == "X": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._x_command(out[instr[1]], ancilla) pattern.seq.extend(seq) Nnode += 2 elif instr[0] == "Y": ancilla = [Nnode, Nnode + 1, Nnode + 2, Nnode + 3] out[instr[1]], seq = self._y_command(out[instr[1]], ancilla) pattern.seq.extend(seq) Nnode += 4 elif instr[0] == "Z": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._z_command(out[instr[1]], ancilla) pattern.seq.extend(seq) Nnode += 2 elif instr[0] == "Rx": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._rx_command(out[instr[1]], ancilla, instr[2]) pattern.seq.extend(seq) Nnode += 2 elif instr[0] == "Ry": ancilla = [Nnode, Nnode + 1, Nnode + 2, Nnode + 3] out[instr[1]], seq = self._ry_command(out[instr[1]], ancilla, instr[2]) pattern.seq.extend(seq) Nnode += 4 elif instr[0] == "Rz": if opt: ancilla = Nnode out[instr[1]], seq = self._rz_command_opt(out[instr[1]], ancilla, instr[2]) pattern.seq.extend(seq) Nnode += 1 else: ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._rz_command(out[instr[1]], ancilla, instr[2]) pattern.seq.extend(seq) Nnode += 2 elif instr[0] == "Rzz": if opt: ancilla = Nnode out[instr[1][0]], out[instr[1][1]], seq = self._rzz_command_opt( out[instr[1][0]], out[instr[1][1]], ancilla, instr[2] ) pattern.seq.extend(seq) Nnode += 1 else: raise NotImplementedError( "YZ-plane measurements not accepted and Rzz gate\ cannot be directly transpiled" ) else: raise ValueError("Unknown instruction, commands not added") pattern.output_nodes = out pattern.Nnode = Nnode return pattern
[docs] def standardize_and_transpile(self, opt=True): """gate-to-MBQC transpile function. Commutes all byproduct through gates, instead of through measurement commands, to generate standardized measurement pattern. Parameters ---------- opt : bool Whether or not to use pre-optimized gateset with local-Clifford decoration. Returns -------- pattern : :class:`graphix.pattern.Pattern` object """ self._N = [] for i in range(self.width): self._N.append(["N", i]) self._M = [] self._E = [] self._instr = [] Nnode = self.width out = [j for j in range(self.width)] for instr in self.instruction: if instr[0] == "CNOT": ancilla = [Nnode, Nnode + 1] out[instr[1][0]], out[instr[1][1]], seq = self._cnot_command( out[instr[1][0]], out[instr[1][1]], ancilla ) self._N.extend(seq[0:2]) self._E.extend(seq[2:5]) self._M.extend(seq[5:7]) Nnode += 2 self._instr.append(instr) self._instr.append(["XC", instr[1][1], seq[7][2]]) self._instr.append(["ZC", instr[1][1], seq[8][2]]) self._instr.append(["ZC", instr[1][0], seq[9][2]]) elif instr[0] == "I": pass elif instr[0] == "H": ancilla = Nnode out[instr[1]], seq = self._h_command(out[instr[1]], ancilla) self._N.append(seq[0]) self._E.append(seq[1]) self._M.append(seq[2]) self._instr.append(instr) self._instr.append(["XC", instr[1], seq[3][2]]) Nnode += 1 elif instr[0] == "S": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._s_command(out[instr[1]], ancilla) self._N.extend(seq[0:2]) self._E.extend(seq[2:4]) self._M.extend(seq[4:6]) self._instr.append(instr) self._instr.append(["XC", instr[1], seq[6][2]]) self._instr.append(["ZC", instr[1], seq[7][2]]) Nnode += 2 elif instr[0] == "X": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._x_command(out[instr[1]], ancilla) self._N.extend(seq[0:2]) self._E.extend(seq[2:4]) self._M.extend(seq[4:6]) self._instr.append(instr) self._instr.append(["XC", instr[1], seq[6][2]]) self._instr.append(["ZC", instr[1], seq[7][2]]) Nnode += 2 elif instr[0] == "Y": ancilla = [Nnode, Nnode + 1, Nnode + 2, Nnode + 3] out[instr[1]], seq = self._y_command(out[instr[1]], ancilla) self._N.extend(seq[0:4]) self._E.extend(seq[4:8]) self._M.extend(seq[8:12]) self._instr.append(instr) self._instr.append(["XC", instr[1], seq[12][2]]) self._instr.append(["ZC", instr[1], seq[13][2]]) Nnode += 4 elif instr[0] == "Z": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._z_command(out[instr[1]], ancilla) self._N.extend(seq[0:2]) self._E.extend(seq[2:4]) self._M.extend(seq[4:6]) self._instr.append(instr) self._instr.append(["XC", instr[1], seq[6][2]]) self._instr.append(["ZC", instr[1], seq[7][2]]) Nnode += 2 elif instr[0] == "Rx": ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._rx_command(out[instr[1]], ancilla, instr[2]) self._N.extend(seq[0:2]) self._E.extend(seq[2:4]) self._M.extend(seq[4:6]) instr_ = deepcopy(instr) instr_.append(len(self._M) - 1) # index of arb angle measurement command self._instr.append(instr_) self._instr.append(["XC", instr[1], seq[6][2]]) self._instr.append(["ZC", instr[1], seq[7][2]]) Nnode += 2 elif instr[0] == "Ry": ancilla = [Nnode, Nnode + 1, Nnode + 2, Nnode + 3] out[instr[1]], seq = self._ry_command(out[instr[1]], ancilla, instr[2]) self._N.extend(seq[0:4]) self._E.extend(seq[4:8]) self._M.extend(seq[8:12]) instr_ = deepcopy(instr) instr_.append(len(self._M) - 3) # index of arb angle measurement command self._instr.append(instr_) self._instr.append(["XC", instr[1], seq[12][2]]) self._instr.append(["ZC", instr[1], seq[13][2]]) Nnode += 4 elif instr[0] == "Rz": if opt: ancilla = Nnode out[instr[1]], seq = self._rz_command_opt(out[instr[1]], ancilla, instr[2]) self._N.append(seq[0]) self._E.append(seq[1]) self._M.append(seq[2]) instr_ = deepcopy(instr) instr_.append(len(self._M) - 1) # index of arb angle measurement command self._instr.append(instr_) self._instr.append(["ZC", instr[1], seq[3][2]]) Nnode += 1 else: ancilla = [Nnode, Nnode + 1] out[instr[1]], seq = self._rz_command(out[instr[1]], ancilla, instr[2]) self._N.extend(seq[0:2]) self._E.extend(seq[2:4]) self._M.extend(seq[4:6]) instr_ = deepcopy(instr) instr_.append(len(self._M) - 2) # index of arb angle measurement command self._instr.append(instr_) self._instr.append(["XC", instr[1], seq[6][2]]) self._instr.append(["ZC", instr[1], seq[7][2]]) Nnode += 2 elif instr[0] == "Rzz": ancilla = Nnode out[instr[1][0]], out[instr[1][1]], seq = self._rzz_command_opt( out[instr[1][0]], out[instr[1][1]], ancilla, instr[2] ) self._N.append(seq[0]) self._E.extend(seq[1:3]) self._M.append(seq[3]) Nnode += 1 instr_ = deepcopy(instr) instr_.append(len(self._M) - 1) # index of arb angle measurement command self._instr.append(instr_) self._instr.append(["ZC", instr[1][1], seq[4][2]]) self._instr.append(["ZC", instr[1][0], seq[5][2]]) else: raise ValueError("Unknown instruction, commands not added") # move xc, zc to the end of the self._instr, so they will be applied last self._move_byproduct_to_right() # create command sequence command_seq = [] for cmd in self._N: command_seq.append(cmd) for cmd in reversed(self._E): command_seq.append(cmd) for cmd in self._M: command_seq.append(cmd) bpx_added = dict() bpz_added = dict() # byproduct command buffer z_cmds = [] x_cmds = [] for i in range(len(self._instr)): instr = self._instr[i] if instr[0] == "XC": if instr[1] in bpx_added.keys(): x_cmds[bpx_added[instr[1]]][2].extend(instr[2]) else: bpx_added[instr[1]] = len(x_cmds) x_cmds.append(["X", out[instr[1]], deepcopy(instr[2])]) elif instr[0] == "ZC": if instr[1] in bpz_added.keys(): z_cmds[bpz_added[instr[1]]][2].extend(instr[2]) else: bpz_added[instr[1]] = len(z_cmds) z_cmds.append(["Z", out[instr[1]], deepcopy(instr[2])]) # append z commands first (X and Z commute up to global phase) for cmd in z_cmds: command_seq.append(cmd) for cmd in x_cmds: command_seq.append(cmd) pattern = Pattern(self.width) pattern.output_nodes = out pattern.Nnode = Nnode pattern.seq = command_seq return pattern
def _commute_with_cnot(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "CNOT" if self._instr[target][0] == "XC" and self._instr[target][1] == self._instr[target + 1][1][0]: # control new_cmd = ["XC", self._instr[target + 1][1][1], self._instr[target][2]] self._commute_with_following(target) self._instr.insert(target + 1, new_cmd) return target + 1 elif self._instr[target][0] == "ZC" and self._instr[target][1] == self._instr[target + 1][1][1]: # target new_cmd = ["ZC", self._instr[target + 1][1][0], self._instr[target][2]] self._commute_with_following(target) self._instr.insert(target + 1, new_cmd) return target + 1 else: self._commute_with_following(target) return target def _commute_with_H(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "H" if self._instr[target][1] == self._instr[target + 1][1]: if self._instr[target][0] == "XC": self._instr[target][0] = "ZC" # byproduct changes to Z self._commute_with_following(target) else: self._instr[target][0] = "XC" # byproduct changes to X self._commute_with_following(target) else: self._commute_with_following(target) def _commute_with_S(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "S" if self._instr[target][1] == self._instr[target + 1][1]: if self._instr[target][0] == "XC": self._commute_with_following(target) # changes to Y = XZ self._instr.insert(target + 1, ["ZC", self._instr[target + 1][1], self._instr[target + 1][2]]) return target + 1 self._commute_with_following(target) return target def _commute_with_Rx(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "Rx" if self._instr[target][1] == self._instr[target + 1][1]: if self._instr[target][0] == "ZC": # add to the s-domain self._M[self._instr[target + 1][3]][4].extend(self._instr[target][2]) self._commute_with_following(target) else: self._commute_with_following(target) else: self._commute_with_following(target) def _commute_with_Ry(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "Ry" if self._instr[target][1] == self._instr[target + 1][1]: # add to the s-domain self._M[self._instr[target + 1][3]][4].extend(self._instr[target][2]) self._commute_with_following(target) else: self._commute_with_following(target) def _commute_with_Rz(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "Rz" if self._instr[target][1] == self._instr[target + 1][1]: if self._instr[target][0] == "XC": # add to the s-domain self._M[self._instr[target + 1][3]][4].extend(self._instr[target][2]) self._commute_with_following(target) else: self._commute_with_following(target) else: self._commute_with_following(target) def _commute_with_Rzz(self, target): assert self._instr[target][0] in ["XC", "ZC"] assert self._instr[target + 1][0] == "Rzz" if self._instr[target][0] == "XC": cond = self._instr[target][1] == self._instr[target + 1][1][0] cond2 = self._instr[target][1] == self._instr[target + 1][1][1] if cond or cond2: # add to the s-domain self._M[self._instr[target + 1][3]][4].extend(self._instr[target][2]) self._commute_with_following(target) def _commute_with_following(self, target): """Internal method to perform the commutation of two consecutive commands that commutes. commutes the target command with the following command. Parameters ---------- target : int target command index """ A = self._instr[target + 1] self._instr.pop(target + 1) self._instr.insert(target, A) def _find_byproduct_to_move(self, rev=False, skipnum=0): """Internal method for reordering commands Parameters ---------- rev : bool search from the end (true) or start (false) of seq skipnum : int skip the detected command by specified times """ if not rev: # search from the start target = 0 step = 1 else: # search from the back target = len(self._instr) - 1 step = -1 ite = 0 num_ops = 0 while ite < len(self._instr): if self._instr[target][0] in ["ZC", "XC"]: num_ops += 1 if num_ops == skipnum + 1: return target ite += 1 target += step target = "end" return target def _move_byproduct_to_right(self): """Internal method to move the byproduct 'gate' to the end of sequence, using the commutation relations""" moved = 0 # number of moved op target = self._find_byproduct_to_move(rev=True, skipnum=moved) while target != "end": if (target == len(self._instr) - 1) or (self._instr[target + 1][0] in ["XC", "ZC"]): moved += 1 target = self._find_byproduct_to_move(rev=True, skipnum=moved) continue if self._instr[target + 1][0] == "CNOT": target = self._commute_with_cnot(target) elif self._instr[target + 1][0] == "H": self._commute_with_H(target) elif self._instr[target + 1][0] == "S": target = self._commute_with_S(target) elif self._instr[target + 1][0] == "Rx": self._commute_with_Rx(target) elif self._instr[target + 1][0] == "Ry": self._commute_with_Ry(target) elif self._instr[target + 1][0] == "Rz": self._commute_with_Rz(target) elif self._instr[target + 1][0] == "Rzz": self._commute_with_Rzz(target) else: # Pauli gates commute up to global phase. self._commute_with_following(target) target += 1 @classmethod def _cnot_command(self, control_node, target_node, ancilla): """MBQC commands for CNOT gate Parameters --------- control_node : int control node on graph target : int target node on graph ancilla : list of two ints ancilla node indices to be added to graph Returns --------- control_out : int control node on graph after the gate target_out : int target node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [["N", ancilla[0]], ["N", ancilla[1]]] seq.append(["E", (target_node, ancilla[0])]) seq.append(["E", (control_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["M", target_node, "XY", 0, [], []]) seq.append(["M", ancilla[0], "XY", 0, [], []]) seq.append(["X", ancilla[1], [ancilla[0]]]) seq.append(["Z", ancilla[1], [target_node]]) seq.append(["Z", control_node, [target_node]]) return control_node, ancilla[1], seq @classmethod def _h_command(self, input_node, ancilla): """MBQC commands for Hadamard gate Parameters --------- input_node : int target node on graph ancilla : int ancilla node index to be added Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ seq = [["N", ancilla]] seq.append(["E", (input_node, ancilla)]) seq.append(["M", input_node, "XY", 0, [], []]) seq.append(["X", ancilla, [input_node]]) return ancilla, seq @classmethod def _s_command(self, input_node, ancilla): """MBQC commands for S gate Parameters --------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [["N", ancilla[0]], ["N", ancilla[1]]] seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["M", input_node, "XY", -0.5, [], []]) seq.append(["M", ancilla[0], "XY", 0, [], []]) seq.append(["X", ancilla[1], [ancilla[0]]]) seq.append(["Z", ancilla[1], [input_node]]) return ancilla[1], seq @classmethod def _x_command(self, input_node, ancilla): """MBQC commands for Pauli X gate Parameters --------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [["N", ancilla[0]], ["N", ancilla[1]]] seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["M", input_node, "XY", 0, [], []]) seq.append(["M", ancilla[0], "XY", -1, [], []]) seq.append(["X", ancilla[1], [ancilla[0]]]) seq.append(["Z", ancilla[1], [input_node]]) return ancilla[1], seq @classmethod def _y_command(self, input_node, ancilla): """MBQC commands for Pauli Y gate Parameters --------- input_node : int input node index ancilla : list of four ints ancilla node indices to be added to graph Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 4 seq = [["N", ancilla[0]], ["N", ancilla[1]]] # assign new qubit labels seq.extend([["N", ancilla[2]], ["N", ancilla[3]]]) seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["E", (ancilla[1], ancilla[2])]) seq.append(["E", (ancilla[2], ancilla[3])]) seq.append(["M", input_node, "XY", 0.5, [], []]) seq.append(["M", ancilla[0], "XY", 1.0, [input_node], []]) seq.append(["M", ancilla[1], "XY", -0.5, [input_node], []]) seq.append(["M", ancilla[2], "XY", 0, [], []]) seq.append(["X", ancilla[3], [ancilla[0], ancilla[2]]]) seq.append(["Z", ancilla[3], [ancilla[0], ancilla[1]]]) return ancilla[3], seq @classmethod def _z_command(self, input_node, ancilla): """MBQC commands for Pauli Z gate Parameters --------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [["N", ancilla[0]], ["N", ancilla[1]]] # assign new qubit labels seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["M", input_node, "XY", -1, [], []]) seq.append(["M", ancilla[0], "XY", 0, [], []]) seq.append(["X", ancilla[1], [ancilla[0]]]) seq.append(["Z", ancilla[1], [input_node]]) return ancilla[1], seq @classmethod def _rx_command(self, input_node, ancilla, angle): """MBQC commands for X rotation gate Parameters --------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph angle : float measurement angle in radian Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [["N", ancilla[0]], ["N", ancilla[1]]] # assign new qubit labels seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["M", input_node, "XY", 0, [], []]) seq.append(["M", ancilla[0], "XY", -1 * angle / np.pi, [input_node], []]) seq.append(["X", ancilla[1], [ancilla[0]]]) seq.append(["Z", ancilla[1], [input_node]]) return ancilla[1], seq @classmethod def _ry_command(self, input_node, ancilla, angle): """MBQC commands for Y rotation gate Parameters --------- input_node : int input node index ancilla : list of four ints ancilla node indices to be added to graph angle : float rotation angle in radian Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 4 seq = [["N", ancilla[0]], ["N", ancilla[1]]] # assign new qubit labels seq.extend([["N", ancilla[2]], ["N", ancilla[3]]]) seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["E", (ancilla[1], ancilla[2])]) seq.append(["E", (ancilla[2], ancilla[3])]) seq.append(["M", input_node, "XY", 0.5, [], []]) seq.append(["M", ancilla[0], "XY", -1 * angle / np.pi, [input_node], []]) seq.append(["M", ancilla[1], "XY", -0.5, [input_node], []]) seq.append(["M", ancilla[2], "XY", 0, [], []]) seq.append(["X", ancilla[3], [ancilla[0], ancilla[2]]]) seq.append(["Z", ancilla[3], [ancilla[0], ancilla[1]]]) return ancilla[3], seq @classmethod def _rz_command(self, input_node, ancilla, angle): """MBQC commands for Z rotation gate Parameters --------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph angle : float measurement angle in radian Returns --------- out_node : int node on graph after the gate commands : list list of MBQC commands """ assert len(ancilla) == 2 seq = [["N", ancilla[0]], ["N", ancilla[1]]] # assign new qubit labels seq.append(["E", (input_node, ancilla[0])]) seq.append(["E", (ancilla[0], ancilla[1])]) seq.append(["M", input_node, "XY", -1 * angle / np.pi, [], []]) seq.append(["M", ancilla[0], "XY", 0, [], []]) seq.append(["X", ancilla[1], [ancilla[0]]]) seq.append(["Z", ancilla[1], [input_node]]) return ancilla[1], seq @classmethod def _rz_command_opt(self, input_node, ancilla, angle): """optimized MBQC commands for Z rotation gate Parameters --------- input_node : int input node index ancilla : int ancilla node index to be added to graph angle : float measurement angle in radian Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ seq = [["N", ancilla]] # assign new qubit label seq.append(["E", (input_node, ancilla)]) seq.append(["M", ancilla, "XY", -angle / np.pi, [], [], 6]) seq.append(["Z", input_node, [ancilla]]) return input_node, seq @classmethod def _rzz_command_opt(self, control_node, target_node, ancilla, angle): """Optimized MBQC commands for ZZ-rotation gate Parameters --------- input_node : int input node index ancilla : int ancilla node index angle : float measurement angle in radian Returns --------- out_node_control : int control node on graph after the gate out_node_target : int target node on graph after the gate commands : list list of MBQC commands """ seq = [["N", ancilla]] # assign new qubit labels seq.append(["E", (control_node, ancilla)]) seq.append(["E", (target_node, ancilla)]) seq.append(["M", ancilla, "XY", -angle / np.pi, [], [], 6]) seq.append(["Z", control_node, [ancilla]]) seq.append(["Z", target_node, [ancilla]]) return control_node, target_node, seq @classmethod def _sort_outputs(self, pattern, output_nodes): """Sort the node indices of ouput qubits. Parameters --------- input_node : int input node index ancilla : list of two ints ancilla node indices to be added to graph angle : float measurement angle in radian Returns --------- out_node : int control node on graph after the gate commands : list list of MBQC commands """ old_out = deepcopy(output_nodes) output_nodes.sort() # check all commands and swap node indices for i in range(len(pattern.seq)): if pattern.seq[i][0] == "E": j, k = pattern.seq[i][1] if j in old_out: j = output_nodes[old_out.index(j)] if k in old_out: k = output_nodes[old_out.index(k)] pattern.seq[i][1] = (j, k) elif pattern.seq[i][1] in old_out: pattern.seq[i][1] = output_nodes[old_out.index(pattern.seq[i][1])]
[docs] def simulate_statevector(self, input_state=None): """Run statevector simultion of the gate sequence, using graphix.Statevec Returns ------- stete : graphix.Statevec output state of the statevector simulation. """ if input_state is None: state = Statevec(nqubit=self.width) else: state = input_state for i in range(len(self.instruction)): if self.instruction[i][0] == "CNOT": state.CNOT((self.instruction[i][1][0], self.instruction[i][1][1])) elif self.instruction[i][0] == "I": pass elif self.instruction[i][0] == "S": state.evolve_single(Ops.s, self.instruction[i][1]) elif self.instruction[i][0] == "H": state.evolve_single(Ops.h, self.instruction[i][1]) elif self.instruction[i][0] == "X": state.evolve_single(Ops.x, self.instruction[i][1]) elif self.instruction[i][0] == "Y": state.evolve_single(Ops.y, self.instruction[i][1]) elif self.instruction[i][0] == "Z": state.evolve_single(Ops.z, self.instruction[i][1]) elif self.instruction[i][0] == "Rx": state.evolve_single(Ops.Rx(self.instruction[i][2]), self.instruction[i][1]) elif self.instruction[i][0] == "Ry": state.evolve_single(Ops.Ry(self.instruction[i][2]), self.instruction[i][1]) elif self.instruction[i][0] == "Rz": state.evolve_single(Ops.Rz(self.instruction[i][2]), self.instruction[i][1]) elif self.instruction[i][0] == "Rzz": state.evolve(Ops.Rzz(self.instruction[i][2]), [self.instruction[i][1][0], self.instruction[i][1][1]]) return state