def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [ DecomposeBoxes(), FlattenRegisters(), ] if optimisation_level == 1: passlist.append(SynthesiseIBM()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) passlist.append( CXMappingPass( self._device, NoiseAwarePlacement(self._device), directed_cx=False, delay_measures=True, )) if optimisation_level == 2: passlist.append(CliffordSimp(False)) if optimisation_level > 0: passlist.append(SynthesiseIBM()) passlist.append(RebaseQuil()) if optimisation_level > 0: passlist.append(EulerAngleReduction(OpType.Rx, OpType.Rz)) return SequencePass(passlist)
def run(d,fname): f = open(fname, "w") f.write("name,1q,2q,total,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) circ = circuit_from_qasm(os.path.join(d, fname)) # Other useful optimizations include OptimisePhaseGadgets and PauliSimp. # We exclude them here because they make performance worse on our benchmarks. seq_pass=SequencePass([FullPeepholeOptimise(), RemoveRedundancies()]) start = time.perf_counter() seq_pass.apply(circ) stop = time.perf_counter() total_count = circ.n_gates two_count = circ.n_gates_of_type(OpType.CX) one_count = total_count - two_count # note: could potentially convert to other gate sets here with RebaseCustom print("\t Total %d, 1q %d, CNOT %d\n" % (total_count, one_count, two_count)) f.write("%s,%d,%d,%d,%f\n" % (fname, one_count, two_count, total_count, stop - start)) f.close()
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass([ DecomposeBoxes(), FlattenRegisters(), RenameQubitsPass(self._qm), ionq_pass, ]) elif optimisation_level == 1: return SequencePass([ DecomposeBoxes(), SynthesiseIBM(), FlattenRegisters(), RenameQubitsPass(self._qm), ionq_pass, ]) else: return SequencePass([ DecomposeBoxes(), FullPeepholeOptimise(), FlattenRegisters(), RenameQubitsPass(self._qm), ionq_pass, SquashCustom( ionq_singleqs, lambda a, b, c: Circuit(1).Rz(c, 0).Rx(b, 0).Rz(a, 0), ), ])
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass([ FlattenRegisters(), RenameQubitsPass(self._qm), DecomposeBoxes(), _aqt_rebase(), ]) elif optimisation_level == 1: return SequencePass([ DecomposeBoxes(), SynthesiseIBM(), FlattenRegisters(), RenameQubitsPass(self._qm), _aqt_rebase(), RemoveRedundancies(), EulerAngleReduction(OpType.Ry, OpType.Rx), ]) else: return SequencePass([ DecomposeBoxes(), FullPeepholeOptimise(), FlattenRegisters(), RenameQubitsPass(self._qm), _aqt_rebase(), RemoveRedundancies(), EulerAngleReduction(OpType.Ry, OpType.Rx), ])
def objective(params): circ = ucc(params) PauliSimp().apply(circ) FullPeepholeOptimise().apply(circ) return (get_operator_expectation_value( circ, hamiltonian_op, backend, n_shots=4000) + nuclear_repulsion_energy)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passes = [DecomposeBoxes()] if optimisation_level == 1: passes.append(SynthesiseIBM()) elif optimisation_level == 2: passes.append(FullPeepholeOptimise()) passes.append(self._rebase_pass) if self._device_type == _DeviceType.QPU: passes.append( CXMappingPass( self._tket_device, NoiseAwarePlacement(self._tket_device), directed_cx=False, delay_measures=True, )) # If CX weren't supported by the device then we'd need to do another # rebase_pass here. But we checked above that it is. if optimisation_level == 1: passes.extend([RemoveRedundancies(), self._squash_pass]) if optimisation_level == 2: passes.extend([ CliffordSimp(False), SynthesiseIBM(), self._rebase_pass, self._squash_pass, ]) return SequencePass(passes)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [DecomposeBoxes()] if optimisation_level == 0: passlist.append(self._rebase_pass) elif optimisation_level == 1: passlist.append(SynthesiseIBM()) else: passlist.append(FullPeepholeOptimise()) if self._noise_model and self._device: passlist.append( CXMappingPass( self._device, NoiseAwarePlacement(self._device), directed_cx=True, delay_measures=False, )) if optimisation_level == 0: passlist.append(self._rebase_pass) elif optimisation_level == 1: passlist.append(SynthesiseIBM()) else: passlist.extend([CliffordSimp(False), SynthesiseIBM()]) return SequencePass(passlist)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass( [DecomposeClassicalExp(), DecomposeBoxes(), RebaseHQS()]) elif optimisation_level == 1: return SequencePass([ DecomposeClassicalExp(), DecomposeBoxes(), SynthesiseIBM(), RebaseHQS(), RemoveRedundancies(), SquashHQS(), ]) else: return SequencePass([ DecomposeClassicalExp(), DecomposeBoxes(), FullPeepholeOptimise(), RebaseHQS(), RemoveRedundancies(), SquashHQS(), ])
def run(d, fname): f = open(fname, "w") f.write("name,1q,2q,total,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) circ = circuit_from_qasm(os.path.join(d, fname)) start = time.perf_counter() FullPeepholeOptimise().apply(circ) stop = time.perf_counter() total_count = circ.n_gates two_count = circ.n_gates_of_type(OpType.CX) one_count = total_count - two_count print("\t Total %d, 1q %d, CNOT %d\n" % (total_count, one_count, two_count)) f.write("%s,%d,%d,%d,%f\n" % (fname, one_count, two_count, total_count, stop - start)) f.close()
def ucc(params): singles_params = {qps: params[0] * coeff for qps, coeff in singles.items()} doubles_params = {qps: params[1] * coeff for qps, coeff in doubles.items()} excitation_op = QubitPauliOperator({**singles_params, **doubles_params}) reference_circ = Circuit(4).X(1).X(3) ansatz = gen_term_sequence_circuit(excitation_op, reference_circ) GuidedPauliSimp().apply(ansatz) FullPeepholeOptimise().apply(ansatz) return ansatz
def test_tketpass() -> None: qc = get_test_circuit(False, False) tkpass = FullPeepholeOptimise() back = Aer.get_backend("unitary_simulator") for _ in range(12): tkc = qiskit_to_tk(qc) print(tkc.phase) tkpass.apply(tkc) print(tkc.phase) qc1 = tk_to_qiskit(tkc) res = execute(qc1, back).result() u1 = res.get_unitary(qc1) qispass = TketPass(tkpass) pm = PassManager(qispass) qc2 = pm.run(qc) res = execute(qc2, back).result() u2 = res.get_unitary(qc2) assert np.allclose(u1, u2)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) if optimisation_level == 0: return SequencePass([DecomposeBoxes(), RebaseIBM()]) elif optimisation_level == 1: return SequencePass([DecomposeBoxes(), SynthesiseIBM()]) else: return SequencePass([DecomposeBoxes(), FullPeepholeOptimise()])
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [DecomposeBoxes(), FlattenRegisters()] if optimisation_level == 1: passlist.append(SynthesiseIBM()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) passlist.append(RebaseQuil()) if optimisation_level > 0: passlist.append(EulerAngleReduction(OpType.Rx, OpType.Rz)) return SequencePass(passlist)
def _optimize(self, c): c_tket = pyzx_to_tk(c) FullPeepholeOptimise().apply(c_tket) # FIXME: Failing equality! # RebasePyZX().apply(c_tket) # c_opt = tk_to_pyzx(c_tket) # FIXME: Failing equality! qasm_str = circuit_to_qasm_str(c_tket) c_opt = zx.Circuit.from_qasm(qasm_str) return c_opt
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: """ A suggested compilation pass that will guarantee the resulting circuit will be suitable to run on this backend with as few preconditions as possible. :param optimisation_level: The level of optimisation to perform during compilation. Level 0 just solves the device constraints without optimising. Level 1 additionally performs some light optimisations. Level 2 adds more intensive optimisations that can increase compilation time for large circuits. Defaults to 1. :type optimisation_level: int, optional :return: Compilation pass guaranteeing required predicates. :rtype: BasePass """ assert optimisation_level in range(3) cx_circ = Circuit(2) cx_circ.Sdg(0) cx_circ.V(1) cx_circ.Sdg(1) cx_circ.Vdg(1) cx_circ.add_gate(OpType.ZZMax, [0, 1]) cx_circ.Vdg(1) cx_circ.Sdg(1) cx_circ.add_phase(0.5) def sq(a, b, c): circ = Circuit(1) if c != 0: circ.Rz(c, 0) if b != 0: circ.Rx(b, 0) if a != 0: circ.Rz(a, 0) return circ rebase = RebaseCustom({OpType.ZZMax}, cx_circ, {OpType.Rx, OpType.Ry, OpType.Rz}, sq) squash = SquashCustom({OpType.Rz, OpType.Rx, OpType.Ry}, sq) seq = [DecomposeBoxes()] # Decompose boxes into basic gates if optimisation_level == 1: seq.append(SynthesiseIBM()) # Optional fast optimisation elif optimisation_level == 2: seq.append(FullPeepholeOptimise()) # Optional heavy optimisation seq.append(rebase) # Map to target gate set if optimisation_level != 0: seq.append( squash) # Optionally simplify 1qb gate chains within this gate set return SequencePass(seq)
def default_compilation_pass(self, optimisation_level: int = 1) -> BasePass: assert optimisation_level in range(3) passlist = [DecomposeBoxes()] if optimisation_level == 0: passlist.append(self._rebase_pass) elif optimisation_level == 1: passlist.append(SynthesiseIBM()) elif optimisation_level == 2: passlist.append(FullPeepholeOptimise()) passlist.append( CXMappingPass( self._device, NoiseAwarePlacement(self._device), directed_cx=False, delay_measures=(not self._mid_measure), )) if optimisation_level == 1: passlist.append(SynthesiseIBM()) if optimisation_level == 2: passlist.extend([CliffordSimp(False), SynthesiseIBM()]) if not self._legacy_gateset: passlist.extend([self._rebase_pass, RemoveRedundancies()]) return SequencePass(passlist)
def run(d, fname): f = open(fname, "w") f.write("name,Orig. total,Orig. CNOT,tket total,tket CNOT,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) # same hack as the one used in run_qiskit.py inqasm = open(os.path.join(d, fname), "r") tmp = open("copy.qasm", "w") # hardcoded filename p_ccz = re.compile("ccz (.*), (.*), (.*);") p_ccx = re.compile("ccx (.*), (.*), (.*);") for line in inqasm: m1 = p_ccx.match(line) m2 = p_ccz.match(line) if m1: a = m1.group(1) b = m1.group(2) c = m1.group(3) tmp.write("h %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("t %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("tdg %s;\n" % (b)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("t %s;\n" % (a)) tmp.write("t %s;\n" % (b)) tmp.write("t %s;\n" % (c)) tmp.write("h %s;\n" % (c)) elif m2: a = m2.group(1) b = m2.group(2) c = m2.group(3) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("t %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("tdg %s;\n" % (b)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("t %s;\n" % (a)) tmp.write("t %s;\n" % (b)) tmp.write("t %s;\n" % (c)) else: tmp.write(line) tmp.close() circ = circuit_from_qasm("copy.qasm") num_gates_before = circ.n_gates num_CNOTs_before = circ.n_gates_of_type(OpType.CX) print("Original:\t Total %d, CNOT %d" % (num_gates_before, num_CNOTs_before)) start = time.perf_counter() # start timer FullPeepholeOptimise().apply(circ) stop = time.perf_counter() # stop timer num_gates_after = circ.n_gates num_CNOTs_after = circ.n_gates_of_type(OpType.CX) print("Final:\t Total %d, CNOT %d\n" % (num_gates_after, num_CNOTs_after)) circuit_to_qasm(circ, fname.split(".")[0] + "_tket.qasm") f.write("%s,%d,%d,%d,%d,%f\n" % (fname, num_gates_before, num_CNOTs_before, num_gates_after, num_CNOTs_after, stop - start))
regular_pass_0 = SequencePass([FlattenRegisters(), DecomposeBoxes(), RebaseCirq()]) regular_pass_1 = SequencePass( [ FlattenRegisters(), DecomposeBoxes(), SynthesiseIBM(), RebaseCirq(), RemoveRedundancies(), _cirq_squash, ] ) regular_pass_2 = SequencePass( [ FlattenRegisters(), DecomposeBoxes(), FullPeepholeOptimise(), RebaseCirq(), RemoveRedundancies(), _cirq_squash, ] ) clifford_pass_0 = SequencePass( [FlattenRegisters(), DecomposeBoxes(), _partial_clifford_rebase] ) clifford_pass_1 = SequencePass( [ FlattenRegisters(), DecomposeBoxes(), SynthesiseIBM(), RemoveRedundancies(),
qi = QuantumInstance(backend, noise_model=model, seed_transpiler=2, seed_simulator=2) grover_optimizer = GroverOptimizer(6, num_iterations=10, quantum_instance=qi) results = grover_optimizer.solve(qp) print("x={}".format(results.x)) print("fval={}".format(results.fval)) print("n_circs={}".format(len(results.operation_counts))) # We can insert compilation passes from `pytket` into Qiskit as `TranspilerPass`es, compose with others to form a `PassManager`, and embed into the `QuantumInstance`. from pytket.passes import FullPeepholeOptimise from pytket.extensions.qiskit.tket_pass import TketPass from qiskit.transpiler import PassManager from qiskit.transpiler.passes import Unroller tp = TketPass(FullPeepholeOptimise()) pm = PassManager([Unroller(["cx", "p", "u"]), tp]) qi = QuantumInstance(backend, pass_manager=pm, noise_model=model, seed_simulator=2) grover_optimizer = GroverOptimizer(6, num_iterations=10, quantum_instance=qi) results = grover_optimizer.solve(qp) print("x={}".format(results.x)) print("fval={}".format(results.fval)) print("n_circs={}".format(len(results.operation_counts))) # For this particular run, the case of compiling with Qiskit converged with fewer circuit executions but the additional noise incurred in running the circuit caused the optimiser to miss the global optimum, whereas the case for `pytket` eventually reached the true minimum value. # # When using `TketPass` there is a conversion between `qiskit.DAGCircuit` and `pytket.Circuit`, meaning the circuit needs to be in a form suitable for conversion (i.e. the gates used have to be supported by both `qiskit` and `pytket`). If you encounter any issues with using this, we recommend using `Unroller(['cx', 'u1', 'u2', 'u3'])` and `RebaseIBM` at the point before conversion to guarantee appropriate gates are used. from pytket.passes import RebaseIBM, SequencePass
from qiskit import QuantumCircuit from qiskit.compiler import transpile from qiskit.transpiler import CouplingMap if comp_pass == _PASS_FULLPASS: # Default comp_pass = _PASS_QISO3 outfile = "{set}Results_{comp}_{cpass}_{back}.csv".format( set=set_outfile_str[test_set], comp=compiler_outfile_str[compiler], cpass=pass_outfile_str[comp_pass], back=backend_outfile_str[backend]) ### optimisation passes: tketpass = FullPeepholeOptimise() if compiler == _COMPILER_TKET and comp_pass == _PASS_CHEMPASS: tketpass = SequencePass([PauliSimp(), FullPeepholeOptimise()]) all_to_all_coupling = list() for i in range(53): for j in range(i + 1, 53): all_to_all_coupling.append([i, j]) rigetti_coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], [15, 8], [2, 15], [3, 14]] google_coupling = [[0, 5], [1, 5], [1, 6], [2, 6], [2, 7], [3, 8], [3, 9], [4, 9], [4, 10], [5, 11], [5, 12], [6, 12], [6, 13], [7, 13], [7, 14], [8, 14], [8, 15], [9, 15], [9, 16],
tk_circ = qiskit_to_tk(q_circ) backend = FakeMelbourne() coupling_list = backend.configuration().coupling_map coupling_map = CouplingMap(coupling_list) characterisation = process_characterisation(backend) directed_arc = Device( characterisation.get("NodeErrors", {}), characterisation.get("EdgeErrors", {}), characterisation.get("Architecture", Architecture([])), ) comp_tk = tk_circ.copy() DecomposeBoxes().apply(comp_tk) FullPeepholeOptimise().apply(comp_tk) CXMappingPass(directed_arc, NoiseAwarePlacement(directed_arc), directed_cx=True, delay_measures=True).apply(comp_tk) DecomposeSwapsToCXs(directed_arc).apply(comp_tk) cost = lambda c: c.n_gates_of_type(OpType.CX) comp = RepeatWithMetricPass( SequencePass( [CommuteThroughMultis(), RemoveRedundancies(), CliffordSimp(False)]), cost) comp.apply(comp_tk) SynthesiseIBM().apply(comp_tk)
# Start timer start = time.time() circ = Circuit(nb_qubits) h = 1.0 Jz = 1.0 for i in range(nb_steps): # Using Heisenberg Hamiltonian: for q in range(nb_qubits): circ.add_pauliexpbox(PauliExpBox([Pauli.X], -h * step_size), [q]) for q in range(nb_qubits - 1): circ.add_pauliexpbox( PauliExpBox([Pauli.Z, Pauli.Z], -Jz * step_size), [q, q + 1]) # Compile to gates backend = AerStateBackend() circ = backend.get_compiled_circuit(circ) # Apply optimization FullPeepholeOptimise().apply(circ) end = time.time() data.append(end - start) print('n_qubits =', nb_qubits, '; Elapsed time =', mean(data), '+/-', stdev(data), '[secs]') # for com in circ: # equivalently, circ.get_commands() # print(com.op, com.op.type, com.args)
energy = objective(arg_values) print(energy) # This is now the simplest form that this operation can take, but it isn't necessarily the most effective. When we decompose the ansatz circuit into basic gates, it is still very expensive. We can employ some of the circuit simplification passes available in `pytket` to reduce its size and improve fidelity in practice. # # A good example is to decompose each `PauliExpBox` into basic gates and then apply `FullPeepholeOptimise`, which defines a compilation strategy utilising all of the simplifications in `pytket` that act locally on small regions of a circuit. We can examine the effectiveness by looking at the number of two-qubit gates before and after simplification, which tends to be a good indicator of fidelity for near-term systems where these gates are often slow and inaccurate. from pytket import OpType from pytket.passes import FullPeepholeOptimise test_circuit = ucc(arg_values) print("CX count before", test_circuit.n_gates_of_type(OpType.CX)) print("CX depth before", test_circuit.depth_by_type(OpType.CX)) FullPeepholeOptimise().apply(test_circuit) print("CX count after FPO", test_circuit.n_gates_of_type(OpType.CX)) print("CX depth after FPO", test_circuit.depth_by_type(OpType.CX)) # These simplification techniques are very general and are almost always beneficial to apply to a circuit if you want to eliminate local redundancies. But UCC ansätze have extra structure that we can exploit further. They are defined entirely out of exponentiated tensors of Pauli matrices, giving the regular structure described by the `PauliExpBox`es. Under many circumstances, it is more efficient to not synthesise these constructions individually, but simultaneously in groups. The `PauliSimp` pass finds the description of a given circuit as a sequence of `PauliExpBox`es and resynthesises them (by default, in groups of commuting terms). This can cause great change in the overall structure and shape of the circuit, enabling the identification and elimination of non-local redundancy. from pytket.passes import PauliSimp test_circuit = ucc(arg_values) print("CX count before", test_circuit.n_gates_of_type(OpType.CX)) print("CX depth before", test_circuit.depth_by_type(OpType.CX)) PauliSimp().apply(test_circuit)
def run_on_nam_benchmarks(fname): f = open(fname, "w") d = "nam-benchmarks" f.write("name, num gates before, num gates after\n") for fname in os.listdir(d): print("Processing %s..." % fname) # same hack as the one used in run_qiskit.py inqasm = open("nam-benchmarks/%s" % fname, "r") tmp = open("copy.qasm", "w") # hardcoded filename p_ccz = re.compile("ccz (.*), (.*), (.*);") p_ccx = re.compile("ccx (.*), (.*), (.*);") for line in inqasm: m1 = p_ccx.match(line) m2 = p_ccz.match(line) if m1: a = m1.group(1) b = m1.group(2) c = m1.group(3) tmp.write("h %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("t %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("tdg %s;\n" % (b)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("t %s;\n" % (a)) tmp.write("t %s;\n" % (b)) tmp.write("t %s;\n" % (c)) tmp.write("h %s;\n" % (c)) elif m2: a = m2.group(1) b = m2.group(2) c = m2.group(3) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("t %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (b, c)) tmp.write("tdg %s;\n" % (c)) tmp.write("cx %s, %s;\n" % (a, c)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("tdg %s;\n" % (b)) tmp.write("cx %s, %s;\n" % (a, b)) tmp.write("t %s;\n" % (a)) tmp.write("t %s;\n" % (b)) tmp.write("t %s;\n" % (c)) else: tmp.write(line) tmp.close() circ = circuit_from_qasm("copy.qasm") num_gates_before = circ.n_gates print("\nORIGINAL: %d gates" % (num_gates_before)) FullPeepholeOptimise().apply(circ) num_gates_after = circ.n_gates print("OPTIMIZED: %d gates\n" % (num_gates_after))