# Randomised iterated PyZX + TODD search used for adder_8 (pyzx 0.10.7, mqt.qcec 3.11, qiskit 2.5.2).
# Usage: python3 iterate3.py <seed_start> <seed_end> <rounds> <seconds>; run from a folder holding ref/reference_circuit.qasm and runs/.
# The T=161 circuit came from seed 103, round 8 (seeds 102..131 run in one process).
import sys, random, re, time
import pyzx as zx
from fractions import Fraction
from qiskit import QuantumCircuit
from mqt import qcec
REF = QuantumCircuit.from_qasm_file("ref/reference_circuit.qasm")
seq = {0:[],1:["t"],2:["s"],3:["s","t"],4:["z"],5:["z","t"],6:["sdg"],7:["tdg"]}
def norm(qasm):
    out=[]
    for line in qasm.splitlines():
        m = re.match(r"\s*rz\((.*?)\)\s+(.*);", line)
        if m:
            k = Fraction(eval(m.group(1).replace("pi","1"))).limit_denominator(64)*4
            out += [f"{g} {m.group(2)};" for g in seq[int(k)%8]]
        else: out.append(line)
    return "\n".join(out)+"\n"
def tc(q): return sum(1 for l in q.splitlines() if l.startswith(("t ","tdg ")))
def twoq(q): return sum(1 for l in q.splitlines() if l.startswith(("cx ","cz ")))
s0, s1, R, deadline = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]), time.time()+float(sys.argv[4])
c0 = zx.Circuit.load("ref/reference_circuit.qasm")
gbest = 999
for seed in range(s0, s1):
    random.seed(seed); c = c0.copy(); best_here = 999
    for it in range(R):
        if time.time() > deadline: break
        g = c.to_graph()
        mode = "fr" if it == 0 or random.random() < 0.75 else "tr"
        try:
            if mode == "fr": zx.full_reduce(g); c = zx.extract_circuit(g.copy())
            else: zx.teleport_reduce(g); c = zx.Circuit.from_graph(g)
            c = zx.full_optimize(zx.basic_optimization(c.to_basic_gates()))
        except Exception as e:
            print("err", seed, it, e, flush=True); break
        q = norm(c.to_basic_gates().to_qasm()); t = tc(q)
        if t < gbest and twoq(q) <= 1636:
            r = str(qcec.verify(REF, QuantumCircuit.from_qasm_str(q), timeout=400, parallel=False).equivalence)
            print(f"seed {seed} it {it} {mode} T {t} 2q {twoq(q)} {r}", flush=True)
            if r.endswith(("equivalent","equivalent_up_to_global_phase")) and "not" not in r:
                gbest = t; open(f"runs/ok_{t}_s{seed}_i{it}.qasm","w").write(q)
        best_here = min(best_here, t)
    print(f"seed {seed} done best {best_here}", flush=True)
print("process best", gbest)
