def my_transpile(old_circuit): new_circuit = QuantumCircuit(*old_circuit.qregs, *old_circuit.cregs, name=old_circuit.name + '_transpiled') for inst, qargs, cargs in old_circuit._data: # print(inst.name) if inst.name == "id": # print("my_I removes id") my_I(new_circuit, qargs) elif inst.name == "x": # print("my_x used") my_x(new_circuit, qargs) elif inst.name == "y": # print("my_y used") my_y(new_circuit, qargs) elif inst.name == "z": # print("my_z used") my_z(new_circuit, qargs) elif inst.name == "h": # print("my_h used") my_h(new_circuit, qargs) elif inst.name == "cx": # print("my_cx used") my_cx(new_circuit, *qargs) elif inst.name == "ry": # print("my_ry used") my_ry(new_circuit, qargs) elif inst.name == "barrier": # print("barrier kept") new_circuit._append(inst, qargs, cargs) else: new_circuit._append(inst, qargs, cargs) return new_circuit
def clifford_2_qubit_circuit(self, num): """Return the 2-qubit clifford circuit corresponding to `num` where `num` is between 0 and 11519. """ vals = self._unpack_num_multi_sigs(num, self.CLIFFORD_2_QUBIT_SIGS) qr = QuantumRegister(2) qc = QuantumCircuit(qr) if vals[0] == 0 or vals[0] == 3: (form, i0, i1, j0, j1, p0, p1) = vals else: (form, i0, i1, j0, j1, k0, k1, p0, p1) = vals if i0 == 1: qc.h(0) if i1 == 1: qc.h(1) if j0 == 1: qc.sxdg(0) if j0 == 2: qc.s(0) if j1 == 1: qc.sxdg(1) if j1 == 2: qc.s(1) if form in (1, 2, 3): qc.cx(0, 1) if form in (2, 3): qc.cx(1, 0) if form == 3: qc.cx(0, 1) if form in (1, 2): if k0 == 1: qc._append(VGate(), [qr[0]], []) if k0 == 2: qc._append(WGate(), [qr[0]], []) if k1 == 1: qc._append(VGate(), [qr[1]], []) if k1 == 2: qc._append(VGate(), [qr[1]], []) qc._append(VGate(), [qr[1]], []) if p0 == 1: qc.x(0) if p0 == 2: qc.y(0) if p0 == 3: qc.z(0) if p1 == 1: qc.x(1) if p1 == 2: qc.y(1) if p1 == 3: qc.z(1) return qc
def clifford_1_qubit_circuit(self, num): """Return the 1-qubit clifford circuit corresponding to `num` where `num` is between 0 and 23. """ # pylint: disable=unbalanced-tuple-unpacking # This is safe since `_unpack_num` returns list the size of the sig (i, j, p) = self._unpack_num(num, self.CLIFFORD_1_QUBIT_SIG) qr = QuantumRegister(1) qc = QuantumCircuit(qr) if i == 1: qc.h(0) if j == 1: qc._append(SXdgGate(), [qr[0]], []) if j == 2: qc._append(SGate(), [qr[0]], []) if p == 1: qc.x(0) if p == 2: qc.y(0) if p == 3: qc.z(0) return qc
def my_transpile_optimized(old_circuit): """ Implements following optimizations: 1. Skip I 2. Deletes RX, RX, RX, RX on same qubit 3. Deletes RZ, RZ, RZ, RZ on same qubit 4. Deletes CZ, CZ on same qubits 5. Deletes CX, CX on same qubits 6. Deletes H, H on same qubit 7. Deletes X, X on same qubit 8. Deletes Y, Y on same qubit More identities could probably used to optimized further... """ optimized_circuit = QuantumCircuit(*old_circuit.qregs, *old_circuit.cregs, name=old_circuit.name + '_optimized') optimized_circuit_new_set = QuantumCircuit(*old_circuit.qregs, *old_circuit.cregs, name=old_circuit.name + '_optimized_new_set') # Implement rules 5, 6, 7, 8, as these are on the old set # list counts consecutively gates by number of elements for every qubit # (qubitwise because gates on different qubits do not cancel: # e.g: q1: Rx Rx, q2: Rx Rx, do not cancel cx_list = [[] for i in old_circuit._qubits] h_list = [[] for i in old_circuit._qubits] x_list = [[] for i in old_circuit._qubits] y_list = [[] for i in old_circuit._qubits] delete_list = [[] for i in old_circuit._qubits] # gate index i = 0 # counts deleted rx, rz, cz gates number_deleted_gates_new_set = 0 for inst, qargs, cargs in old_circuit._data: print(inst, qargs) print("cx_list:", cx_list) # print("qargs:", qargs[0].index) if inst.name == "h": # clear other gates list as its not consecutive anymore cx_list = [[] for i in old_circuit._qubits] x_list = [[] for i in old_circuit._qubits] y_list = [[] for i in old_circuit._qubits] h_list[qargs[0].index].append(i) elif inst.name == "x": # clear other gates list as its not consecutive anymore cx_list = [[] for i in old_circuit._qubits] h_list = [[] for i in old_circuit._qubits] y_list = [[] for i in old_circuit._qubits] x_list[qargs[0].index].append(i) elif inst.name == "y": # clear other gates list as its not consecutive anymore cx_list = [[] for i in old_circuit._qubits] h_list = [[] for i in old_circuit._qubits] x_list = [[] for i in old_circuit._qubits] y_list[qargs[0].index].append(i) elif inst.name == "z": # WARNING: No z_list as Z Z will be removed trough step 2 # clear other gates list as its not consecutive anymore cx_list = [[] for i in old_circuit._qubits] h_list = [[] for i in old_circuit._qubits] x_list = [[] for i in old_circuit._qubits] y_list = [[] for i in old_circuit._qubits] # id does not change the unitary, so does not break a sequence # elif inst.name == "i": # # WARNING: No z_list as Z Z will be removed trough step 2 # # clear other gates list as its not consecutive anymore # cx_list = [[] for i in old_circuit._qubits] # h_list = [[] for i in old_circuit._qubits] # x_list = [[] for i in old_circuit._qubits] # y_list = [[] for i in old_circuit._qubits] elif inst.name == "cx": # clear other gates list as its not consecutive anymore h_list = [[] for i in old_circuit._qubits] x_list = [[] for i in old_circuit._qubits] y_list = [[] for i in old_circuit._qubits] # As CZ is a symmetric qubit gate both cz(0, 1) # and cz(1, 0) need to be count cx_list[qargs[0].index].append(i) cx_list[qargs[1].index].append(i) # Rule 5: delete if 2 consecutive Cx if len(cx_list[qargs[0].index]) == 2: delete_list[qargs[0].index].append(cx_list[qargs[0].index]) number_deleted_gates_new_set += 2 * CXGATES # reset to empty list for that qubit cx_list = [[] for i in old_circuit._qubits] # Rule 6: delete if 2 consecutive H if len(h_list[qargs[0].index]) == 2: delete_list[qargs[0].index].append(h_list[qargs[0].index]) number_deleted_gates_new_set += 2 * HGATES # reset to empty list for that qubit h_list = [[] for i in old_circuit._qubits] # Rule 7: delete if 2 consecutive x if len(x_list[qargs[0].index]) == 2: delete_list[qargs[0].index].append(x_list[qargs[0].index]) number_deleted_gates_new_set += 2 * XGATES # reset to empty list for that qubit x_list = [[] for i in old_circuit._qubits] # Rule 8: delete if 2 consecutive y if len(y_list[qargs[0].index]) == 2: delete_list[qargs[0].index].append(y_list[qargs[0].index]) number_deleted_gates_new_set += 2 * YGATES # reset to empty list for that qubit y_list = [[] for i in old_circuit._qubits] i += 1 # flatten the lists for different qubits for x, _ in enumerate(old_circuit._qubits): delete_list[x] = [element for line in delete_list[x] \ for element in line] print("delete[" + str(x) + "]: ", delete_list[x]) # flatten the lists for all qubits, as seperation is not needed anymore delete_list_flatten = [element for line in delete_list \ for element in line] print("delete_list_flatten: ", delete_list_flatten) counter = 0 for inst, qargs, cargs in old_circuit._data: # print(inst.name) # if gate should be deleted skip it # (do not insert it into optimized circ) if counter in delete_list_flatten: pass elif inst.name == "id": print("my_I removes id") # my_I(optimized_circuit, qargs) elif inst.name == "x": # print("my_x used") optimized_circuit.x(qargs) elif inst.name == "y": # print("my_y used") optimized_circuit.y(qargs) elif inst.name == "z": # print("my_z used") optimized_circuit.z(qargs) elif inst.name == "h": # print("my_h used") optimized_circuit.h(qargs) elif inst.name == "cx": # print("my_cx used") optimized_circuit.cx(*qargs) elif inst.name == "ry": # print("my_ry used") optimized_circuit.ry(np.pi / 2, qargs) elif inst.name == "barrier": # print("barrier kept") optimized_circuit._append(inst, qargs, cargs) else: optimized_circuit._append(inst, qargs, cargs) counter += 1 number_deleted_gates = sum([len(qubit_list) \ for qubit_list in delete_list]) print("Optimization deleted " + str(number_deleted_gates) + " gates in first step (gates from old basis set)" + " and " + str(number_deleted_gates_new_set) + " gates in terms of new set.") # Implement rules 1, 2, 3, 4, as these are on the new set # list counts consecutively gates by number of elements for every qubit # (qubitwise because gates on different qubits do not cancel: # e.g: q1: Rx Rx, q2: Rx Rx, do not cancel rx_list = [[] for i in old_circuit._qubits] rz_list = [[] for i in old_circuit._qubits] cz_list = [[] for i in old_circuit._qubits] delete_list = [[] for i in old_circuit._qubits] # gate index i = 0 for inst, qargs, cargs in old_circuit._data: # print(inst, qargs) # print("qargs:", qargs[0].index) if inst.name == "rx": # clear other gates list as its not consecutive anymore rz_list = [[] for i in old_circuit._qubits] cz_list = [[] for i in old_circuit._qubits] rx_list[qargs[0].index].append(i) elif inst.name == "rz": # clear other gates list as its not consecutive anymore rx_list = [[] for i in old_circuit._qubits] cz_list = [[] for i in old_circuit._qubits] rz_list[qargs[0].index].append(i) elif inst.name == "cz": # clear other gates list as its not consecutive anymore rx_list = [[] for i in old_circuit._qubits] rz_list = [[] for i in old_circuit._qubits] # As CZ is a symmetric qubit gate both cz(0, 1) # and cz(1, 0) need to be count cz_list[qargs[0].index].append(i) cz_list[qargs[1].index].append(i) # Rule 2: delete if 4 consecutive Rx if len(rx_list[qargs[0].index]) == 4: delete_list[qargs[0].index].append(rx_list[qargs[0].index]) # reset to empty list for that qubit rx_list = [[] for i in old_circuit._qubits] # Rule 3: delete if 4 consecutive Rz if len(rz_list[qargs[0].index]) == 4: delete_list[qargs[0].index].append(rz_list[qargs[0].index]) # reset to empty list for that qubit rz_list = [[] for i in old_circuit._qubits] # Rule 4: delete if 2 consecutive Cz if len(cz_list[qargs[0].index]) == 2: delete_list[qargs[0].index].append(cz_list[qargs[0].index]) # reset to empty list for that qubit cz_list = [[] for i in old_circuit._qubits] i += 1 # flatten the lists for different qubits for x, _ in enumerate(old_circuit._qubits): delete_list[x] = [element for line in delete_list[x] \ for element in line] print("delete[" + str(x) + "]: ", delete_list[x]) # flatten the lists for all qubits, as seperation is not needed anymore delete_list_flatten = [element for line in delete_list \ for element in line] print("delete_list_flatten: ", delete_list_flatten) counter = 0 for inst, qargs, cargs in optimized_circuit._data: # print(inst.name) # if gate should be deleted skip it # (do not insert it into optimized circ) if counter in delete_list_flatten: pass elif inst.name == "id": print("my_I removes id") # my_I(optimized_circuit_new_set, qargs) elif inst.name == "x": # print("my_x used") my_x(optimized_circuit_new_set, qargs) elif inst.name == "y": # print("my_y used") my_y(optimized_circuit_new_set, qargs) elif inst.name == "z": # print("my_z used") my_z(optimized_circuit_new_set, qargs) elif inst.name == "h": # print("my_h used") my_h(optimized_circuit_new_set, qargs) elif inst.name == "cx": # print("my_cx used") my_cx(optimized_circuit_new_set, *qargs) elif inst.name == "ry": # print("my_ry used") my_ry(optimized_circuit_new_set, qargs) elif inst.name == "barrier": # print("barrier kept") optimized_circuit_new_set._append(inst, qargs, cargs) else: optimized_circuit_new_set._append(inst, qargs, cargs) counter += 1 number_deleted_gates = sum([len(qubit_list) \ for qubit_list in delete_list]) print("Optimization deleted " + str(number_deleted_gates) + " gates in second step (gates from new basis set).") return optimized_circuit_new_set
def circuits(self): sub_circuits = [] sub_qubits = [] sub_maps = [] sub_size = [] num_qubits = 0 # Generate data for combination for sub_exp in self._experiments: # Generate transpiled subcircuits circuits = sub_exp._transpiled_circuits() # Add subcircuits sub_circuits.append(circuits) sub_size.append(len(circuits)) # Sub experiment logical qubits in the combined circuits full qubits qubits = list(range(num_qubits, num_qubits + sub_exp.num_qubits)) sub_qubits.append(qubits) # Construct mapping for the sub-experiments logical qubits to physical qubits # in the full combined circuits sub_maps.append( {q: qubits[i] for i, q in enumerate(sub_exp.physical_qubits)}) num_qubits += sub_exp.num_qubits # Generate empty joint circuits num_circuits = max(sub_size) joint_circuits = [] for circ_idx in range(num_circuits): # Create joint circuit circuit = QuantumCircuit(self.num_qubits, name=f"parallel_exp_{circ_idx}") circuit.metadata = { "experiment_type": self._type, "composite_index": [], "composite_metadata": [], "composite_qubits": [], "composite_clbits": [], } for exp_idx in range(self._num_experiments): if circ_idx < sub_size[exp_idx]: # Add subcircuits to joint circuit sub_circ = sub_circuits[exp_idx][circ_idx] num_clbits = circuit.num_clbits qubits = sub_qubits[exp_idx] qargs_map = sub_maps[exp_idx] clbits = list( range(num_clbits, num_clbits + sub_circ.num_clbits)) circuit.add_register(ClassicalRegister( sub_circ.num_clbits)) # Apply transpiled subcircuit # Note that this assumes the circuit was not expanded to use # any qubits outside the specified physical qubits for inst, qargs, cargs in sub_circ.data: try: mapped_qargs = [ circuit.qubits[qargs_map[sub_circ.find_bit( i).index]] for i in qargs ] except KeyError as ex: # Instruction is outside physical qubits for the component # experiment. # This could legitimately happen if the subcircuit was # explicitly scheduled during transpilation which would # insert delays on all auxillary device qubits. # We skip delay instructions to allow for this. if inst.name == "delay": continue raise QiskitError( "Component experiment has been transpiled outside of the " "allowed physical qubits for that component. Check the " "experiment is valid on the backends coupling map." ) from ex mapped_cargs = [ circuit.clbits[clbits[sub_circ.find_bit(i).index]] for i in cargs ] circuit._append(inst, mapped_qargs, mapped_cargs) # Add subcircuit metadata circuit.metadata["composite_index"].append(exp_idx) circuit.metadata["composite_metadata"].append( sub_circ.metadata) circuit.metadata["composite_qubits"].append(qubits) circuit.metadata["composite_clbits"].append(clbits) # Add the calibrations for gate, cals in sub_circ.calibrations.items(): for key, sched in cals.items(): circuit.add_calibration(gate, qubits=key[0], schedule=sched, params=key[1]) # Add joint circuit to returned list joint_circuits.append(circuit) return joint_circuits