def run(d, fname): f = open(fname, "w") f.write("name,Orig. total,Orig. CNOT,Orig. T,PyZX total,PyZX CNOT,PyZX T,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) circ = zx.Circuit.load(os.path.join(d, fname)).to_basic_gates() num_gates_before = len(circ.gates) cnot_count_before = circ.twoqubitcount() t_count_before = zx.tcount(circ) print("Original:\t Total %d, CNOT %d, T %d" % (num_gates_before, cnot_count_before, t_count_before)) start = time.perf_counter() # start timer g = circ.to_graph() zx.full_reduce(g,quiet=False) g.normalize() new_circ = zx.extract_circuit(g).to_basic_gates() stop = time.perf_counter() # stop timer num_gates_after = len(new_circ.gates) cnot_count_after = new_circ.twoqubitcount() t_count_after = zx.tcount(new_circ) print("Final:\t Total %d, CNOT %d, T %d\n" % (num_gates_after, cnot_count_after, t_count_after)) f.write("%s,%d,%d,%d,%d,%d,%d,%f\n" % (fname, num_gates_before, cnot_count_before, t_count_before, num_gates_after, cnot_count_after, t_count_after, stop - start))
def run(d, fname): f = open(fname, "w") f.write("name,T,2q,total,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) circ = zx.Circuit.load(os.path.join(d, fname)).to_basic_gates() start = time.perf_counter() g = circ.to_graph() zx.full_reduce(g, quiet=False) g.normalize() new_circ = zx.extract_circuit(g).to_basic_gates() stop = time.perf_counter() total_count = len(new_circ.gates) two_count = new_circ.twoqubitcount() t_count = zx.tcount(new_circ) print("\t Total %d, T %d, CNOT %d\n" % (total_count, t_count, two_count)) f.write("%s,%d,%d,%d,%f\n" % (fname, t_count, two_count, total_count, stop - start)) f.close()
def test_convert_to_from_pyzx_optimizing_circuit(tequila_circuit, t_reduce): pyzx_circuit = convert_to_pyzx(tequila_circuit) pyzx_graph = pyzx_circuit.to_graph() if t_reduce: pyzx.teleport_reduce(pyzx_graph) pyzx_circuit_opt = pyzx.Circuit.from_graph(pyzx_graph) else: pyzx.full_reduce(pyzx_graph) pyzx_graph.normalize() pyzx_circuit_opt = pyzx.extract_circuit(pyzx_graph.copy()) # compare_tensors returns True if pyzx_circuit and pyzx_circuit_opt # implement the same circuit (up to global phase) assert (pyzx.compare_tensors(pyzx_circuit, pyzx_circuit_opt)) # verify_equality return True if full_reduce() is able to reduce the # composition of the circuits to the identity assert (pyzx_circuit.verify_equality(pyzx_circuit_opt)) converted_circuit = convert_from_pyzx(pyzx_circuit_opt) wfn1 = simulate(tequila_circuit, backend="symbolic") wfn2 = simulate(converted_circuit, backend="symbolic") assert (numpy.isclose(wfn1.inner(wfn2), 1.0))
def pivot_anneal(g, iters=1000, temp=25, cool=0.005, score=g_score, cong_ps=[0.5, 0.5], lc_select=uniform_weights, pivot_select=uniform_weights, full_reduce_prob=0.1, reset_prob=0.0, quiet=False): g_best = g.copy() sz = score(g_best) sz_best = sz best_scores = list() for i in tqdm(range(iters), desc="annealing...", disable=quiet): g1 = g.copy() cong_method = np.random.choice(["LC", "PIVOT"], 1, p=cong_ps)[0] if cong_method == "PIVOT": apply_rand_pivot(g1, weight_func=pivot_select) else: apply_rand_lc(g1, weight_func=lc_select) # probabilistically full_reduce: if random.uniform(0, 1) < full_reduce_prob: zx.full_reduce(g1) sz1 = score(g1) best_scores.append(sz_best) if temp != 0: temp *= 1.0 - cool # if i % 50 == 0: # print(i) # print(temp) if sz1 < sz or \ (temp != 0 and random.random() < math.exp((sz - sz1)/temp)): # if temp != 0: temp *= 1.0 - cool sz = sz1 g = g1.copy() if sz < sz_best: # print("NEW BEST") g_best = g.copy() sz_best = sz elif random.uniform(0, 1) < reset_prob: g = g_best.copy() return g_best, best_scores
def g_score(g): g_tmp = g.copy() # FIXME: VERY EXPENSIVE. only to enable circuit extraction. # A better strategy would be to probailistically full_reduce zx.full_reduce(g_tmp) c = zx.extract_circuit(g_tmp.copy()).to_basic_gates() c = zx.basic_optimization(c) return c_score(c) """
def rand_lc(c, g, reduce_prob=0.1): g_tmp = g.copy() apply_rand_lc(g_tmp) # Note the work around to not always full_reduce the graph itself! g_fr = g_tmp.copy() zx.full_reduce(g_fr) c_new = zx.extract_circuit(g_fr.copy()).to_basic_gates() c_new = zx.basic_optimization(c_new) if random.uniform(0, 1) < reduce_prob: g_tmp = g_fr.copy() return True, (c_new, g_tmp)
def full_reduce(c, g): # FIXME: Should really be using g_tmp here? orig_tcount = c.tcount() orig_2qubitcount = c.twoqubitcount() zx.full_reduce(g) c_opt = zx.extract_circuit(g.copy()) opt_tcount = c_opt.tcount() opt_2qubitcount = c_opt.twoqubitcount() if orig_tcount == opt_tcount and orig_2qubitcount == opt_2qubitcount: return False, (c, g) return True, (c_opt, g)
def optimize(self): """ Optimize the circuit using PyZX full_reduce optimization function """ # transforming the PyZX circuit to a graph graph = self.circuit_zx.to_graph() zx.full_reduce(graph, quiet=True) # Optimizing the graph graph.normalize() # Extracting the circuit from the optimized graph self.circuit_zx = zx.extract_circuit(graph.copy()) return self.circuit_zx
def run(d, fname): f = open(fname, "w") f.write("name,T,2q,total,time\n") for fname in os.listdir(d): print("Processing %s..." % fname) circ = zx.Circuit.load(os.path.join(d, fname)).to_basic_gates() start = time.perf_counter() g = circ.to_graph() zx.full_reduce(g) g.normalize() opt_circ = zx.extract_circuit(g).to_basic_gates() stop = time.perf_counter() elapsed = stop - start if elapsed < TIMEOUT: try: # try to run full_optimize, cancelling after 10 minutes - elapsed with time_limit(TIMEOUT - round(elapsed)): opt_circ = zx.full_optimize(opt_circ) stop = time.perf_counter() except TimeoutException: print("\tfull_optimize is slow; only running full_reduce") else: print("\tfull_optimize is slow; only running full_reduce") total_count = len(opt_circ.gates) two_count = opt_circ.twoqubitcount() t_count = zx.tcount(opt_circ) print("\tTotal %d, T %d, 2-qubit %d\n" % (total_count, t_count, two_count)) f.write("%s,%d,%d,%d,%f\n" % (fname, t_count, two_count, total_count, stop - start)) f.close()
def qiskit_transpiler_pass(qasm): """ Class for parsing OPENQASM source files from a Qiskit QuantumCircuit, optimizing within PyZX, and converting back to OPENQASM that can be reconstructed into a Qiskit QuantumCircuit. """ # Call to a parent class of QASMParser to parse Qiskit's OPENQASM p = QiskitQASMParser() circ_list, whichpyzx = p.qiskitparse(qasm) graph_list = [circ_list[w].to_graph() for w in whichpyzx] [pyzx.full_reduce(g) for g in graph_list] pyzx_circ_list = [pyzx.extract.streaming_extract(g) for g in graph_list] pyzx_circ_list = [ pyzx.optimize.basic_optimization(new_c.to_basic_gates()) for new_c in pyzx_circ_list ] pyzx_qasm = [new_c.to_basic_gates().to_qasm() for new_c in pyzx_circ_list] # Verify with compare_tensor that all PyZX optimizations correctly simplify passedAll = True for i in range(len(pyzx_circ_list)): try: assert ( True ) #assert(pyzx.compare_tensors(pyzx_circ_list[i], circ_list[whichpyzx[i]], False)) except AssertionError: passedAll = False if not passedAll: return None # Ignore all register declarations and map registers back to the input qasm pyzx_qasm = [ "\n".join([ '' if line.startswith("qreg") else line for line in circ.splitlines()[2:] ]) for circ in pyzx_qasm ] for i in range(len(pyzx_qasm)): circ_list[whichpyzx[i]] = pyzx_qasm[i] qasm_string = 'OPENQASM 2.0;\ninclude "qelib1.inc";\n' + "\n".join( circ_list) sorted_registers = sorted(p.registers.items(), key=lambda x: x[1][0]) poss = [m.start() for m in re.finditer('q\[', qasm_string)] registered_qasm = '' prev_pos = 0 for pos in poss: registered_qasm += qasm_string[prev_pos:pos] prev_pos = pos + qasm_string[pos:].find(']') id = int(qasm_string[pos + 2:prev_pos]) leq_list = list(filter(lambda x: (x[1][0] <= id), sorted_registers)) registered_qasm += leq_list[-1][0] + '[' + str(id - leq_list[-1][1][0]) registered_qasm += qasm_string[prev_pos:] return registered_qasm
def _optimize(self, c): g = c.to_graph() zx.full_reduce(g, quiet=True) c_opt = zx.extract_circuit(g.copy()) # FIXME: maybe don't need g.copy()? return c_opt
N_QUBITS = 5 DEPTH = 100 c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=N_QUBITS, depth=DEPTH, clifford=False) print("----- initial -----") print(c.stats()) # zx.draw(c) plt.show() plt.close('all') g = c.to_graph() g_fr = g.copy() zx.full_reduce(g_fr) c_fr = zx.extract_circuit(g_fr.copy()).to_basic_gates() c_fr = zx.basic_optimization(c_fr) # note: we don't reset g_fr here because for any future annealing, we'd really optimize after the graph produced by full_reduce, rather than something resulting from extraction print("\n----- full_reduce + basic_optimization -----") print(c_fr.stats()) g_just_tr = g.copy() zx.teleport_reduce(g_just_tr) c_just_tr = zx.Circuit.from_graph(g_just_tr.copy()).to_basic_gates() print("\n----- teleport_reduce -----") print(c_just_tr.stats()) g_tr = g.copy() zx.teleport_reduce(g_tr) c_tr = zx.Circuit.from_graph(g_tr.copy()).to_basic_gates()
improvement_after = { x: list() for x in range(0, N_ITERS, INTERVAL) } REDUCE_METHOD = "TR" for c in tqdm(cs, desc="Circuits..."): g = c.to_graph() g_tmp = g.copy() if REDUCE_METHOD == "TR": zx.teleport_reduce(g_tmp) c_tr = zx.Circuit.from_graph(g_tmp.copy()).to_basic_gates() c_tr = zx.basic_optimization(c_tr) g_simp = c_tr.to_graph() elif REDUCE_METHOD == "FR": zx.full_reduce(g_tmp) g_simp = g_tmp.copy() else: raise RuntimeError(f"Invalid REDUCE_METHOD: {REDUCE_METHOD}") to_graph_like(g_simp) _, scores = pivot_anneal(g_simp, iters=N_ITERS) final_score = scores[-1] for x in range(0, N_ITERS, INTERVAL): if final_score < scores[x]: improvement_after[x].append(1) else: improvement_after[x].append(0)
# plt.show() cs_thresholded = [(f, c) for (f, c) in bench_cs if c.qubits <= QUBIT_THRESHOLD] print(f"{len(bench_cs)} benchmark circuits, {len(cs_thresholded)} of which have at most {QUBIT_THRESHOLD} qubits") reductions = dict() for (f, c) in cs_thresholded[:3]: init_score = c_score(c) g = c.to_graph() g_tmp = g.copy() if METHOD == "FR": g_opt = g_tmp.copy() zx.full_reduce(g_opt) c_opt = zx.extract_circuit(g_opt.copy()).to_basic_gates() c_opt = zx.basic_optimization(c_opt) opt_score = c_score(c_opt) elif METHOD == "TR": g_opt = g_tmp.copy() zx.teleport_reduce(g_opt) c_opt = zx.Circuit.from_graph(g_opt.copy()).to_basic_gates() c_opt = zx.basic_optimization(c_opt) opt_score = c_score(c_opt) # g_opt = c_opt.to_graph() elif METHOD == "qiskit": c_opt = QISKIT_OPT._optimize(c.copy())
print("Can convert from original graph back to circuit") except: print("Can NOT convert from original graph back to circuit") successes = 0 for _ in tqdm(range(1000)): c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=N_QUBITS, depth=DEPTH, clifford=False) g = c.to_graph() zx.full_reduce(g) try: c_opt = zx.Circuit.from_graph(g) successes += 1 except: continue print(f"Number of successes: {successes}") """ # The below tests the graph-likeness utilities for _ in tqdm(range(100), desc="Verifying equality with [to_graph_like]..."): c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=N_QUBITS, depth=DEPTH, clifford=False) g = c.to_graph() g1 = g.copy() to_graph_like(g1) zx.full_reduce(g1) c1 = zx.extract_circuit(g1.copy()) assert (c.verify_equality(c1)) print("[to_graph_like] appears to maintain equality!")