benchmark测试,发现瓶颈:路径搜索
Some checks are pending
Build wheels / build (ubuntu-latest, 3.11) (push) Waiting to run
Build wheels / build (ubuntu-latest, 3.12) (push) Waiting to run
Build wheels / build (ubuntu-latest, 3.13) (push) Waiting to run
Tests / check (push) Waiting to run
Tests / build (ubuntu-latest, 3.11) (push) Blocked by required conditions
Tests / build (ubuntu-latest, 3.12) (push) Blocked by required conditions
Tests / build (ubuntu-latest, 3.13) (push) Blocked by required conditions

This commit is contained in:
2026-04-27 18:59:54 +08:00
parent 2c54840e7b
commit 80d9c1de5a
5 changed files with 82 additions and 11 deletions

View File

@@ -1,10 +1,13 @@
"""Benchmark: qibojit (reference) vs qibotn/quimb MPS, with error comparison.""" """Benchmark: qibojit (reference) vs qibotn/quimb MPS, with error comparison."""
import time import time
import argparse import argparse
import os
import numpy as np import numpy as np
import qibo import qibo
from qibo import Circuit, gates from qibo import Circuit, gates
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
def make_circuit(circuit_type, nqubits, nlayers=1): def make_circuit(circuit_type, nqubits, nlayers=1):
c = Circuit(nqubits) c = Circuit(nqubits)
@@ -12,10 +15,11 @@ def make_circuit(circuit_type, nqubits, nlayers=1):
from qibo.models import QFT from qibo.models import QFT
return QFT(nqubits) return QFT(nqubits)
elif circuit_type == "variational": elif circuit_type == "variational":
for _ in range(nlayers): for layer in range(nlayers):
for q in range(nqubits): for q in range(nqubits):
c.add(gates.RY(q, theta=np.random.uniform(0, 2 * np.pi))) 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)) c.add(gates.CZ(q, q + 1))
elif circuit_type == "ghz": elif circuit_type == "ghz":
c.add(gates.H(0)) 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): def compare(sv_ref, sv_mps):
sv_ref = np.array(sv_ref).flatten() sv_ref = np.array(sv_ref, dtype=complex).flatten()
sv_mps = np.array(sv_mps).flatten() sv_mps = np.array(sv_mps, dtype=complex).flatten()
fidelity = abs(np.dot(sv_ref.conj(), sv_mps)) ** 2 fidelity = abs(np.dot(sv_ref.conj(), sv_mps)) ** 2
l2_err = np.linalg.norm(sv_ref - sv_mps) l2_err = np.linalg.norm(sv_ref - sv_mps)
return fidelity, l2_err 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(): def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--nqubits", type=int, default=10) parser.add_argument("--nqubits", type=int, default=10)
@@ -66,16 +75,27 @@ def main():
help="Max bond dimension for MPS (None = unlimited)") help="Max bond dimension for MPS (None = unlimited)")
parser.add_argument("--svd-cutoff", type=float, default=1e-6) parser.add_argument("--svd-cutoff", type=float, default=1e-6)
parser.add_argument("--optimizer", type=str, default="auto-hq") 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() args = parser.parse_args()
np.random.seed(42)
print(f"Circuit: {args.circuit}, nqubits={args.nqubits}, nlayers={args.nlayers}") 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}") 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) cache_path = jit_cache_path(args.circuit, args.nqubits, args.nlayers)
sv_ref, t_ref = run_qibojit(circuit_ref) t_ref = None
print(f"\n[qibojit] time={t_ref:.4f}s")
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) circuit_mps = make_circuit(args.circuit, args.nqubits, args.nlayers)
try: try:
sv_mps, t_mps = run_quimb_mps(circuit_mps, args.max_bond, args.svd_cutoff, args.optimizer) 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"[quimb MPS] time={t_mps:.4f}s")
print(f"\nFidelity : {fidelity:.8f} (1=perfect)") print(f"\nFidelity : {fidelity:.8f} (1=perfect)")
print(f"L2 error : {l2_err:.2e}") 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: except Exception as e:
print(f"[quimb MPS] FAILED: {e}") print(f"[quimb MPS] FAILED: {e}")
raise raise

11
log Normal file
View File

@@ -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

View File

@@ -167,7 +167,7 @@ def execute_circuit(
raise_error(ValueError, "Initial state not None supported only for MPS ansatz.") raise_error(ValueError, "Initial state not None supported only for MPS ansatz.")
circ_quimb = self.circuit_ansatz.from_openqasm2_str( 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: if nshots:

View File

@@ -58,7 +58,7 @@ class TensorNetworkResult:
def state(self): def state(self):
"""Return the statevector if the number of qubits is less than 20.""" """Return the statevector if the number of qubits is less than 20."""
if self.nqubits < 32: if self.nqubits < 35:
return self.statevector return self.statevector
raise_error( raise_error(
NotImplementedError, NotImplementedError,

39
sweep_bond_32q.py Normal file
View File

@@ -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}")