From 57d5fbcbb0f7d2285ea2edab684e529e69970eb2 Mon Sep 17 00:00:00 2001 From: jaunatisblue Date: Sat, 9 May 2026 13:59:39 +0800 Subject: [PATCH] =?UTF-8?q?mps=E5=9F=BA=E7=A1=80=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- baseline_mps_expectation.py | 83 +++++++++++++++++++++++++++++++ src/qibotn/backends/qmatchatea.py | 16 ++++-- src/qibotn/observables.py | 5 +- 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 baseline_mps_expectation.py diff --git a/baseline_mps_expectation.py b/baseline_mps_expectation.py new file mode 100644 index 0000000..8a7e68e --- /dev/null +++ b/baseline_mps_expectation.py @@ -0,0 +1,83 @@ +"""Baseline MPS expectation scan with the qmatchatea backend.""" + +import argparse +import logging +import math +import time + +from qibo import Circuit, gates, hamiltonians +from qibo.symbols import X, Z + +from qibotn.backends.qmatchatea import QMatchaTeaBackend + + +def parse_bonds(value): + return [int(item) for item in value.split(",") if item.strip()] + + +def build_circuit(nqubits, nlayers, seed): + import numpy as np + + rng = np.random.default_rng(seed) + circuit = Circuit(nqubits) + for _ in range(nlayers): + for qubit in range(nqubits): + circuit.add(gates.RY(qubit, theta=rng.uniform(-math.pi, math.pi))) + circuit.add(gates.RZ(qubit, theta=rng.uniform(-math.pi, math.pi))) + for qubit in range(0, nqubits - 1, 2): + circuit.add(gates.CNOT(qubit, qubit + 1)) + for qubit in range(1, nqubits - 1, 2): + circuit.add(gates.CNOT(qubit, qubit + 1)) + return circuit + + +def build_observable(nqubits): + form = 0 + for qubit in range(nqubits - 1): + form += 0.5 * Z(qubit) * Z(qubit + 1) + form += 0.25 * X(0) + return hamiltonians.SymbolicHamiltonian(form=form) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--nqubits", type=int, default=20) + parser.add_argument("--nlayers", type=int, default=8) + parser.add_argument("--bonds", type=parse_bonds, default=parse_bonds("2,4,8,16,32")) + parser.add_argument("--seed", type=int, default=42) + parser.add_argument("--cut-ratio", type=float, default=1e-12) + parser.add_argument("--svd-control", default="V") + parser.add_argument("--no-exact", action="store_true") + args = parser.parse_args() + logging.getLogger("qibo.config").setLevel(logging.ERROR) + logging.getLogger("qtealeaves").setLevel(logging.ERROR) + + circuit = build_circuit(args.nqubits, args.nlayers, args.seed) + observable = build_observable(args.nqubits) + exact = None + if not args.no_exact: + exact = float(observable.expectation_from_state(circuit().state()).real) + + print(f"nqubits={args.nqubits} nlayers={args.nlayers} seed={args.seed}") + if exact is not None: + print(f"exact={exact:.16e}") + print("bond_dim expval abs_error rel_error seconds") + + backend = QMatchaTeaBackend() + for bond in args.bonds: + backend.configure_tn_simulation( + ansatz="MPS", + max_bond_dimension=bond, + cut_ratio=args.cut_ratio, + svd_control=args.svd_control, + ) + start = time.perf_counter() + value = float(backend.expectation(circuit, observable, preprocess=False).real) + elapsed = time.perf_counter() - start + abs_error = float("nan") if exact is None else abs(value - exact) + rel_error = float("nan") if exact is None else abs_error / max(abs(exact), 1e-15) + print(f"{bond:d} {value:.16e} {abs_error:.6e} {rel_error:.6e} {elapsed:.3f}") + + +if __name__ == "__main__": + main() diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index a5261f4..cacc71f 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -11,6 +11,7 @@ from qibo.backends import NumpyBackend from qibo.config import raise_error from qibotn.backends.abstract import QibotnBackend +from qibotn.observables import check_observable from qibotn.result import TensorNetworkResult @@ -75,6 +76,8 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): ini_bond_dimension=ini_bond_dimension, ) self.ansatz = ansatz + if hasattr(self, "qmatchatea_backend"): + self._setup_backend_specifics() def _setup_backend_specifics(self): """Configure qmatchatea QCBackend object.""" @@ -193,7 +196,7 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): statevector=statevector, ) - def expectation(self, circuit, observable): + def expectation(self, circuit, observable, preprocess=True): """Compute the expectation value of a Qibo-friendly ``observable`` on the Tensor Network constructed from a Qibo ``circuit``. @@ -216,8 +219,10 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): simulation setup. """ + observable = check_observable(observable, circuit.nqubits) + # From Qibo to Qiskit - circuit = self._qibocirc_to_qiskitcirc(circuit) + circuit = self._qibocirc_to_qiskitcirc(circuit, preprocess=preprocess) run_qk_params = qmatchatea.preprocessing.qk_transpilation_params(False) operators = qmatchatea.QCOperators() @@ -236,12 +241,17 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): return np.real(results.observables["custom_hamiltonian"]) - def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> qiskit.QuantumCircuit: + def _qibocirc_to_qiskitcirc( + self, qibo_circuit, preprocess=True + ) -> qiskit.QuantumCircuit: """Convert a Qibo Circuit into a Qiskit Circuit.""" # Convert the circuit to QASM 2.0 to qiskit qasm_circuit = qibo_circuit.to_qasm() qiskit_circuit = qiskit.QuantumCircuit.from_qasm_str(qasm_circuit) + if not preprocess: + return qiskit_circuit + # Transpile the circuit to adapt it to the linear structure of the MPS, # with the constraint of having only the gates basis_gates qiskit_circuit = qmatchatea.preprocessing.preprocess( diff --git a/src/qibotn/observables.py b/src/qibotn/observables.py index cca738e..56b3953 100644 --- a/src/qibotn/observables.py +++ b/src/qibotn/observables.py @@ -12,7 +12,10 @@ def check_observable(observable, circuit_nqubit): return create_hamiltonian_from_dict(observable, circuit_nqubit) if isinstance(observable, hamiltonians.SymbolicHamiltonian): return observable - raise TypeError("Invalid observable type.") + try: + return hamiltonians.SymbolicHamiltonian(form=observable) + except Exception as exc: + raise TypeError("Invalid observable type.") from exc def build_observable(circuit_nqubit):