From 80d9c1de5af3f33821febe4a07925a1a30be146a Mon Sep 17 00:00:00 2001 From: jaunatisblue Date: Mon, 27 Apr 2026 18:59:54 +0800 Subject: [PATCH] =?UTF-8?q?benchmark=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E7=93=B6=E9=A2=88=EF=BC=9A=E8=B7=AF=E5=BE=84=E6=90=9C?= =?UTF-8?q?=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- benchmark_mps.py | 39 +++++++++++++++++++++++++++--------- log | 11 ++++++++++ src/qibotn/backends/quimb.py | 2 +- src/qibotn/result.py | 2 +- sweep_bond_32q.py | 39 ++++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 log create mode 100644 sweep_bond_32q.py diff --git a/benchmark_mps.py b/benchmark_mps.py index c2342b5..90c8275 100644 --- a/benchmark_mps.py +++ b/benchmark_mps.py @@ -1,10 +1,13 @@ """Benchmark: qibojit (reference) vs qibotn/quimb MPS, with error comparison.""" import time import argparse +import os import numpy as np import qibo from qibo import Circuit, gates +DATA_DIR = os.path.join(os.path.dirname(__file__), "data") + def make_circuit(circuit_type, nqubits, nlayers=1): c = Circuit(nqubits) @@ -12,10 +15,11 @@ def make_circuit(circuit_type, nqubits, nlayers=1): from qibo.models import QFT return QFT(nqubits) elif circuit_type == "variational": - for _ in range(nlayers): + for layer in range(nlayers): for q in range(nqubits): c.add(gates.RY(q, theta=np.random.uniform(0, 2 * np.pi))) - for q in range(0, nqubits - 1, 2): + offset = layer % 2 + for q in range(offset, nqubits - 1, 2): c.add(gates.CZ(q, q + 1)) elif circuit_type == "ghz": c.add(gates.H(0)) @@ -49,13 +53,18 @@ def run_quimb_mps(circuit, max_bond, svd_cutoff, optimizer): def compare(sv_ref, sv_mps): - sv_ref = np.array(sv_ref).flatten() - sv_mps = np.array(sv_mps).flatten() + sv_ref = np.array(sv_ref, dtype=complex).flatten() + sv_mps = np.array(sv_mps, dtype=complex).flatten() fidelity = abs(np.dot(sv_ref.conj(), sv_mps)) ** 2 l2_err = np.linalg.norm(sv_ref - sv_mps) return fidelity, l2_err +def jit_cache_path(circuit_type, nqubits, nlayers): + os.makedirs(DATA_DIR, exist_ok=True) + return os.path.join(DATA_DIR, f"jit_{circuit_type}_n{nqubits}_l{nlayers}.npy") + + def main(): parser = argparse.ArgumentParser() parser.add_argument("--nqubits", type=int, default=10) @@ -66,16 +75,27 @@ def main(): help="Max bond dimension for MPS (None = unlimited)") parser.add_argument("--svd-cutoff", type=float, default=1e-6) parser.add_argument("--optimizer", type=str, default="auto-hq") + parser.add_argument("--skip-jit", action="store_true", + help="Skip qibojit run, load cached statevector if available") args = parser.parse_args() - np.random.seed(42) print(f"Circuit: {args.circuit}, nqubits={args.nqubits}, nlayers={args.nlayers}") print(f"MPS config: max_bond={args.max_bond}, svd_cutoff={args.svd_cutoff}, optimizer={args.optimizer}") - circuit_ref = make_circuit(args.circuit, args.nqubits, args.nlayers) - sv_ref, t_ref = run_qibojit(circuit_ref) - print(f"\n[qibojit] time={t_ref:.4f}s") + cache_path = jit_cache_path(args.circuit, args.nqubits, args.nlayers) + t_ref = None + if args.skip_jit and os.path.exists(cache_path): + sv_ref = np.load(cache_path) + print(f"\n[qibojit] loaded from cache: {cache_path}") + else: + np.random.seed(42) + circuit_ref = make_circuit(args.circuit, args.nqubits, args.nlayers) + sv_ref, t_ref = run_qibojit(circuit_ref) + np.save(cache_path, sv_ref) + print(f"\n[qibojit] time={t_ref:.4f}s (saved to {cache_path})") + + np.random.seed(42) circuit_mps = make_circuit(args.circuit, args.nqubits, args.nlayers) try: sv_mps, t_mps = run_quimb_mps(circuit_mps, args.max_bond, args.svd_cutoff, args.optimizer) @@ -83,7 +103,8 @@ def main(): print(f"[quimb MPS] time={t_mps:.4f}s") print(f"\nFidelity : {fidelity:.8f} (1=perfect)") print(f"L2 error : {l2_err:.2e}") - print(f"Speedup : {t_ref/t_mps:.2f}x" if t_mps > 0 else "") + if t_ref is not None and t_mps > 0: + print(f"Speedup : {t_ref/t_mps:.2f}x") except Exception as e: print(f"[quimb MPS] FAILED: {e}") raise diff --git a/log b/log new file mode 100644 index 0000000..6fd3fb2 --- /dev/null +++ b/log @@ -0,0 +1,11 @@ +[qibojit] loaded from cache: /home/yx/qibotn/data/jit_variational_n32_l5.npy + + bond time(s) fidelity l2_err +---------------------------------------------- + 1 157.4587 0.00000280 9.99e-01 + 8 61.9126 0.99999014 2.22e-03 + 16 63.4902 0.99999014 2.22e-03 + 32 58.3594 0.99999014 2.22e-03 + 64 59.7043 0.99999014 2.22e-03 + 128 64.6368 0.99999014 2.22e-03 + 256 64.9058 0.99999014 2.22e-03 diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index 3ee200d..c378aef 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -167,7 +167,7 @@ def execute_circuit( raise_error(ValueError, "Initial state not None supported only for MPS ansatz.") circ_quimb = self.circuit_ansatz.from_openqasm2_str( - circuit.to_qasm(), psi0=initial_state + circuit.to_qasm(), psi0=initial_state, gate_opts={"max_bond": self.max_bond_dimension, "cutoff": self.svd_cutoff} ) if nshots: diff --git a/src/qibotn/result.py b/src/qibotn/result.py index 98d58a6..6548ebe 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -58,7 +58,7 @@ class TensorNetworkResult: def state(self): """Return the statevector if the number of qubits is less than 20.""" - if self.nqubits < 32: + if self.nqubits < 35: return self.statevector raise_error( NotImplementedError, diff --git a/sweep_bond_32q.py b/sweep_bond_32q.py new file mode 100644 index 0000000..5aae456 --- /dev/null +++ b/sweep_bond_32q.py @@ -0,0 +1,39 @@ +"""Bond dimension sweep for 32-qubit variational circuit.""" +import os +import sys +import numpy as np + +sys.path.insert(0, os.path.dirname(__file__)) +from benchmark_mps import make_circuit, run_qibojit, run_quimb_mps, compare, jit_cache_path, DATA_DIR + +NQUBITS = 32 +NLAYERS = 5 +BOND_VALUES = [1, 8, 16, 32, 64, 128, 256] +SVD_CUTOFF = 1e-6 +OPTIMIZER = "auto-hq" + +if __name__ == "__main__": + cache_path = jit_cache_path("variational", NQUBITS, NLAYERS) + + if os.path.exists(cache_path): + sv_ref = np.load(cache_path) + print(f"[qibojit] loaded from cache: {cache_path}\n") + else: + np.random.seed(42) + circuit_ref = make_circuit("variational", NQUBITS, NLAYERS) + sv_ref, t_ref = run_qibojit(circuit_ref) + np.save(cache_path, sv_ref) + print(f"[qibojit] time={t_ref:.4f}s (saved to {cache_path})\n") + + print(f"{'bond':>6} {'time(s)':>10} {'fidelity':>12} {'l2_err':>10}") + print("-" * 46) + + for bond in BOND_VALUES: + np.random.seed(42) + circuit_mps = make_circuit("variational", NQUBITS, NLAYERS) + try: + sv_mps, t_mps = run_quimb_mps(circuit_mps, bond, SVD_CUTOFF, OPTIMIZER) + fidelity, l2_err = compare(sv_ref, sv_mps) + print(f"{bond:>6} {t_mps:>10.4f} {fidelity:>12.8f} {l2_err:>10.2e}") + except Exception as e: + print(f"{bond:>6} FAILED: {e}")