def basic_optimization(c, g): orig_tcount = c.tcount() orig_2qubitcount = c.twoqubitcount() c_opt = zx.basic_optimization(c) 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, c.to_graph())
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 evolve(self, g, n_mutants, n_generations): self.n_mutants = n_mutants self.n_gens = n_generations self.g_orig = g.copy() to_graph_like(self.g_orig) self.c_orig = zx.extract_circuit(self.g_orig.copy()).to_basic_gates() self.c_orig = zx.basic_optimization(self.c_orig) # self.c_orig = c # self.g_orig = c.to_graph() # to_graph_like(self.g_orig) # self.mutants = [Mutant(c, self.g_orig) for _ in range(self.n_mutants)] self.mutants = [Mutant(self.c_orig, self.g_orig) for _ in range(self.n_mutants)] self.update_scores() best_mutant = min(self.mutants, key=lambda m: m.score) # FIXME: Check if this assignment is by reference or value best_score = best_mutant.score gen_scores = [best_score] best_scores = [best_score] for i in tqdm(range(self.n_gens), desc="Generations", disable=self.quiet): n_unique_mutants = len(list(set([id(m) for m in self.mutants]))) assert(n_unique_mutants == self.n_mutants) self.mutate() # self.update_scores() best_in_gen = min(self.mutants, key=lambda m: m.score) gen_scores.append(best_in_gen.score) # So that if we never improve, we see each generation. Because our actions might all rely on extracting, we may never improve on the original if best_in_gen.score < best_score: best_mutant = deepcopy(best_in_gen) best_score = best_in_gen.score best_scores.append(best_score) if all([m.dead for m in self.mutants]): print("[_optimize] stopping early -- all mutants are dead") break self.select() return best_scores, gen_scores, best_mutant.c_curr
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() # c_opt = zx.full_optimize(c_opt) c_tr = zx.basic_optimization(c_tr)
N_ITERS = 5000 INTERVAL = 250 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:
""" # Compare GA vs SA N_QUBITS = 9 DEPTH = 100 c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=N_QUBITS, depth=DEPTH, clifford=False) g = c.to_graph() g_tr = g.copy() zx.teleport_reduce(g_tr) c_tr = zx.Circuit.from_graph(g_tr.copy()) c_tr = zx.basic_optimization(c_tr).to_basic_gates() g_tr = c_tr.to_graph() to_graph_like(g_tr) # fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(5, 3)) fig, axes = plt.subplots(nrows=1, ncols=2) ## GA part N_MUTANTS = 100 N_GENS = 100 actions = [ga.rand_pivot, ga.rand_lc, ga.do_nothing] # actions = [rand_lc, rand_pivot] ga_opt = ga.GeneticOptimizer(actions, n_generations=N_GENS, n_mutants=N_MUTANTS,
if __name__ == "__main__": # imports import matplotlib.pyplot as plt N_QUBITS = 5 DEPTH = 100 c = zx.generate.CNOT_HAD_PHASE_circuit(qubits=N_QUBITS, depth=DEPTH, clifford=False) g = c.to_graph() g_tr = g.copy() zx.teleport_reduce(g_tr) c_tr = zx.Circuit.from_graph(g_tr.copy()) c_tr = zx.basic_optimization(c_tr).to_basic_gates() g_tr = c_tr.to_graph() to_graph_like(g_tr) # Testing for simplification operators """ actions = [ # BASIC color_change, remove_id, fuse, strong_comp, copy_X, # INTERMEDIATE spider_simp, pivot_gadget_simp, pivot_boundary_simp, lcomp_simp, bialg_simp, spider_simp, id_simp, gadget_simp, # supplementary_simp, cx_cancellation, optimize_1q, optimize_1q_decomp, # remove_redundancies, pauli_simp, clifford_simp, # kak_decomposition