Some checks failed
Build wheels / build (ubuntu-latest, 3.11) (push) Has been cancelled
Build wheels / build (ubuntu-latest, 3.12) (push) Has been cancelled
Build wheels / build (ubuntu-latest, 3.13) (push) Has been cancelled
Tests / check (push) Has been cancelled
Tests / build (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / build (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / build (ubuntu-latest, 3.13) (push) Has been cancelled
138 lines
4.7 KiB
Python
138 lines
4.7 KiB
Python
"""Compare QMatchaTeaBackend with the VidalBackend fast path."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import math
|
|
import time
|
|
|
|
import numpy as np
|
|
import torch
|
|
from qibo import Circuit, gates, hamiltonians
|
|
from qibo.symbols import X, Y, Z
|
|
|
|
from qibotn.backends.qmatchatea import QMatchaTeaBackend
|
|
from qibotn.backends.vidal import VidalBackend
|
|
|
|
|
|
def build_circuit(nqubits, nlayers, seed, kind):
|
|
rng = np.random.default_rng(seed)
|
|
circuit = Circuit(nqubits)
|
|
for layer in range(nlayers):
|
|
for q in range(nqubits):
|
|
circuit.add(gates.RY(q, theta=rng.uniform(-math.pi, math.pi)))
|
|
circuit.add(gates.RZ(q, theta=rng.uniform(-math.pi, math.pi)))
|
|
if kind == "brickwall":
|
|
for q in range(0, nqubits - 1, 2):
|
|
circuit.add(gates.CNOT(q, q + 1))
|
|
for q in range(1, nqubits - 1, 2):
|
|
circuit.add(gates.CNOT(q, q + 1))
|
|
elif kind == "shifted-cz":
|
|
for q in range(layer % 2, nqubits - 1, 2):
|
|
circuit.add(gates.CZ(q, q + 1))
|
|
elif kind == "reversed-cnot":
|
|
for q in range(0, nqubits - 1, 2):
|
|
circuit.add(gates.CNOT(q + 1, q))
|
|
for q in range(1, nqubits - 1, 2):
|
|
circuit.add(gates.CNOT(q, q + 1))
|
|
else:
|
|
raise ValueError(f"Unknown circuit kind {kind!r}.")
|
|
return circuit
|
|
|
|
|
|
def build_observable(nqubits, kind):
|
|
form = 0
|
|
if kind == "ring-xz":
|
|
for q in range(nqubits):
|
|
form += 0.5 * X(q) * Z((q + 1) % nqubits)
|
|
elif kind == "open-zz":
|
|
for q in range(nqubits - 1):
|
|
form += Z(q) * Z(q + 1) / (nqubits - 1)
|
|
elif kind == "mixed":
|
|
form += 0.25 * X(0) - 0.5 * Z(nqubits - 1)
|
|
for q in range(0, nqubits - 1, 3):
|
|
form += 0.125 * Y(q) * Y(q + 1)
|
|
else:
|
|
raise ValueError(f"Unknown observable kind {kind!r}.")
|
|
return hamiltonians.SymbolicHamiltonian(form=form)
|
|
|
|
|
|
def run_backend(backend, circuit, observable):
|
|
start = time.perf_counter()
|
|
value = backend.expectation(circuit, observable, preprocess=False, compile_circuit=True)
|
|
return float(np.real(value)), time.perf_counter() - start
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--nqubits", type=int, default=34)
|
|
parser.add_argument("--nlayers", type=int, default=20)
|
|
parser.add_argument("--bond", "--bonds", dest="bond", type=int, default=512)
|
|
parser.add_argument("--seed", type=int, default=42)
|
|
parser.add_argument("--tensor-module", choices=("torch", "numpy"), default="torch")
|
|
parser.add_argument("--torch-threads", type=int, default=32)
|
|
parser.add_argument(
|
|
"--circuit-kind",
|
|
choices=("brickwall", "shifted-cz", "reversed-cnot"),
|
|
default="brickwall",
|
|
)
|
|
parser.add_argument(
|
|
"--observable-kind",
|
|
choices=("ring-xz", "open-zz", "mixed"),
|
|
default="ring-xz",
|
|
)
|
|
parser.add_argument("--reference-file")
|
|
parser.add_argument("--skip-qmatchatea", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
torch.set_num_threads(args.torch_threads)
|
|
circuit = build_circuit(args.nqubits, args.nlayers, args.seed, args.circuit_kind)
|
|
observable = build_observable(args.nqubits, args.observable_kind)
|
|
|
|
exact = None
|
|
if args.reference_file:
|
|
with open(args.reference_file, "r", encoding="utf-8") as f:
|
|
exact = float(json.load(f)["expectation"])
|
|
|
|
print(
|
|
f"nqubits={args.nqubits} nlayers={args.nlayers} bond={args.bond} "
|
|
f"circuit={args.circuit_kind} observable={args.observable_kind} "
|
|
f"tensor_module={args.tensor_module} torch_threads={args.torch_threads}"
|
|
)
|
|
if exact is not None:
|
|
print(f"exact={exact:.16e}")
|
|
print("backend value abs_error seconds")
|
|
|
|
if not args.skip_qmatchatea:
|
|
qmt = QMatchaTeaBackend()
|
|
qmt.configure_tn_simulation(
|
|
ansatz="MPS",
|
|
max_bond_dimension=args.bond,
|
|
cut_ratio=1e-12,
|
|
svd_control="E!",
|
|
tensor_module=args.tensor_module,
|
|
compile_circuit=True,
|
|
track_memory=False,
|
|
)
|
|
value, seconds = run_backend(qmt, circuit, observable)
|
|
error = float("nan") if exact is None else abs(value - exact)
|
|
print(f"qmatchatea {value:.16e} {error:.6e} {seconds:.3f}")
|
|
|
|
vidal = VidalBackend()
|
|
vidal.configure_tn_simulation(
|
|
ansatz="MPS",
|
|
max_bond_dimension=args.bond,
|
|
cut_ratio=1e-12,
|
|
tensor_module=args.tensor_module,
|
|
compile_circuit=True,
|
|
fallback=True,
|
|
)
|
|
value, seconds = run_backend(vidal, circuit, observable)
|
|
error = float("nan") if exact is None else abs(value - exact)
|
|
print(f"vidal {value:.16e} {error:.6e} {seconds:.3f}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|