def iterated_entanglement_swap(n_iter): # Iterate the entanglement swapping protocol n_iter times it_es = Circuit() ava = it_es.add_q_register("a", 1) bella = it_es.add_q_register("b", 2) charlie = it_es.add_q_register("c", 1) data = it_es.add_c_register("d", 2) # Start with an initial Bell state it_es.H(ava[0]) it_es.CX(ava[0], bella[0]) for i in range(n_iter): if i % 2 == 0: # Teleport bella[0] to charlie[0] to give a Bell pair between ava[0] and charlier[0] tel_to_c = qtel.copy() tel_to_c.rename_units( {alice[0]: bella[0], alice[1]: bella[1], bob[0]: charlie[0]} ) it_es.append(tel_to_c) it_es.add_gate(OpType.Reset, [bella[0]]) it_es.add_gate(OpType.Reset, [bella[1]]) else: # Teleport charlie[0] to bella[0] to give a Bell pair between ava[0] and bella[0] tel_to_b = qtel.copy() tel_to_b.rename_units( {alice[0]: charlie[0], alice[1]: bella[1], bob[0]: bella[0]} ) it_es.append(tel_to_b) it_es.add_gate(OpType.Reset, [bella[1]]) it_es.add_gate(OpType.Reset, [charlie[0]]) # Return the circuit and the qubits expected to share a Bell pair if n_iter % 2 == 0: return it_es, [ava[0], bella[0]] else: return it_es, [ava[0], charlie[0]]
class QASMParser(object): """Class for parsing OpenQASM files into CQC t|ket> Circuits.""" def __init__(self): self.circuit = Circuit() self.register_dict = dict() def parse_qasm(self, qasm): lines = qasm.splitlines() rows = [] #first, get rid of comments and whitespace lines for l in lines: i = l.find("//") if i != -1: s = l[0:i].strip() else: s = l.strip() if s: rows.append(s) #now, throw away OPENQASM descriptor etc. if not (rows[0].startswith("OPENQASM 2.0") and rows[1].startswith('include "qelib1.inc";')): raise TypeError( "File must declare OPENQASM version and its includes.") data = "\n".join(rows[2:]) #now, separate out the custom gates to deal with elsewhere while True: i = data.find("gate ") if i == -1: break j = data.find("}", i) if j == -1: raise TypeError("Custom gate definition is invalid.") self.parse_custom_gate(data[i:j + 1]) #TODO: deal with custom gate data = data[:i] + data[j + 1:] #now, parse the regular instructions instructions = [s.strip() for s in data.split(";") if s.strip()] for i in instructions: self.parse_instruction(i) return self.circuit def parse_custom_gate(self, data): raise TypeError("Cannot currently parse custom gates") def parse_instruction(self, instruction): if instruction.find("->") != -1: ###handle measure gates ###currently assumes that there is just 1 qb being read to 1 bit name_and_qbs, bits = instruction.split("->", 1) if (name_and_qbs.find("measure") == -1): raise Exception( "Error in parsing: cannot accept a non-Measure gate writing to classical register" ) name_and_qbs = name_and_qbs.replace("measure", "") name_and_qbs = name_and_qbs.replace(" ", "") name_and_qbs.strip() qregname, qbindex = name_and_qbs.split("[") qbindex = int(qbindex[0]) qubit = self.circuit.q_regs[qregname][qbindex] bits = bits.replace(" ", "") bitreg, bitindex = bits.split("[") bitindex = int(bitindex[0]) bit = self.circuit.c_regs[bitreg][bitindex] self.circuit.add_measure(qubit, bit) return if instruction.find("(") != -1: name, rest = instruction.split(") ", 1) name = name.replace(" ", "") else: name, rest = instruction.split(" ", 1) args = [s.strip() for s in rest.split(",") if s.strip()] #deal with qubit register declarations if name == "qreg" or name == "creg": regname, size = args[0].split("[", 1) regname.strip() size = int(size[:-1]) if name == "qreg": self.circuit.add_q_register(regname, size) else: self.circuit.add_c_register(regname, size) return #get qubits to append operation to qubits = [] for a in args: if "[" in a: regname, val = a.split("[", 1) val = int(val[:-1]) qubits.append(self.circuit.q_regs[regname][val]) else: raise Exception( "Unknown error in parsing: Cannot parse argument {}". format(a)) #if the gate is parameterised, get these parameters if name.find("(") != -1: name, params = name.split("(", 1) if name in PARAM_COMMANDS: angles = [s.strip() for s in params.split(",") if s.strip()] halfturn_angles = [] for ang in angles: ang = ang.replace("pi", str(math.pi)) try: halfturns = sympify(ang) / math.pi halfturn_angles.append(halfturns) except: raise TypeError("Cannot parse angle: {}".format(ang)) self.circuit.add_gate(PARAM_COMMANDS[name], halfturn_angles, qubits, []) else: raise TypeError("Cannot parse gate of type: {}".format(name)) else: if name in NOPARAM_COMMANDS: self.circuit.add_gate(NOPARAM_COMMANDS[name], [], qubits, []) else: raise TypeError("Cannot parse gate of type: {}".format(name))
# `measure a[0] -> c[0];` # `measure a[1] -> c[1];` # `// Correction of Bob's qubit` # `if(c==1) z b[0];` # `if(c==3) z b[0];` # `if(c==2) x b[0];` # `if(c==3) x b[0];` # # This corresponds to the following `pytket` code: from pytket import Circuit qtel = Circuit() alice = qtel.add_q_register("a", 2) bob = qtel.add_q_register("b", 1) data = qtel.add_c_register("d", 2) # Bell state between Alice and Bob: qtel.H(alice[1]) qtel.CX(alice[1], bob[0]) # Bell measurement of Alice's qubits: qtel.CX(alice[0], alice[1]) qtel.H(alice[0]) qtel.Measure(alice[0], data[0]) qtel.Measure(alice[1], data[1]) # Correction of Bob's qubit:
# - can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation. # Useful features: # - support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s. from pytket import Circuit from pytket.extensions.qiskit import AerBackend from itertools import combinations from qiskit.providers.aer.noise import NoiseModel, depolarizing_error # Quantum teleportation circuit: c = Circuit() alice = c.add_q_register("a", 2) bob = c.add_q_register("b", 1) data = c.add_c_register("d", 2) final = c.add_c_register("f", 1) # Start in an interesting state: c.Rx(0.3, alice[0]) # Set up a Bell state between Alice and Bob: c.H(alice[1]).CX(alice[1], bob[0]) # Measure Alice's qubits in the Bell basis: c.CX(alice[0], alice[1]).H(alice[0]) c.Measure(alice[0], data[0]) c.Measure(alice[1], data[1])