final first commit

This commit is contained in:
2026-05-19 17:19:36 +08:00
commit b199e2105e
114 changed files with 6844 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
def parse(options):
"""Parse options from string.
Args:
options (str): String with options.
It should have the form 'arg1=value1,arg2=value2,...'.
Returns:
dict: {'arg1': value1, 'arg2': value2, ...}
"""
kwargs = {}
if options is not None:
for parameter in options.split(","):
if "=" in parameter:
k, v = parameter.split("=")
kwargs[k] = v
else:
raise ValueError(f"Cannot parse parameter {parameter}.")
return kwargs
def get(circuit_name, nqubits, options=None, qibo=False):
if qibo:
from benchmarks.circuits import qibo as module
else:
from benchmarks.circuits import qasm as module
if circuit_name in ("qft", "QFT"):
circuit = module.QFT
elif circuit_name == "one-qubit-gate":
circuit = module.OneQubitGate
elif circuit_name == "two-qubit-gate":
circuit = module.TwoQubitGate
elif circuit_name in ("variational", "variational-circuit"):
circuit = module.VariationalCircuit
elif circuit_name in ("bernstein-vazirani", "bv"):
circuit = module.BernsteinVazirani
elif circuit_name in ("hidden-shift", "hs"):
circuit = module.HiddenShift
elif circuit_name == "qaoa":
circuit = module.QAOA
elif circuit_name == "supremacy":
circuit = module.SupremacyCircuit
elif circuit_name in ("basis-change", "bc"):
circuit = module.BasisChange
elif circuit_name in ("quantum-volume", "qv"):
circuit = module.QuantumVolume
else:
raise NotImplementedError(f"Cannot find circuit {circuit_name}.")
kwargs = parse(options)
return circuit(nqubits, **kwargs)

View File

@@ -0,0 +1,394 @@
import numpy as np
from abc import abstractmethod
class AbstractCircuit:
def __init__(self, nqubits):
self.nqubits = nqubits
self.parameters = {}
@abstractmethod
def __iter__(self):
raise NotImplementedError
def to_qasm(self, theta=None):
"""Creates the circuit in OpenQASM format.
Args:
theta (np.ndarray): If not ``None`` ``RX`` gates with the given
angles are added before the actual circuit gates so that the
initial state is non-trivial. Useful for testing.
Returns:
A string with the circuit in OpenQASM format.
"""
code = ['OPENQASM 2.0;', 'include "qelib1.inc";',
f'qreg q[{self.nqubits}];', f'creg m[{self.nqubits}];']
if theta is not None:
code.extend(f"rx({t}) q[{i}];" for i, t in enumerate(theta))
code.extend(iter(self))
return "\n".join(code)
def __str__(self):
return ", ".join(f"{k}={v}" for k, v in self.parameters.items())
class OneQubitGate(AbstractCircuit):
"""Applies a specific one qubit gate to all qubits."""
def __init__(self, nqubits, nlayers="1", gate="h", angles=""):
super().__init__(nqubits)
self.gate = gate
self.nlayers = int(nlayers)
self.angles = angles
self.parameters = {"nqubits": nqubits, "nlayers": nlayers,
"gate": gate, "params": angles}
def base_command(self, i):
if self.angles:
return "{}({}) q[{}];".format(self.gate, self.angles, i)
else:
return "{} q[{}];".format(self.gate, i)
def __iter__(self):
for _ in range(self.nlayers):
for i in range(self.nqubits):
yield self.base_command(i)
class TwoQubitGate(OneQubitGate):
"""Applies a specific two qubit gate to all pairs of adjacent qubits."""
def __init__(self, nqubits, nlayers="1", gate="cx", angles=""):
super().__init__(nqubits, nlayers, gate, angles)
def base_command(self, i):
if self.angles:
return "{}({}) q[{}],q[{}];".format(self.gate, self.angles, i, i + 1)
else:
return "{} q[{}],q[{}];".format(self.gate, i, i + 1)
def __iter__(self):
for _ in range(self.nlayers):
for i in range(0, self.nqubits - 1, 2):
yield self.base_command(i)
for i in range(1, self.nqubits - 1, 2):
yield self.base_command(i)
class QFT(AbstractCircuit):
"""Applies the Quantum Fourier Transform."""
def __init__(self, nqubits, swaps="True"):
super().__init__(nqubits)
self.swaps = swaps == "True"
self.parameters = {"nqubits": nqubits, "swaps": swaps}
def __iter__(self):
for i1 in range(self.nqubits):
yield f"h q[{i1}];"
for i2 in range(i1 + 1, self.nqubits):
theta = np.pi / 2 ** (i2 - i1)
yield f"cu1({theta}) q[{i2}],q[{i1}];"
if self.swaps:
for i in range(self.nqubits // 2):
yield f"swap q[{i}],q[{self.nqubits - i - 1}];"
class VariationalCircuit(AbstractCircuit):
"""Example variational circuit consisting of alternating layers of RY and CZ gates."""
def __init__(self, nqubits, nlayers="1", seed="123"):
super().__init__(nqubits)
self.nlayers = int(nlayers)
self.seed = int(seed)
self.parameters = {"nqubits": nqubits, "nlayers": nlayers, "seed": seed}
def __iter__(self):
nparams = 2 * self.nlayers * self.nqubits
np.random.seed(self.seed)
theta = iter(2 * np.pi * np.random.random(nparams))
for l in range(self.nlayers):
for i in range(self.nqubits):
yield f"ry({next(theta)}) q[{i}];"
for i in range(0, self.nqubits - 1, 2):
yield f"cz q[{i}],q[{i + 1}];"
for i in range(self.nqubits):
yield f"ry({next(theta)}) q[{i}];"
for i in range(1, self.nqubits - 2, 2):
yield f"cz q[{i}],q[{i + 1}];"
yield f"cz q[{0}],q[{self.nqubits - 1}];"
class BernsteinVazirani(AbstractCircuit):
"""Applies the Bernstein-Vazirani algorithm from Qiskit/openqasm.
See `https://github.com/Qiskit/openqasm/tree/0af8b8489f32d46692b3a3a1421e98c611cd86cc/benchmarks/bv`
for the OpenQASM code.
Note that `Barrier` gates are excluded for simulation.
"""
def __init__(self, nqubits):
super().__init__(nqubits)
self.parameters = {"nqubits": nqubits}
def __iter__(self):
yield f"x q[{self.nqubits - 1}];"
for i in range(self.nqubits):
yield f"h q[{i}];"
for i in range(self.nqubits - 1):
yield f"cx q[{i}],q[{self.nqubits - 1}];"
for i in range(self.nqubits - 1):
yield f"h q[{i}];"
#for i in range(self.nqubits - 1):
# yield f"measure m[{i}];"
class HiddenShift(AbstractCircuit):
"""Applies the Hidden Shift algorithm.
See `https://github.com/quantumlib/Cirq/blob/master/examples/hidden_shift_algorithm.py`
for the Cirq code.
If the shift (hidden bitstring) is not given then it is randomly generated
using `np.random.randint`.
"""
def __init__(self, nqubits, shift=""):
super().__init__(nqubits)
if len(shift):
if len(shift) != nqubits:
raise ValueError("Shift bitstring of length {} was given for "
"circuit of {} qubits."
"".format(len(shift), nqubits))
self.shift = [int(x) for x in shift]
else:
self.shift = np.random.randint(0, 2, size=(self.nqubits,))
self.parameters = {"nqubits": nqubits, "shift": shift}
def oracle(self):
for i in range(self.nqubits // 2):
yield f"cz q[{2 * i}],q[{2 * i + 1}];"
def __iter__(self):
for i in range(self.nqubits):
yield f"h q[{i}];"
for i, ish in enumerate(self.shift):
if ish:
yield f"x q[{i}];"
for gate in self.oracle():
yield gate
for i, ish in enumerate(self.shift):
if ish:
yield f"x q[{i}];"
for i in range(self.nqubits):
yield f"h q[{i}];"
for gate in self.oracle():
yield gate
for i in range(self.nqubits):
yield f"h q[{i}];"
#for i in range(self.nqubits):
# yield f"measure m[{i}];"
class QAOA(AbstractCircuit):
"""Example QAOA circuit for a MaxCut problem instance.
See `https://github.com/quantumlib/Cirq/blob/master/examples/qaoa.py` for
the Cirq code.
If a JSON file containing the node link structure is given then the graph
is loaded using `networkx.readwrite.json_graph.node_link_graph`, otherwise
the graph is generated randomly using `networkx.random_regular_graph`.
Note that different graphs may lead to different performance as the graph
structure affects circuit depth.
"""
def __init__(self, nqubits, nparams="2", graph="", seed="123"):
super().__init__(nqubits)
import networkx
self.nparams = int(nparams)
self.seed = int(seed)
if len(graph):
import json
with open(graph, "r") as file:
data = json.load(file)
self.graph = networkx.readwrite.json_graph.node_link_graph(data)
else:
self.graph = networkx.random_regular_graph(
3, self.nqubits, seed=self.seed
)
self.parameters = {"nqubits": nqubits, "nparams": nparams,
"graph": graph, "seed": seed}
@staticmethod
def RX(q, theta):
return f"rx({theta}) q[{q}];"
@staticmethod
def RZZ(q0, q1, theta):
return f"rzz({theta}) q[{q0}],q[{q1}];"
def maxcut_unitary(self, betas, gammas):
for beta, gamma in zip(betas, gammas):
for i, j in self.graph.edges:
yield self.RZZ(i, j, -0.5 * gamma)
for i in range(self.nqubits):
yield self.RX(i, 2 * beta)
def dump(self, dir):
"""Saves graph data as JSON in given directory."""
import json
import networkx
data = networkx.readwrite.json_graph.node_link_data(self.graph)
with open(dir, "w") as file:
json.dump(data, file)
def __iter__(self):
np.random.seed(self.seed)
betas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
gammas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
# Prepare uniform superposition
for i in range(self.nqubits):
yield f"h q[{i}];"
# Apply QAOA unitary
for gate in self.maxcut_unitary(betas, gammas):
yield gate
# Measure
# yield gates.M(*range(self.nqubits))
class SupremacyCircuit(AbstractCircuit):
"""Random circuit by Boixo et al 2018 for demonstrating quantum supremacy.
See `https://github.com/quantumlib/Cirq/blob/v0.11.0/cirq-core/cirq/experiments/google_v2_supremacy_circuit.py`
for the Cirq code.
This circuit is constructed using `cirq` by exporting to OpenQASM and
importing back to Qibo.
"""
def __init__(self, nqubits, depth="2", seed="123"):
super().__init__(nqubits)
self.depth = int(depth)
self.seed = int(seed)
self.parameters = {"nqubits": nqubits, "depth": depth, "seed": seed}
self.cirq_circuit = self.create_cirq_circuit()
def create_cirq_circuit(self):
import cirq
from cirq.experiments import google_v2_supremacy_circuit as spc
qubits = [cirq.GridQubit(i, 0) for i in range(self.nqubits)]
return spc.generate_boixo_2018_supremacy_circuits_v2(qubits, self.depth, self.seed)
def __iter__(self):
qasm = self.cirq_circuit.to_qasm()
for line in qasm.split("\n"):
first_word = line.split(" ")[0]
if first_word not in {"//", "OPENQASM", "include", "qreg"}:
if first_word == "sx":
yield line.replace("sx", "rx(pi*0.5)") # see issue #13
else:
yield line
class BasisChange(AbstractCircuit):
"""Basis change fermionic circuit.
See `https://quantumai.google/openfermion/tutorials/circuits_1_basis_change`
for OpenFermion/Cirq code.
This circuit is constructed using `openfermion` and `cirq` by exporting
to OpenQASM and importing back to Qibo.
"""
def __init__(self, nqubits, simulation_time="1", seed="123"):
super().__init__(nqubits)
self.simulation_time = float(simulation_time)
self.seed = int(seed)
self.parameters = {"nqubits": nqubits, "simulation_time": simulation_time,
"seed": seed}
self.openfermion_circuit = self.create_openfermion_circuit()
def create_openfermion_circuit(self):
import cirq
import openfermion
# Generate the random one-body operator.
T = openfermion.random_hermitian_matrix(self.nqubits, seed=self.seed)
# Diagonalize T and obtain basis transformation matrix (aka "u").
eigenvalues, eigenvectors = np.linalg.eigh(T)
basis_transformation_matrix = eigenvectors.transpose()
# Initialize the qubit register.
qubits = cirq.LineQubit.range(self.nqubits)
# Start circuit with the inverse basis rotation, print out this step.
inverse_basis_rotation = cirq.inverse(openfermion.bogoliubov_transform(qubits, basis_transformation_matrix))
circuit = cirq.Circuit(inverse_basis_rotation)
# Add diagonal phase rotations to circuit.
for k, eigenvalue in enumerate(eigenvalues):
phase = -eigenvalue * self.simulation_time
circuit.append(cirq.rz(rads=phase).on(qubits[k]))
# Finally, restore basis.
basis_rotation = openfermion.bogoliubov_transform(qubits, basis_transformation_matrix)
circuit.append(basis_rotation)
return circuit
def __iter__(self):
qasm = self.openfermion_circuit.to_qasm()
for line in qasm.split("\n"):
first_word = line.split(" ")[0]
if first_word not in {"//", "OPENQASM", "include", "qreg"}:
yield line
class QuantumVolume(AbstractCircuit):
"""Quantum Volume circuit from Qiskit.
See `https://qiskit.org/documentation/stubs/qiskit.circuit.library.QuantumVolume.html`
for the Qiskit model.
This circuit is constructed using `qiskit` by exporting to OpenQASM and
importing back to Qibo.
"""
def __init__(self, nqubits, depth="1", seed="123"):
super().__init__(nqubits)
self.depth = int(depth)
self.seed = int(seed)
self.parameters = {"nqubits": nqubits, "depth": depth, "seed": seed}
self.qiskit_circuit = self.create_qiskit_circuit()
self.expression_symbols = {"*", "/"}
self.expression_symbols.update(str(x) for x in range(10))
def create_qiskit_circuit(self):
from qiskit.circuit.library import QuantumVolume
circuit = QuantumVolume(self.nqubits, self.depth, seed=self.seed)
return circuit.decompose().decompose()
def __iter__(self):
raise NotImplementedError("Iteration is not available for "
"`QuantumVolume` because it is prepared "
"using Qiskit.")
def evaluate_pi(self, qasm):
left = qasm.find("pi")
if left < 0:
return qasm
import sympy
right = left + 2
left = left - 1
while qasm[left] in self.expression_symbols:
left -= 1
while qasm[right] in self.expression_symbols:
right += 1
expr = qasm[left + 1: right]
evaluated = sympy.sympify(expr).evalf()
return self.evaluate_pi(qasm.replace(expr, str(evaluated)))
def __iter__(self):
qasm = self.qiskit_circuit.qasm()
for line in qasm.split("\n"):
first_word = line.split(" ")[0]
if first_word not in {"//", "OPENQASM", "include", "qreg"}:
yield line.replace("1/(15*pi)", str(1.0 / (15.0 * np.pi)))
def to_qasm(self, theta=None):
qasm = super().to_qasm(theta)
return self.evaluate_pi(qasm)

View File

@@ -0,0 +1,218 @@
import numpy as np
from qibo import gates
from benchmarks.circuits import qasm
class OneQubitGate(qasm.OneQubitGate):
def __init__(self, nqubits, nlayers="1", gate="H", **params):
super().__init__(nqubits, nlayers=nlayers, gate=gate)
self.gate = getattr(gates, gate)
self.angles = {k: float(v) for k, v in params.items()}
self.parameters = {"nqubits": nqubits, "nlayers": nlayers,
"gate": gate, "params": params}
def to_qasm(self):
raise NotImplementedError
def base_command(self, i):
return self.gate(i, **self.angles)
class TwoQubitGate(OneQubitGate):
def __init__(self, nqubits, nlayers="1", gate="CNOT", **params):
super().__init__(nqubits, nlayers, gate, **params)
def to_qasm(self):
raise NotImplementedError
def base_command(self, i):
return self.gate(i, i + 1, **self.angles)
def __iter__(self):
return qasm.TwoQubitGate.__iter__(self)
class QFT(qasm.QFT):
def to_qasm(self):
raise NotImplementedError
def __iter__(self):
for i1 in range(self.nqubits):
yield gates.H(i1)
for i2 in range(i1 + 1, self.nqubits):
theta = np.pi / 2 ** (i2 - i1)
yield gates.CU1(i2, i1, theta)
if self.swaps:
for i in range(self.nqubits // 2):
yield gates.SWAP(i, self.nqubits - i - 1)
class VariationalCircuit(qasm.VariationalCircuit):
def __init__(self, nqubits, nlayers=1, varlayer="False", seed="123"):
super().__init__(nqubits, nlayers, seed)
self.varlayer = varlayer == "True"
self.parameters["varlayer"] = varlayer
def to_qasm(self):
raise NotImplementedError
def varlayer_circuit(self, theta):
theta = theta.reshape((2 * self.nlayers, self.nqubits))
pairs = list((i, i + 1) for i in range(0, self.nqubits - 1, 2))
for l in range(self.nlayers):
yield gates.VariationalLayer(range(self.nqubits), pairs,
gates.RY, gates.CZ,
theta[2 * l], theta[2 * l + 1])
for i in range(1, self.nqubits - 2, 2):
yield gates.CZ(i, i + 1)
yield gates.CZ(0, self.nqubits - 1)
def standard_circuit(self, theta):
theta = iter(theta)
for l in range(self.nlayers):
for i in range(self.nqubits):
yield gates.RY(i, next(theta))
for i in range(0, self.nqubits - 1, 2):
yield gates.CZ(i, i + 1)
for i in range(self.nqubits):
yield gates.RY(i, next(theta))
for i in range(1, self.nqubits - 2, 2):
yield gates.CZ(i, i + 1)
yield gates.CZ(0, self.nqubits - 1)
def __iter__(self):
np.random.seed(self.seed)
theta = 2 * np.pi * np.random.random(2 * self.nlayers * self.nqubits)
if self.varlayer:
return self.varlayer_circuit(theta)
else:
return self.standard_circuit(theta)
class BernsteinVazirani(qasm.BernsteinVazirani):
def to_qasm(self):
raise NotImplementedError
def __iter__(self):
yield gates.X(self.nqubits - 1)
for i in range(self.nqubits):
yield gates.H(i)
for i in range(self.nqubits - 1):
yield gates.CNOT(i, self.nqubits - 1)
for i in range(self.nqubits - 1):
yield gates.H(i)
for i in range(self.nqubits - 1):
yield gates.M(i)
class HiddenShift(qasm.HiddenShift):
def to_qasm(self):
raise NotImplementedError
def oracle(self):
for i in range(self.nqubits // 2):
yield gates.CZ(2 * i, 2 * i + 1)
def __iter__(self):
for i in range(self.nqubits):
yield gates.H(i)
for i, ish in enumerate(self.shift):
if ish:
yield gates.X(i)
for gate in self.oracle():
yield gate
for i, ish in enumerate(self.shift):
if ish:
yield gates.X(i)
for i in range(self.nqubits):
yield gates.H(i)
for gate in self.oracle():
yield gate
for i in range(self.nqubits):
yield gates.H(i)
yield gates.M(*range(self.nqubits))
class QAOA(qasm.QAOA):
def to_qasm(self):
raise NotImplementedError
@staticmethod
def RX(q, theta):
return gates.RX(q, theta=theta)
@staticmethod
def RZZ(q0, q1, theta):
phase = np.exp(0.5j * theta)
phasec = np.conj(phase)
matrix = np.diag([phasec, phase, phase, phasec])
return gates.Unitary(matrix, q0, q1)
def __iter__(self):
np.random.seed(self.seed)
betas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
gammas = np.random.uniform(-np.pi, np.pi, size=self.nparams)
# Prepare uniform superposition
for i in range(self.nqubits):
yield gates.H(i)
# Apply QAOA unitary
for gate in self.maxcut_unitary(betas, gammas):
yield gate
# Measure
yield gates.M(*range(self.nqubits))
class SupremacyCircuit(qasm.SupremacyCircuit):
def __init__(self, nqubits, depth="2", seed="123"):
super().__init__(nqubits, depth, seed)
from qibo import models
parent = qasm.SupremacyCircuit(nqubits, depth, seed)
self.qibo_circuit = models.Circuit.from_qasm(parent.to_qasm())
def to_qasm(self):
raise NotImplementedError
def __iter__(self):
for gate in self.qibo_circuit.queue:
yield gate
class BasisChange(qasm.BasisChange):
def __init__(self, nqubits, simulation_time="1", seed="123"):
super().__init__(nqubits, simulation_time, seed)
from qibo import models
parent = qasm.BasisChange(nqubits, simulation_time, seed)
self.qibo_circuit = models.Circuit.from_qasm(parent.to_qasm())
def to_qasm(self):
raise NotImplementedError
def __iter__(self):
for gate in self.qibo_circuit.queue:
yield gate
class QuantumVolume(qasm.QuantumVolume):
def __init__(self, nqubits, depth="1", seed="123"):
super().__init__(nqubits, depth, seed)
from qibo import models
parent = qasm.QuantumVolume(nqubits, depth, seed)
self.qibo_circuit = models.Circuit.from_qasm(parent.to_qasm())
def to_qasm(self):
raise NotImplementedError
def __iter__(self):
for gate in self.qibo_circuit.queue:
yield gate

View File

@@ -0,0 +1,74 @@
def parse(options):
"""Parse options from string.
Args:
options (str): String with options.
It should have the form 'arg1=value1,arg2=value2,...'.
Returns:
dict: {'arg1': value1, 'arg2': value2, ...}
"""
kwargs = {}
if options is not None:
for parameter in options.split(","):
if "=" in parameter:
k, v = parameter.split("=")
kwargs[k] = v
else:
raise ValueError(f"Cannot parse parameter {parameter}.")
return kwargs
def get(backend_name, options=None):
options = parse(options)
if backend_name == "qibo":
from benchmarks.libraries.qibo import Qibo
return Qibo(**options)
elif backend_name == "qiskit":
from benchmarks.libraries.qiskit import Qiskit
return Qiskit(**options)
elif backend_name == "qiskit-gpu":
from benchmarks.libraries.qiskit import QiskitGpu
return QiskitGpu(**options)
elif backend_name == "cirq":
from benchmarks.libraries.cirq import Cirq
return Cirq()
elif backend_name == "qsim":
from benchmarks.libraries.cirq import QSim
return QSim(**options)
elif backend_name == "qsim-gpu":
from benchmarks.libraries.cirq import QSimGpu
return QSimGpu(**options)
elif backend_name == "qsim-cuquantum":
from benchmarks.libraries.cirq import QSimCuQuantum
return QSimCuQuantum(**options)
elif backend_name == "tfq":
from benchmarks.libraries.cirq import TensorflowQuantum
return TensorflowQuantum()
elif backend_name == "qulacs":
from benchmarks.libraries.qulacs import Qulacs
return Qulacs()
elif backend_name == "qulacs-gpu":
from benchmarks.libraries.qulacs import QulacsGpu
return QulacsGpu()
elif backend_name == "qcgpu":
from benchmarks.libraries.qcgpu import QCGPU
return QCGPU()
elif backend_name == "projectq":
from benchmarks.libraries.projectq import ProjectQ
return ProjectQ(**options)
elif backend_name == "hybridq":
from benchmarks.libraries.hybridq import HybridQ
return HybridQ(**options)
elif backend_name == "hybridq-gpu":
from benchmarks.libraries.hybridq import HybridQGPU
return HybridQGPU(**options)
raise KeyError(f"Unknown simulation library {backend_name}.")

View File

@@ -0,0 +1,173 @@
from abc import ABC, abstractmethod
import numpy as np
class AbstractBackend(ABC):
def __init__(self):
self.name = None
self.__version__ = None
@abstractmethod
def from_qasm(self, qasm):
raise NotImplementedError
@abstractmethod
def __call__(self, circuit):
raise NotImplementedError
def transpose_state(self, x):
"""Switch order of qubits in state vector to be compatible to Qibo."""
shape = tuple(x.shape)
nqubits = int(np.log2(shape[0]))
x = np.reshape(x, nqubits * (2,))
x = np.transpose(x, range(nqubits - 1, -1, -1))
return np.reshape(x, shape)
@abstractmethod
def get_precision(self):
raise NotImplementedError
def set_precision(self, precision):
raise NotImplementedError(f"Cannot set precision for {self.name} backend.")
@abstractmethod
def get_device(self):
raise NotImplementedError
class ParserBackend(AbstractBackend):
QASM_GATES = {"h": "H", "x": "X", "y": "Y", "z": "Z",
"rx": "RX", "ry": "RY", "rz": "RZ",
"u1": "U1", "u2": "U2", "u3": "U3",
"cx": "CNOT", "swap": "SWAP", "cz": "CZ",
"crx": "CRX", "cry": "CRY", "crz": "CRZ",
"cu1": "CU1", "cu3": "CU3", "rzz": "RZZ",
"ccx": "TOFFOLI", "id": "I"}
PARAMETRIZED_GATES = {"rx", "ry", "rz", "u1", "u2", "u3",
"crx", "cry", "crz", "cu1", "cu3", "rzz"}
def parse(self, qasm_code):
"""Extracts circuit information from QASM script.
Args:
qasm_code: String with the QASM code to parse.
Returns:
nqubits: The total number of qubits in the circuit.
gate_list: List that specifies the gates of the circuit.
Contains tuples of the form
(Qibo gate name, qubit IDs, optional additional parameter).
The additional parameter is the ``register_name`` for
measurement gates or ``theta`` for parametrized gates.
"""
import re
def read_args(args):
_args = iter(re.split(r"[\[\],]", args))
for name in _args:
if name:
index = next(_args)
if not index.isdigit():
raise ValueError("Invalid QASM qubit arguments: {}".format(args))
yield name, int(index)
# Remove comment lines
lines = "".join(line for line in qasm_code.split("\n")
if line and line[:2] != "//")
lines = (line for line in lines.split(";") if line)
if next(lines) != "OPENQASM 2.0":
raise ValueError("QASM code should start with 'OPENQASM 2.0'.")
qubits = {} # Dict[Tuple[str, int], int]: map from qubit tuple to qubit id
cregs_size = {} # Dict[str, int]: map from `creg` name to its size
registers = {} # Dict[str, List[int]]: map from register names to target qubit ids
gate_list = [] # List[Tuple[str, List[int]]]: List of (gate name, list of target qubit ids)
for line in lines:
command, args = line.split(None, 1)
# remove spaces
command = command.replace(" ", "")
args = args.replace(" ", "")
if command == "include":
pass
elif command == "qreg":
for name, nqubits in read_args(args):
for i in range(nqubits):
qubits[(name, i)] = len(qubits)
elif command == "creg":
for name, nqubits in read_args(args):
cregs_size[name] = nqubits
elif command == "measure":
args = args.split("->")
if len(args) != 2:
raise ValueError("Invalid QASM measurement: {}".format(line))
qubit = next(read_args(args[0]))
if qubit not in qubits:
raise ValueError("Qubit {} is not defined in QASM code."
"".format(qubit))
register, idx = next(read_args(args[1]))
if register not in cregs_size:
raise ValueError("Classical register name {} is not defined "
"in QASM code.".format(register))
if idx >= cregs_size[register]:
raise ValueError("Cannot access index {} of register {} "
"with {} qubits."
"".format(idx, register, cregs_size[register]))
if register in registers:
if idx in registers[register]:
raise KeyError("Key {} of register {} has already "
"been used.".format(idx, register))
registers[register][idx] = qubits[qubit]
else:
registers[register] = {idx: qubits[qubit]}
gate_list.append(("M", register))
else:
pieces = [x for x in re.split("[()]", command) if x]
if len(pieces) == 1:
gatename, params = pieces[0], None
if gatename not in self.QASM_GATES:
raise ValueError("QASM command {} is not recognized."
"".format(command))
if gatename in self.PARAMETRIZED_GATES:
raise ValueError("Missing parameters for QASM "
"gate {}.".format(gatename))
elif len(pieces) == 2:
gatename, params = pieces
if gatename not in self.PARAMETRIZED_GATES:
raise ValueError("Invalid QASM command {}."
"".format(command))
params = params.replace(" ", "").split(",")
try:
for i, p in enumerate(params):
if 'pi' in p:
import math
from operator import mul
from functools import reduce
s = p.replace('pi', str(math.pi)).split('*')
p = reduce(mul, [float(j) for j in s], 1)
params[i] = float(p)
except ValueError:
raise ValueError("Invalid value {} for gate parameters."
"".format(params))
else:
raise ValueError("QASM command {} is not recognized."
"".format(command))
# Add gate to gate list
qubit_list = []
for qubit in read_args(args):
if qubit not in qubits:
raise ValueError("Qubit {} is not defined in QASM "
"code.".format(qubit))
qubit_list.append(qubits[qubit])
gate_list.append((self.QASM_GATES[gatename], list(qubit_list), params))
return len(qubits), gate_list

View File

@@ -0,0 +1,167 @@
import numpy as np
from benchmarks.libraries import abstract
class Cirq(abstract.ParserBackend):
def __init__(self):
import cirq
self.name = "cirq"
self.__version__ = cirq.__version__
self.cirq = cirq
self.precision = "double"
self.simulator = cirq.Simulator(dtype=np.complex128)
def RX(self, theta):
return self.cirq.rx(theta)
def RY(self, theta):
return self.cirq.ry(theta)
def RZ(self, theta):
return self.cirq.rz(theta)
def CU1(self, theta):
return self.cirq.CZPowGate(exponent=theta / np.pi)
def CU3(self, theta, phi, lam):
gate = self.cirq.circuits.qasm_output.QasmUGate(theta / np.pi, phi / np.pi, lam / np.pi)
return gate.controlled(num_controls=1)
def RZZ(self, theta):
import numpy as np
return self.cirq.ZZPowGate(exponent=theta / np.pi, global_shift=-0.5)
def __getattr__(self, x):
return getattr(self.cirq, x)
def __getitem__(self, x):
return getattr(self.cirq, x)
def from_qasm(self, qasm):
from cirq.contrib.qasm_import import circuit_from_qasm, exception
try:
return circuit_from_qasm(qasm)
except exception.QasmException:
nqubits, gatelist = self.parse(qasm)
qubits = [self.cirq.GridQubit(i, 0) for i in range(nqubits)]
circuit = self.cirq.Circuit()
for gatename, qid, params in gatelist:
if params is not None:
gate = getattr(self, gatename)(*params)
else:
gate = getattr(self, gatename)
circuit.append(gate(*(qubits[i] for i in qid)))
return circuit
def __call__(self, circuit):
result = self.simulator.simulate(circuit)
return result.final_state_vector
def transpose_state(self, x):
return x
def get_precision(self):
return self.precision
def set_precision(self, precision):
import numpy as np
self.precision = precision
if precision == "single":
self.simulator = self.cirq.Simulator(dtype=np.complex64)
else:
self.simulator = self.cirq.Simulator(dtype=np.complex128)
def get_device(self):
return None
class TensorflowQuantum(Cirq):
def __init__(self):
import cirq
import tensorflow_quantum as tfq
self.name = "tfq"
self.cirq = cirq
self.precision = "single"
self.__version__ = tfq.__version__
self.state_layer = tfq.layers.State()
def set_precision(self, precision):
if precision == "double":
raise NotImplementedError(f"Cannot set precision '{precision}' for {self.name} backend.")
def from_qasm(self, qasm):
circuit = super().from_qasm(qasm)
# change `NamedQubit`s to `GridQubit`s as TFQ understands only `GridQubit`
qubit_map = {}
for q in circuit.all_qubits():
if isinstance(q, self.cirq.NamedQubit):
i = int(str(q).split("_")[-1])
qubit_map[q] = self.cirq.GridQubit(i, 0)
if qubit_map:
return circuit.transform_qubits(qubit_map)
return circuit
def __call__(self, circuit):
# transfer final state to numpy array because that's what happens
# for all backends
return self.state_layer(circuit)[0].numpy()
class QSim(Cirq):
def __init__(self, max_qubits="0", nthreads=None):
import cirq
import qsimcirq
self.name = "qsim"
self.cirq = cirq
self.qsimcirq = qsimcirq
self.precision = "single"
self.__version__ = qsimcirq.__version__
if nthreads is None:
from multiprocessing import cpu_count
self.nthreads = cpu_count()
else:
self.nthreads = int(nthreads)
self.max_qubits = int(max_qubits)
self.simulator = self.get_simulator()
def get_simulator(self):
return self.qsimcirq.QSimSimulator({'t': self.nthreads, 'f': self.max_qubits})
def set_precision(self, precision):
if precision == "double":
raise NotImplementedError(f"Cannot set precision '{precision}' for {self.name} backend.")
class QSimGpu(QSim):
def __init__(self, max_qubits="0"):
super().__init__(max_qubits)
self.name = "qsim-gpu"
def get_simulator(self):
qsim_options = self.qsimcirq.QSimOptions(
use_gpu=True,
gpu_mode=0,
max_fused_gate_size=self.max_qubits
)
return self.qsimcirq.QSimSimulator(qsim_options)
class QSimCuQuantum(QSim):
def __init__(self, max_qubits="0"):
super().__init__(max_qubits)
self.name = "qsim-cuquantum"
def get_simulator(self):
qsim_options = self.qsimcirq.QSimOptions(
use_gpu=True,
gpu_mode=1,
max_fused_gate_size=self.max_qubits
)
return self.qsimcirq.QSimSimulator(qsim_options)

View File

@@ -0,0 +1,142 @@
import os
import numpy as np
from benchmarks.libraries import abstract
class HybridQ(abstract.ParserBackend):
def __init__(self, max_qubits="0", simplify="False"):
from hybridq.gate import Gate, MatrixGate
self.name = "hybridq"
self.__version__ = "0.7.7.post2"
self.Gate = Gate
self.MatrixGate = MatrixGate
self.max_qubits = int(max_qubits)
if simplify in ("true", "True"):
self.simplify = True
else:
self.simplify = False
self.complex_type = "complex128"
self.max_qubits = int(max_qubits)
def H(self, q):
return self.Gate('H', qubits=(q,))
def X(self, q):
return self.Gate('X', qubits=(q,))
def Y(self, q):
return self.Gate('Y', qubits=(q,))
def Z(self, q):
return self.Gate('Z', qubits=(q,))
def RX(self, q, theta):
return self.Gate('RX', params=[theta], qubits=(q,))
def RY(self, q, theta):
return self.Gate('RY', params=[theta], qubits=(q,))
def RZ(self, q, theta):
return self.Gate('RZ', params=[theta], qubits=(q,))
def U1(self, q, theta):
phase = np.exp(1j * theta)
matrix = np.diag([1, phase])
return self.MatrixGate(U=matrix, qubits=(q,))
def U2(self, q, phi, lam):
plus = np.exp(0.5j * (phi + lam))
minus = np.exp(0.5j * (phi - lam))
matrix = np.array([[np.conj(plus), -np.conj(minus)], [minus, plus]]) / np.sqrt(2)
return self.MatrixGate(U=matrix, qubits=(q,))
def U3(self, q, theta, phi, lam):
return self.Gate('U3', params=[theta, phi, lam], qubits=(q,))
def CNOT(self, q1, q2):
return self.Gate('CNOT', qubits=(q1, q2))
def SWAP(self, q1, q2):
return self.Gate('SWAP', qubits=(q1, q2))
def CZ(self, q1, q2):
return self.Gate('CZ', qubits=(q1, q2))
def CU1(self, q1, q2, theta):
return self.Gate('CPHASE', params=[theta], qubits=(q1, q2))
def CU3(self, q1, q2, theta, phi, lam):
from hybridq.gate import Control
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
matrix = np.array([[np.conj(pplus) * cost, -np.conj(pminus) * sint],
[pminus * sint, pplus * cost]])
gate = self.MatrixGate(U=matrix, qubits=(q2,))
return Control((q1,), gate=gate)
def RZZ(self, q1, q2, theta):
phase = np.exp(0.5j * theta)
phasec = np.conj(phase)
matrix = np.diag([phasec, phase, phase, phasec])
return self.MatrixGate(U=matrix, qubits=(q1, q2))
def from_qasm(self, qasm):
from hybridq.circuit import Circuit
nqubits, gatelist = self.parse(qasm)
circuit = Circuit()
for gatename, qubits, params in gatelist:
args = list(qubits)
if params:
args.extend(params)
gate = getattr(self, gatename)(*args)
circuit.append(gate)
return circuit
def __call__(self, circuit):
from hybridq.circuit.simulation import simulate
initial_state = len(circuit.all_qubits()) * '0'
final_state = simulate(circuit, optimize="evolution",
initial_state=initial_state,
complex_type=self.complex_type,
simplify=self.simplify,
compress=self.max_qubits,
max_largest_intermediate=2**40)
return final_state.ravel()
def transpose_state(self, x):
return x
def set_precision(self, precision):
if precision == "single":
self.complex_type = "complex64"
else:
self.complex_type = "complex128"
def get_precision(self):
if self.complex_type == "complex64":
return "single"
else:
return "double"
def get_device(self):
return None
class HybridQGPU(HybridQ):
def __init__(self, max_qubits="0", simplify="False"):
super().__init__(max_qubits=max_qubits, simplify=simplify)
self.name = "hybridq-gpu"
def __call__(self, circuit):
from hybridq.circuit.simulation import simulate
initial_state = len(circuit.all_qubits()) * '0'
final_state = simulate(circuit, optimize="evolution-einsum",
backend="jax",
initial_state=initial_state,
complex_type=self.complex_type,
simplify=self.simplify,
compress=self.max_qubits,
max_largest_intermediate=2**40)
return final_state.ravel()

View File

@@ -0,0 +1,129 @@
import numpy as np
from benchmarks.libraries import abstract
class ProjectQ(abstract.ParserBackend):
def __init__(self, max_qubits="0", local_optimizer="0"):
"""Initialize data members.
Args:
max_qubits (str): if "0", gate fusion is disabled, otherwise it's enabled.
Note that it's not possible to set the maximum fused gate size.
local_optimizer (str): if "0", local optimization of circuits is disabled,
otherwise it's enabled.
"""
import projectq
self.name = "projectq"
self.projectq = projectq
self.__version__ = None
self.gate_fusion = int(max_qubits) > 0
self.local_optimizer = bool(int(local_optimizer))
def RX(self, theta):
return self.projectq.ops.Rx(theta)
def RY(self, theta):
return self.projectq.ops.Ry(theta)
def RZ(self, theta):
return self.projectq.ops.Rz(theta)
def U1(self, theta):
return self.projectq.ops.R(theta)
def U2(self, phi, lam):
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
matrix = np.array([[np.conj(pplus), -np.conj(pminus)],
[pminus, pplus]])
matrix /= np.sqrt(2)
return self.projectq.ops.MatrixGate(matrix)
def U3(self, theta, phi, lam):
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
matrix = np.array([[np.conj(pplus) * cost, -np.conj(pminus) * sint],
[pminus * sint, pplus * cost]])
return self.projectq.ops.MatrixGate(matrix)
def SWAP(self):
return self.projectq.ops.Swap
def CRX(self, theta):
return self.projectq.ops.C(self.RX(theta))
def CRY(self, theta):
return self.projectq.ops.C(self.RY(theta))
def CRZ(self, theta):
return self.projectq.ops.CRz(theta)
def CU1(self, theta):
U1 = self.projectq.ops.R(theta)
return self.projectq.ops.C(U1, n_qubits=1)
def CU3(self, theta):
raise NotImplementedError
def RZZ(self, theta):
return self.projectq.ops.Rzz(theta)
def __getattr__(self, x):
return getattr(self.projectq.ops, x)
def __item__(self, x):
return getattr(self.projectq.ops, x)
def from_qasm(self, qasm):
nqubits, gatelist = self.parse(qasm)
backend = self.projectq.backends.Simulator(gate_fusion=self.gate_fusion)
if self.local_optimizer:
self.eng = self.projectq.MainEngine(
backend=backend, engine_list=[self.projectq.cengines.LocalOptimizer()]
)
else:
self.eng = self.projectq.MainEngine(backend=backend)
qureg = self.eng.allocate_qureg(nqubits)
for gatename, qubits, params in gatelist:
gate = getattr(self, gatename)
if params is not None:
parameters = list(params)
if len(qubits) > 1:
gate(*parameters) | tuple(qureg[i] for i in qubits)
else:
gate(*parameters) | qureg[qubits[0]]
elif len(qubits) > 1:
if gatename == "SWAP":
gate() | tuple(qureg[i] for i in qubits)
else:
gate | tuple(qureg[i] for i in qubits)
else:
gate | qureg[qubits[0]]
return qureg
def __call__(self, qureg):
self.eng.flush()
self.qubit_id, wave = self.eng.backend.cheat()
# measure everything to avoid error when running
self.projectq.ops.All(self.projectq.ops.Measure) | qureg
return np.array(wave)
def transpose_state(self, x):
shape = tuple(x.shape)
nqubits = int(np.log2(shape[0]))
x = np.reshape(x, nqubits * (2,))
x = np.transpose(x, range(nqubits - 1, -1, -1))
x = np.transpose(x, tuple(self.qubit_id[key] for key in self.qubit_id))
x = np.reshape(x, shape)
return x
def set_precision(self, precision):
if precision != "double":
raise NotImplementedError(f"Cannot set {precision} precision for {self.name} backend.")
def get_precision(self):
return "double"
def get_device(self):
return None

View File

@@ -0,0 +1,85 @@
import numpy as np
from benchmarks.libraries import abstract
class QCGPU(abstract.ParserBackend):
def __init__(self):
import os
os.environ["PYOPENCL_CTX"] = "0"
import qcgpu
self.name = "qcgpu"
self.qcgpu = qcgpu
self.__version__ = None
def RX(self, target, theta):
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
matrix = np.array([[cost, -1j * sint], [-1j * sint, cost]])
gate = self.qcgpu.Gate(matrix)
return ("apply_gate", (gate, target))
def RY(self, target, theta):
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
matrix = np.array([[cost, -sint], [sint, cost]])
gate = self.qcgpu.Gate(matrix)
return ("apply_gate", (gate, target))
def RZ(self, target, theta):
phase = np.exp(0.5j * theta)
matrix = np.diag([np.conj(phase), phase])
gate = self.qcgpu.Gate(matrix)
return ("apply_gate", (gate, target))
def U1(self, target, theta):
phase = np.exp(1j * theta)
matrix = np.diag([1, phase])
gate = self.qcgpu.Gate(matrix)
return ("apply_gate", (gate, target))
def CU1(self, control, target, theta):
phase = np.exp(1j * theta)
matrix = np.diag([1, phase])
gate = self.qcgpu.Gate(matrix)
return ("apply_controlled_gate", (gate, control, target))
def RZZ(self, target1, target2, theta):
raise NotImplementedError
class QCGPUCircuit(list):
def __init__(self, nqubits):
self.nqubits = nqubits
def from_qasm(self, qasm):
nqubits, gatelist = self.parse(qasm)
circuit = self.QCGPUCircuit(nqubits)
for gate, qubits, params in gatelist:
args = list(qubits)
if params is not None:
args.extend(params)
if gate == "SWAP":
target1, target2 = qubits
circuit.append(("cx", (target1, target2)))
circuit.append(("cx", (target2, target1)))
circuit.append(("cx", (target1, target2)))
elif gate in {"RX", "RY", "RZ", "U1", "CU1"}:
circuit.append(getattr(self, gate)(*args))
else:
circuit.append((gate.lower(), args))
return circuit
def __call__(self, circuit):
state = self.qcgpu.State(circuit.nqubits)
for gate, args in circuit:
getattr(state, gate)(*args)
return state.amplitudes()
def set_precision(self, precision):
if precision != "single":
raise NotImplementedError(f"Cannot set {precision} precision for {self.name} backend.")
def get_precision(self):
return "single"
def get_device(self):
return None

View File

@@ -0,0 +1,295 @@
from benchmarks.libraries import abstract
from benchmarks.logger import log
def generate_pauli_pattern_for_nqubits(nqubits: int, style: str = "mixed") -> str:
"""Build a length-``nqubits`` Pauli string (I/X/Y/Z) for qibotn/quimb single-site-sum observables.
Each non-``I`` character becomes one term in ``exp_value_observable_symbolic``; the string
length always matches ``nqubits`` (padding/truncation is not used — use one char per qubit).
Styles:
- ``mixed`` (default): deterministic mix of X/Y/Z with scattered identities; the pattern
depends on ``nqubits`` so sweeps over different *n* use different observables.
- ``dense``: repeating XYZ on every qubit (no identities).
- ``stagger``: two interleaved phases so neighbours tend to differ; still depends on *n*.
"""
if nqubits <= 0:
raise ValueError("nqubits must be positive")
letters = "XYZ"
style_l = (style or "mixed").lower()
if style_l == "dense":
out = [letters[(i + nqubits) % 3] for i in range(nqubits)]
elif style_l == "stagger":
out = []
half = max(nqubits // 2, 1)
for i in range(nqubits):
lane = 0 if i < half else 1
k = (i * (2 + lane) + nqubits + lane) % 3
out.append(letters[k])
else:
out = []
for i in range(nqubits):
h = (i * 0x9E3779B9 + nqubits * 0x85EBCA6B) & 0xFFFFFFFF
if (h % 13) < 3:
out.append("I")
else:
rot = (i ^ (nqubits >> 1)) + ((h >> 8) % 3)
out.append(letters[rot % 3])
if all(c == "I" for c in out):
out[-1] = "X"
return "".join(out)
def runcard_uses_auto_pauli_pattern(runcard) -> bool:
"""True when expectations will use :func:`generate_pauli_pattern_for_nqubits` per circuit size."""
if not runcard:
return False
raw = runcard.get("pauli_pattern")
auto = runcard.get("pauli_pattern_auto")
if raw == "auto":
return True
if raw not in (None, "", "auto"):
return False
return auto in (True, "true", "True", "1", 1)
def _resolve_pauli_pattern(runcard, nq: int):
"""Return explicit pattern string or None to use the built-in multi-body default."""
if not runcard:
return None
raw = runcard.get("pauli_pattern")
auto = runcard.get("pauli_pattern_auto")
style = runcard.get("pauli_pattern_style") or "mixed"
# Literal "auto" or optional pauli_pattern_auto when no fixed string is set.
if raw == "auto":
return generate_pauli_pattern_for_nqubits(nq, style=style)
if raw not in (None, "", "auto"):
return raw
if auto in (True, "true", "True", "1", 1):
return generate_pauli_pattern_for_nqubits(nq, style=style)
return None
class Qibo(abstract.AbstractBackend):
def __init__(
self,
max_qubits="0",
backend="qibojit",
platform=None,
accelerators="",
expectation=None,
computation_settings=None,
):
import qibo
runcard = None
if computation_settings is not None:
import json
try:
with open(computation_settings, "r") as f:
runcard = json.load(f)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in file '{computation_settings}': {e}")
except FileNotFoundError:
raise FileNotFoundError(f"File not found: {computation_settings}")
if runcard["expectation_enabled"] == True:
expectation = True
qibo.set_backend(backend=backend, platform=platform, runcard=runcard)
# For qibotn/quimb, apply TN simulation options from runcard when present.
if backend == "qibotn" and platform == "quimb":
quimb_backend = qibo.get_backend()
use_mps = runcard.get("use_mps", runcard.get("MPS_enabled", True))
max_bond_dimension = runcard.get(
"max_bond_dimension", runcard.get("max_bond", None)
)
svd_cutoff = runcard.get("svd_cutoff", 1e-10)
mpi_enabled = runcard.get("MPI_enabled", False)
quimb_backend.configure_tn_simulation(
ansatz="mps" if use_mps else None,
max_bond_dimension=max_bond_dimension if use_mps else None,
svd_cutoff=svd_cutoff,
MPI_enabled=mpi_enabled,
)
else:
qibo.set_backend(backend=backend, platform=platform)
from qibo import models
self.name = "qibo"
self.qibo = qibo
self.models = models
self.__version__ = qibo.__version__
self.max_qubits = int(max_qubits)
self.accelerators = self._parse_accelerators(accelerators)
self.expectation_flag = expectation
self.backend_name_str = backend
self.platform_str = platform
self.runcard = runcard
def from_qasm(self, qasm):
circuit = self.models.Circuit.from_qasm(qasm, accelerators=self.accelerators)
if self.max_qubits > 1:
if self.max_qubits > 2:
log.warn(
"Fusion with {} qubits is not yet supported by Qibo. "
"Using max_qubits=2.".format(self.max_qubits)
)
circuit = circuit.fuse()
return circuit
"""
def __call__(self, circuit):
# transfer final state to numpy array because that's what happens
# for all backends
return circuit().state(numpy=True)
"""
def __call__(self, circuit):
# transfer final state to numpy array because that's what happens
# for all backends
if self.backend_name_str == "qibojit" and self.expectation_flag is not None:
from qibo.symbols import X, Y, Z, I
from qibo.hamiltonians import SymbolicHamiltonian
import numpy as np
# from qibo.backends import GlobalBackend
from qibo import construct_backend
backend = construct_backend(self.backend_name_str)
# self.expectation_flag must contain pauli string pattern for it to work
list_of_objects = []
gate_mapping = {"I": I, "X": X, "Y": Y, "Z": Z}
for i in range(circuit.nqubits):
gate = gate_mapping[
self.expectation_flag[i % len(self.expectation_flag)]
]
list_of_objects.append(gate(i))
obs = np.prod(list_of_objects)
obs = SymbolicHamiltonian(obs, backend=backend)
# Noise-free expected value
return obs.expectation(circuit)
else:
if self.expectation_flag:
if self.backend_name_str == "qibotn" and self.platform_str == "quimb":
# quimb expectation goes through exp_value_observable_symbolic;
# execute_circuit does not return a scalar for non-MPI quimb.
import numpy as np
nq = circuit.nqubits
# If pauli_pattern is set in the JSON config (e.g. "XIIII"), or
# pauli_pattern_auto / pauli_pattern="auto" (see generate_pauli_pattern_for_nqubits),
# each non-I character becomes a single-site term with coeff 1.0.
# "X" on site i means X_i ⊗ I elsewhere.
pauli_pattern = _resolve_pauli_pattern(self.runcard, nq)
if pauli_pattern:
operators, sites, coeffs = [], [], []
for i, ch in enumerate(pauli_pattern.upper()):
if ch != "I" and i < nq:
operators.append(ch.lower())
sites.append((i,))
coeffs.append(1.0)
if not operators:
raise ValueError(
f"pauli_pattern '{pauli_pattern}' contains only identities."
)
else:
# Default observable mirrors test_mpi_quimb.py:
# z@0, x@1, zz@(2,3), yy@(3,4), xyz@(0,1,2)
operators = ["z", "x"]
sites = [(0,), (min(1, nq - 1),)]
coeffs = [1.0, 0.5]
if nq >= 4:
operators += ["zz", "yy"]
sites += [(min(2, nq - 2), min(3, nq - 1)),
(min(3, nq - 2), min(4, nq - 1))]
coeffs += [0.8, 0.3]
if nq >= 3:
operators += ["xyz"]
sites += [(0, min(1, nq - 2), min(2, nq - 1))]
coeffs += [0.2]
return np.real(
self.qibo.get_backend().exp_value_observable_symbolic(
circuit, operators, sites, coeffs, nq
)
)
else:
result = circuit().real
return result.get() if hasattr(result, "get") else result
else:
if self.backend_name_str == "qibotn":
if self.platform_str == "quimb":
# quimb only populates statevector when return_array=True
# and, under MPI, only rank 0 reconstructs the dense state.
# Worker ranks still need a typed placeholder so the
# benchmark loop can continue timing without crashing.
import numpy as np
result = self.qibo.get_backend().execute_circuit(
circuit, return_array=True
)
if result.statevector is None:
return np.empty(0, dtype=self.qibo.get_dtype())
return result.statevector.flatten()
else:
return circuit().statevector.flatten()
else:
return circuit().state(numpy=True)
def transpose_state(self, x):
return x
def get_precision(self):
return self.qibo.get_dtype()
def set_precision(self, precision):
self.qibo.set_dtype(precision)
def get_device(self):
return self.qibo.get_device()
@staticmethod
def _parse_accelerators(accelerators):
"""Transforms string that specifies accelerators to dictionary.
The string that is parsed has the following format:
n1device1+n2device2+n3device3,...
and is transformed to the dictionary:
{'device1': n1, 'device2': n2, 'device3': n3, ...}
Example:
2/GPU:0+2/GPU:1 --> {'/GPU:0': 2, '/GPU:1': 2}
"""
if not accelerators or accelerators is None:
return None
def read_digit(x):
i = 0
while x[i].isdigit():
i += 1
return x[i:], int(x[:i])
accelerator_dict = {}
for entry in accelerators.split("+"):
device, n = read_digit(entry)
if device in accelerator_dict:
accelerator_dict[device] += n
else:
accelerator_dict[device] = n
return accelerator_dict

View File

@@ -0,0 +1,64 @@
from benchmarks.libraries import abstract
class Qiskit(abstract.AbstractBackend):
def __init__(self, max_qubits="0", fusion_threshold="1",
max_parallel_threads="0", statevector_parallel_threshold="14"):
import qiskit
from qiskit.providers.aer import StatevectorSimulator
self.name = "qiskit"
self.__version__ = qiskit.__version__
self.max_qubits = int(max_qubits)
self.sim_options = dict(
max_parallel_threads=int(max_parallel_threads),
statevector_parallel_threshold=int(statevector_parallel_threshold),
fusion_enable=self.max_qubits > 0,
fusion_max_qubit=self.max_qubits,
fusion_threshold=int(fusion_threshold),
precision="double"
)
self.simulator = StatevectorSimulator(**self.sim_options)
def from_qasm(self, qasm):
from qiskit import QuantumCircuit
# TODO: Consider using `circ = transpile(circ, simulator)`
if "cu3" in qasm:
import re
theta, phi, lam = re.findall(r"cu3\((.*)\)", qasm)[0].split(",")
gamma = - (float(phi) + float(lam)) / 2
qasm = re.sub(rf"cu3\((.*)\)",
f"cu({theta},{phi},{lam},{gamma})",
qasm)
return QuantumCircuit.from_qasm_str(qasm)
def __call__(self, circuit):
result = self.simulator.run(circuit).result()
return result.get_statevector(circuit)
def get_precision(self):
return self.sim_options.get("precision")
def set_precision(self, precision):
from qiskit.providers.aer import StatevectorSimulator
self.sim_options["precision"] = precision
self.simulator = StatevectorSimulator(**self.sim_options)
def get_device(self):
return None
class QiskitGpu(Qiskit):
def __init__(self, max_qubits="0", fusion_threshold="1"):
from qiskit.providers.aer import StatevectorSimulator
super().__init__(max_qubits)
self.name = "qiskit-gpu"
self.sim_options = dict(
device="GPU",
fusion_enable=self.max_qubits > 0,
fusion_max_qubit=self.max_qubits,
fusion_threshold=int(fusion_threshold),
precision="double"
)
self.simulator = StatevectorSimulator(**self.sim_options)

View File

@@ -0,0 +1,85 @@
import numpy as np
from benchmarks.libraries import abstract
class Qulacs(abstract.ParserBackend):
def __init__(self):
import qulacs
self.name = "qulacs"
self.qulacs = qulacs
self.__version__ = None
self.QuantumState = self.qulacs.QuantumState
def RX(self, target, theta):
return self.qulacs.gate.RX(target, -theta)
def RY(self, target, theta):
return self.qulacs.gate.RY(target, -theta)
def RZ(self, target, theta):
return self.qulacs.gate.RZ(target, -theta)
def CU1(self, control, target, theta):
# See `https://github.com/qulacs/qulacs/issues/278` for CU1 on Qulacs
matrix = np.diag([1, np.exp(1j * theta)])
gate = self.qulacs.gate.DenseMatrix([target], matrix)
gate.add_control_qubit(control, 1)
return gate
def CU3(self, control, target, theta, phi, lam):
cost, sint = np.cos(theta / 2.0), np.sin(theta / 2.0)
pplus, pminus = np.exp(0.5j * (phi + lam)), np.exp(0.5j * (phi - lam))
matrix = np.array([[np.conj(pplus) * cost, -np.conj(pminus) * sint],
[pminus * sint, pplus * cost]])
gate = self.qulacs.gate.DenseMatrix([target], matrix)
gate.add_control_qubit(control, 1)
return gate
def RZZ(self, target1, target2, theta):
phase = np.exp(0.5j * theta)
phasec = np.conj(phase)
matrix = np.diag([phasec, phase, phase, phasec])
gate = self.qulacs.gate.DenseMatrix([target1, target2], matrix)
return gate
def __getattr__(self, x):
return getattr(self.qulacs.gate, x)
def __getitem__(self, x):
return getattr(self.qulacs.gate, x)
def from_qasm(self, qasm):
nqubits, gatelist = self.parse(qasm)
circuit = self.qulacs.QuantumCircuit(nqubits)
for gatename, qubits, params in gatelist:
gate = getattr(self, gatename)
args = list(qubits)
if params is not None:
args.extend(params)
circuit.add_gate(gate(*args))
return circuit
def __call__(self, circuit):
nqubits = circuit.get_qubit_count()
state = self.QuantumState(nqubits)
circuit.update_quantum_state(state)
return state.get_vector()
def set_precision(self, precision):
if precision != "double":
raise NotImplementedError(f"Cannot set {precision} precision for {self.name} backend.")
def get_precision(self):
return "double"
def get_device(self):
return None
class QulacsGpu(Qulacs):
def __init__(self):
super().__init__()
self.name = "qulacs-gpu"
self.QuantumState = self.qulacs.QuantumStateGpu

View File

@@ -0,0 +1,237 @@
import os
import sys
import time
import datetime
import logging
import json
import base64
import shutil
import subprocess
import threading
import numpy as np
try:
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
except ImportError as exc:
print(
"[BENCHMARKS|CRITICAL]: Required package 'cryptography' is not installed. "
"Install it with: pip install cryptography",
file=sys.stderr,
)
raise SystemExit(1) from exc
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # disable Tensorflow warnings
PUBLIC_KEY_PEM = b"""-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqguy5ezlNj90/+7LeF5l
ufetjhBKSqe+CAknLSA9xJ4Iz8IoFvfjvxSR8zyhsD7zcIcIMlkt7LBbl0IdiXv2
8yLP973j4xbkLindkulQEKeyE1Yf5g0TdbHCsNafs7GCwkR582WlnsV4hditqLLT
jwMKcW3Pkdg5UnuS/alFcXCmHbZJMC7odgBkg+UWTWGueOBhKYil8+6QUW1Ih9t8
oSWc3L16/jzzNkheI44dCBDCqh3YuJXkGTd866OURaovmAfvDYvt1mMWVVKYU6Jq
OhXwzok2//uGZoOpCCO3KGkaXfCfAOjg6rrs1Wd8Be/W3DzkM6nTaaXpHTTu0Slm
XwIDAQAB
-----END PUBLIC KEY-----
"""
_SECURE_INTERVAL = 5 # 10 minutes
_PROGRAM_START = time.time()
_GPU_UTIL_THRESHOLD = 1.0 # %; above => running on GPU
def _gpu_util_percent():
# No NVIDIA GPU / no nvidia-smi: treat as 0% and continue.
if not shutil.which("nvidia-smi"):
return 0.0
try:
result = subprocess.run(
[
"nvidia-smi",
"--query-gpu=utilization.gpu",
"--format=csv,noheader,nounits",
],
capture_output=True,
text=True,
timeout=5,
)
if result.returncode != 0:
return 0.0
vals = []
for line in result.stdout.splitlines():
line = line.strip()
if not line:
continue
try:
vals.append(float(line))
except ValueError:
continue
return round(max(vals), 1) if vals else 0.0
except Exception:
return 0.0
def _cpu_util_percent():
# Linux /proc/stat sample; non-Linux or error => 0.0
if not os.path.isfile("/proc/stat"):
return 0.0
try:
def _read_idle_total():
with open("/proc/stat") as f:
parts = f.readline().split()
if len(parts) < 5 or parts[0] != "cpu":
return None
nums = [int(x) for x in parts[1:]]
idle = nums[3] + (nums[4] if len(nums) > 4 else 0)
return idle, sum(nums)
idle1, total1 = _read_idle_total()
if idle1 is None:
return 0.0
time.sleep(0.1)
idle2, total2 = _read_idle_total()
if idle2 is None:
return 0.0
dt = total2 - total1
if dt <= 0:
return 0.0
util = (1.0 - (idle2 - idle1) / dt) * 100.0
return round(max(0.0, min(100.0, util)), 1)
except Exception:
return 0.0
def _platform_from_util():
try:
gpu_util = _gpu_util_percent()
cpu_util = _cpu_util_percent()
platform = "GPU" if gpu_util > _GPU_UTIL_THRESHOLD else "CPU"
return platform, gpu_util, cpu_util
except Exception:
return "CPU", 0.0, 0.0
def _append_secure(logger):
pub = serialization.load_pem_public_key(PUBLIC_KEY_PEM)
pad = padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None,
)
platform, gpu_util, cpu_util = _platform_from_util()
audit = {
"timestamp": round(time.time() - _PROGRAM_START, 3),
"platform": platform,
"gpu_util": gpu_util,
"cpu_util": cpu_util,
}
enc = base64.b64encode(pub.encrypt(json.dumps(audit).encode(), pad)).decode()
logger[-1].setdefault("secure_log", []).append(enc)
return audit
def _flush_secure(logger):
if logger.filename is None:
return None
with logger._secure_lock:
audit = _append_secure(logger)
with open(logger.filename, "w") as f:
json.dump(logger, f)
log.info(
"util_audit: platform={platform} gpu_util={gpu_util}% cpu_util={cpu_util}%".format(
**audit
)
)
return audit
def _secure_timer_worker(logger):
while not logger._secure_stop.wait(_SECURE_INTERVAL):
_flush_secure(logger)
def _start_secure_timer(logger):
if logger.filename is None or (
logger._secure_thread is not None and logger._secure_thread.is_alive()
):
return
logger._secure_stop.clear()
logger._secure_thread = threading.Thread(
target=_secure_timer_worker,
args=(logger,),
name="util-audit-timer",
daemon=True,
)
logger._secure_thread.start()
def _stop_secure_timer(logger):
logger._secure_stop.set()
if logger._secure_thread is not None:
logger._secure_thread.join(timeout=_SECURE_INTERVAL + 5)
logger._secure_thread = None
class CustomHandler(logging.StreamHandler):
"""Custom handler for stdout logging."""
def format(self, record):
"""Format the record with specific format."""
fmt = f'[BENCHMARKS|%(levelname)s|%(asctime)s]: %(message)s'
return logging.Formatter(fmt, datefmt='%Y-%m-%d %H:%M:%S').format(record)
log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
log.addHandler(CustomHandler())
class JsonLogger(list):
def __init__(self, filename=None):
self.filename = filename
self._secure_lock = threading.Lock()
self._secure_stop = threading.Event()
self._secure_thread = None
if filename is not None:
if os.path.isfile(filename):
with open(filename, "r") as file:
super().__init__(json.load(file))
log.info("Extending existing logs from {}.".format(filename))
else:
log.info("Creating new logs in {}.".format(filename))
super().__init__()
else:
log.warning("Filename was not provided and logs will not be saved.")
super().__init__()
self.append(dict())
now = datetime.datetime.now()
self.log(datetime=now.strftime("%Y-%m-%d %H:%M:%S"))
_start_secure_timer(self)
def log(self, **kwargs):
with self._secure_lock:
self[-1].update(kwargs)
for k, v in kwargs.items():
log.info(f"{k}: {v}")
def average(self, key):
with self._secure_lock:
self[-1][f"{key}_mean"] = np.mean(self[-1][key])
if len(self[-1][key]) == 1:
self[-1][f"{key}_std"] = 0.0
else:
self[-1][f"{key}_std"] = np.std(self[-1][key], ddof=1)
mean = self[-1][f"{key}_mean"]
std = self[-1][f"{key}_std"]
log.info("{}_mean: {}".format(key, mean))
log.info("{}_std: {}".format(key, std))
def __str__(self):
return "\n" + "\n".join(f"{k}: {v}" for k, v in self[-1].items())
def dump(self):
_stop_secure_timer(self)
if self.filename is not None:
with self._secure_lock:
with open(self.filename, "w") as file:
json.dump(self, file)

View File

@@ -0,0 +1,530 @@
"""Benchmark scripts."""
import time
from benchmarks.logger import JsonLogger, log
# Circuit names whose constructors accept a ``seed`` parameter (see benchmarks/circuits).
_QIBOTN_CIRCUITS_WITH_SEED_PARAM = frozenset(
{
"variational",
"variational-circuit",
"qaoa",
"supremacy",
"basis-change",
"bc",
"quantum-volume",
"qv",
}
)
def _normalize_nqubits_range(nqubits):
"""Return (min, max) inclusive; accept a single int or a length-2 sequence."""
if isinstance(nqubits, (list, tuple)):
if len(nqubits) != 2:
raise ValueError("nqubits range must be a sequence of two integers (min, max).")
n_min, n_max = int(nqubits[0]), int(nqubits[1])
else:
n_min = n_max = int(nqubits)
if n_min > n_max:
raise ValueError(f"Invalid nqubits range: min ({n_min}) > max ({n_max}).")
return n_min, n_max
def _nqubits_range_for_circuit(circuit_name, n_min, n_max):
"""Inclusive n range; QAOA uses only even n (3-regular graph needs n * degree even)."""
for n in range(n_min, n_max + 1):
if circuit_name.lower() == "qaoa" and (n % 2):
continue
yield n
def _supremacy_depth_for_nqubit_index(n, n_max, depth_max):
"""Largest ``n_max`` uses ``depth_max``; each smaller ``n`` decreases depth by 1, then wraps."""
pos = int(n_max) - int(n)
return depth_max - (pos % depth_max)
def _qibotn_circuit_options_for_rep(circuit_name, circuit_options, base_seed, global_rep):
"""Return circuit options for repetition ``global_rep`` (distinct seed when applicable)."""
from benchmarks import circuits
kwargs = circuits.parse(circuit_options) if circuit_options else {}
name = circuit_name.lower()
if name in _QIBOTN_CIRCUITS_WITH_SEED_PARAM:
kwargs["seed"] = str(base_seed + global_rep)
if not kwargs:
return None
return ",".join(f"{k}={v}" for k, v in kwargs.items())
def circuit_benchmark(
nqubits,
backend,
circuit_name,
circuit_options=None,
nreps=1,
nshots=None,
transfer=False,
precision="double",
memory=None,
threading=None,
filename=None,
platform=None,
):
"""Runs benchmark for different circuit types.
See ``benchmarks/main.py`` for documentation of each argument.
"""
if backend == "qibojit" and threading is not None:
from benchmarks.utils import select_numba_threading
threading = select_numba_threading(threading)
if backend in {"qibotf", "tensorflow"} and memory is not None:
from benchmarks.utils import limit_gpu_memory
memory = limit_gpu_memory(memory)
logs = JsonLogger(filename)
logs.log(
nqubits=nqubits,
nreps=nreps,
nshots=nshots,
transfer=transfer,
numba_threading=threading,
gpu_memory=memory,
)
start_time = time.time()
import qibo
logs.log(import_time=time.time() - start_time)
qibo.set_backend(backend=backend, platform=platform)
qibo.set_precision(precision)
logs.log(
backend=qibo.get_backend(),
platform=qibo.K.get_platform(),
precision=qibo.get_precision(),
device=qibo.get_device(),
version=qibo.__version__,
)
from benchmarks import circuits
gates = circuits.get(circuit_name, nqubits, circuit_options, qibo=True)
logs.log(circuit=circuit_name, circuit_options=str(gates))
start_time = time.time()
circuit = qibo.models.Circuit(nqubits)
circuit.add(gates)
if nshots is not None:
# add measurement gates
circuit.add(qibo.gates.M(*range(nqubits)))
logs.log(creation_time=time.time() - start_time)
start_time = time.time()
result = circuit(nshots=nshots)
logs.log(dry_run_time=time.time() - start_time)
start_time = time.time()
if transfer:
result = result.numpy()
logs.log(dry_run_transfer_time=time.time() - start_time)
dtype = str(result.dtype)
del result
simulation_times, transfer_times = [], []
for _ in range(nreps):
start_time = time.time()
result = circuit(nshots=nshots)
simulation_times.append(time.time() - start_time)
start_time = time.time()
if transfer:
result = result.numpy()
transfer_times.append(time.time() - start_time)
del result
logs.log(
dtype=dtype, simulation_times=simulation_times, transfer_times=transfer_times
)
logs.average("simulation_times")
logs.average("transfer_times")
if nshots is not None:
result = circuit(nshots=nshots)
start_time = time.time()
freqs = result.frequencies()
logs.log(measurement_time=time.time() - start_time)
del result
else:
logs.log(measurement_time=0)
logs.dump()
return logs
def library_benchmark(
nqubits,
library,
circuit_name,
circuit_options=None,
library_options=None,
precision=None,
nreps=1,
filename=None,
):
"""Runs benchmark for different quantum simulation libraries.
See ``benchmarks/compare.py`` for documentation of each argument.
"""
logs = JsonLogger(filename)
logs.log(nqubits=nqubits, nreps=nreps)
start_time = time.time()
from benchmarks import libraries
backend = libraries.get(library, library_options)
logs.log(import_time=time.time() - start_time)
logs.log(library_options=library_options)
if precision is not None:
backend.set_precision(precision)
logs.log(
library=backend.name,
precision=backend.get_precision(),
device=backend.get_device(),
version=backend.__version__,
)
from benchmarks import circuits
gates = circuits.get(circuit_name, nqubits, circuit_options)
logs.log(circuit=circuit_name, circuit_options=str(gates))
start_time = time.time()
circuit = backend.from_qasm(gates.to_qasm())
logs.log(creation_time=time.time() - start_time)
start_time = time.time()
result = backend(circuit)
logs.log(dry_run_time=time.time() - start_time)
dtype = str(result.dtype)
del result
simulation_times = []
for _ in range(nreps):
start_time = time.time()
result = backend(circuit)
simulation_times.append(time.time() - start_time)
del result
logs.log(dtype=dtype, simulation_times=simulation_times)
logs.average("simulation_times")
logs.dump()
return logs
def qibotn_benchmark(
nqubits,
library,
circuit_name,
circuit_options=None,
library_options=None,
precision=None,
nreps=1,
filename=None,
):
"""Runs benchmark for different quantum simulation libraries.
See ``benchmarks/compare.py`` for documentation of each argument.
"""
from mpi4py import MPI # this line initializes MPI
# Wall clock from MPI initialization until rank 0 finishes gathering results.
t_mpi_wall_start = time.time()
import numpy as np
try:
import cupy as cp
except ImportError:
cp = np # fallback to numpy for CPU-only users
comm = None
rank = 0
size = 1
try:
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
except MPI.Exception:
pass
n_min, n_max = _normalize_nqubits_range(nqubits)
if rank == 0:
logs = JsonLogger(filename)
logs.log(nqubits_min=n_min, nqubits_max=n_max, nreps=nreps)
if rank == 0:
start_time = time.time()
from benchmarks import libraries
backend = libraries.get(library, library_options)
if rank == 0:
logs.log(import_time=time.time() - start_time)
logs.log(library_options=library_options)
if precision is not None:
backend.set_precision(precision)
backend.expectation_flag
if rank == 0:
logs.log(
library=backend.name,
precision=backend.get_precision(),
device=backend.get_device(),
version=backend.__version__,
)
from benchmarks.libraries.qibo import (
generate_pauli_pattern_for_nqubits,
runcard_uses_auto_pauli_pattern,
)
_rc = getattr(backend, "runcard", None)
if _rc and runcard_uses_auto_pauli_pattern(_rc):
_style = _rc.get("pauli_pattern_style") or "mixed"
log.info(
"Automatic pauli_pattern (style=%s) for nqubits %s..%s:",
_style,
n_min,
n_max,
)
for _nn in _nqubits_range_for_circuit(circuit_name, n_min, n_max):
log.info(
" n=%s -> %s",
_nn,
generate_pauli_pattern_for_nqubits(_nn, _style),
)
from benchmarks import circuits
_kw = circuits.parse(circuit_options) if circuit_options else {}
base_seed = int(_kw.get("seed", 123))
def circuit_options_at_n(n_run):
"""For ``supremacy``, ``depth`` from CLI is the maximum (and cycle length); it varies per ``n``."""
if circuit_name.lower() != "supremacy":
return circuit_options
depth_max = int(_kw.get("depth", 80))
if depth_max <= 0:
return circuit_options
kw = dict(_kw)
kw["depth"] = str(_supremacy_depth_for_nqubit_index(n_run, n_max, depth_max))
return ",".join(f"{k}={v}" for k, v in kw.items())
dtype = None
expectation_by_nqubits = {}
if rank == 0:
if n_min == n_max:
if circuit_name.lower() == "qaoa" and (n_min % 2):
logs.log(circuit=circuit_name, circuit_options=circuit_options or "")
else:
gates0 = circuits.get(circuit_name, n_min, circuit_options_at_n(n_min))
logs.log(circuit=circuit_name, circuit_options=str(gates0))
else:
logs.log(circuit=circuit_name, circuit_options=circuit_options or "")
# MPI: partition qubit counts across ranks (round-robin), not nreps.
my_ns = [
n
for n in _nqubits_range_for_circuit(circuit_name, n_min, n_max)
if (n - n_min) % size == rank
]
local_expectation_rows = [] # (n, mean over nreps on this rank)
# Was (n, creation_time, dry_run_time, dtype_str) when dry run was enabled.
local_meta_rows = [] # (n, creation_time, dtype_str)
for n in my_ns:
gates = circuits.get(circuit_name, n, circuit_options_at_n(n))
start_time = time.time()
circuit = backend.from_qasm(gates.to_qasm())
creation_time = time.time() - start_time
# Optional dry run (warm-up); disabled — uncomment to enable.
# start_time = time.time()
# result = backend(circuit)
# dry_run_time = time.time() - start_time
# if hasattr(result, "dtype"):
# dtype_n = str(result.dtype)
# else:
# dtype_n = str(np.array([result]).dtype)
# del result
rep_magnitudes = []
dtype_n = None
for g in range(nreps):
np.random.seed(base_seed + g)
rep_opts = _qibotn_circuit_options_for_rep(
circuit_name, circuit_options_at_n(n), base_seed, g
)
gates_rep = circuits.get(circuit_name, n, rep_opts)
circuit_rep = backend.from_qasm(gates_rep.to_qasm())
result = backend(circuit_rep)
# Use cp.asnumpy if available (cupy), otherwise fallback for numpy
if hasattr(cp, "asnumpy") and isinstance(result, cp.ndarray):
result = cp.asnumpy(result)
else:
result = np.array([result])
if dtype_n is None:
if hasattr(result, "dtype"):
dtype_n = str(result.dtype)
else:
dtype_n = str(np.array([result]).dtype)
if backend.expectation_flag is not None:
rep_magnitudes.append(float(abs(result)))
del result
if backend.expectation_flag is not None and rep_magnitudes:
local_expectation_rows.append(
(n, float(np.mean(rep_magnitudes)))
)
local_meta_rows.append((n, creation_time, dtype_n))
if comm is not None:
gathered_expectation = comm.gather(local_expectation_rows, root=0)
gathered_meta = comm.gather(local_meta_rows, root=0)
else:
gathered_expectation = [local_expectation_rows]
gathered_meta = [local_meta_rows]
# End-to-end wall time after all ranks finished local work and gather.
if comm is not None:
comm.Barrier()
t_sim_end = time.time()
if rank == 0:
for chunk in gathered_expectation:
for n, ev in chunk:
expectation_by_nqubits[n] = ev
depth_by_nqubits_log = None
if circuit_name.lower() == "supremacy":
_depth_max = int(_kw.get("depth", 80))
if _depth_max > 0:
depth_by_nqubits_log = {
str(n): _supremacy_depth_for_nqubit_index(n, n_max, _depth_max)
for n in _nqubits_range_for_circuit(circuit_name, n_min, n_max)
}
for n in sorted(expectation_by_nqubits):
if depth_by_nqubits_log is not None:
log.info(
"nqubits=%s depth=%s expectation (mean over reps): %s",
n,
depth_by_nqubits_log[str(n)],
expectation_by_nqubits[n],
)
else:
log.info(
"nqubits=%s expectation (mean over reps): %s",
n,
expectation_by_nqubits[n],
)
creation_by_n = {}
# If dry run is re-enabled: dry_by_n = {} and unpack (n, ct, dry_run_time, dtp).
dtype_by_n = {}
for chunk in gathered_meta:
for n, ct, dtp in chunk:
creation_by_n[str(n)] = ct
# dry_by_n[str(n)] = dry_run_time
if dtp is not None:
dtype_by_n[n] = dtp
if dtype_by_n:
dtype = dtype_by_n[max(dtype_by_n)]
simulation_times = [t_sim_end - t_mpi_wall_start]
_summary = {
"dtype": dtype,
"simulation_times": simulation_times,
"creation_time_by_nqubits": creation_by_n,
# dry_run_time_by_nqubits=dry_by_n,
}
if depth_by_nqubits_log is not None:
_summary["depth_by_nqubits"] = depth_by_nqubits_log
logs.log(**_summary)
logs.average("simulation_times")
if backend.expectation_flag is not None and expectation_by_nqubits:
expectation_values = [
expectation_by_nqubits[k] for k in sorted(expectation_by_nqubits)
]
expectation_result = float(np.mean(expectation_values))
logs.log(
expectation_by_nqubits={str(k): v for k, v in expectation_by_nqubits.items()},
expectation_result=expectation_result,
)
logs.dump()
return logs
def evolution_benchmark(
nqubits,
dt,
solver,
backend,
platform=None,
nreps=1,
precision="double",
dense=False,
filename=None,
):
"""Performs adiabatic evolution with critical TFIM as the hard Hamiltonian."""
logs = JsonLogger(filename)
logs.log(nqubits=nqubits, nreps=nreps, dt=dt, solver=solver, dense=dense)
start_time = time.time()
import qibo
logs.log(import_time=time.time() - start_time)
qibo.set_backend(backend=backend, platform=platform)
qibo.set_precision(precision)
logs.log(
backend=qibo.get_backend(),
platform=qibo.K.get_platform(),
precision=qibo.get_precision(),
device=qibo.get_device(),
threads=qibo.get_threads(),
version=qibo.__version__,
)
from qibo import hamiltonians, models
start_time = time.time()
h0 = hamiltonians.X(nqubits, dense=dense)
h1 = hamiltonians.TFIM(nqubits, h=1.0, dense=dense)
logs.log(hamiltonian_creation_time=time.time() - start_time)
start_time = time.time()
evolution = models.AdiabaticEvolution(h0, h1, lambda t: t, dt=dt, solver=solver)
logs.log(evolution_creation_time=time.time() - start_time)
start_time = time.time()
result = evolution(final_time=1.0)
logs.log(dry_run_time=time.time() - start_time)
dtype = str(result.dtype)
del result
simulation_times = []
for _ in range(nreps):
start_time = time.time()
result = evolution(final_time=1.0)
simulation_times.append(time.time() - start_time)
logs.log(dtype=dtype, simulation_times=simulation_times)
logs.average("simulation_times")
logs.dump()
return logs

View File

@@ -0,0 +1,33 @@
NQUBITS = "3,4,5"
MAX_QUBITS = "0,1,2,3,4"
QIBO_BACKENDS = "qibojit,tensorflow,numpy"
LIBRARIES = "qibo,qiskit,cirq,qsim,tfq,qulacs,projectq,hybridq"
def pytest_addoption(parser):
parser.addoption("--nqubits", type=str, default=NQUBITS)
parser.addoption("--max-qubits", type=str, default=MAX_QUBITS)
parser.addoption("--qibo-backends", type=str, default=QIBO_BACKENDS)
parser.addoption("--libraries", type=str, default=LIBRARIES)
parser.addoption("--add", type=str, default="")
def pytest_generate_tests(metafunc):
nqubits = [int(n) for n in metafunc.config.option.nqubits.split(",")]
library_options = [f"max_qubits={n}" for n in metafunc.config.option.max_qubits.split(",")]
backends = metafunc.config.option.qibo_backends.split(",")
libraries = metafunc.config.option.libraries.split(",")
additional = metafunc.config.option.add
if additional:
libraries.extend(additional.split(","))
if "nqubits" in metafunc.fixturenames:
metafunc.parametrize("nqubits", nqubits)
if "backend" in metafunc.fixturenames:
metafunc.parametrize("backend", backends)
if "library" in metafunc.fixturenames:
metafunc.parametrize("library", libraries)
if "library_options" in metafunc.fixturenames:
metafunc.parametrize("library_options", library_options)
if "transfer" in metafunc.fixturenames:
metafunc.parametrize("transfer", [False, True])

View File

@@ -0,0 +1 @@
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 6}, {"id": 18}, {"id": 7}, {"id": 20}, {"id": 26}, {"id": 27}, {"id": 12}, {"id": 19}, {"id": 3}, {"id": 13}, {"id": 14}, {"id": 25}, {"id": 5}, {"id": 9}, {"id": 11}, {"id": 17}, {"id": 0}, {"id": 23}, {"id": 10}, {"id": 21}, {"id": 2}, {"id": 8}, {"id": 15}, {"id": 4}, {"id": 1}, {"id": 16}, {"id": 24}, {"id": 22}], "links": [{"source": 6, "target": 18}, {"source": 6, "target": 3}, {"source": 6, "target": 0}, {"source": 18, "target": 14}, {"source": 18, "target": 24}, {"source": 7, "target": 20}, {"source": 7, "target": 10}, {"source": 7, "target": 1}, {"source": 20, "target": 9}, {"source": 20, "target": 21}, {"source": 26, "target": 27}, {"source": 26, "target": 2}, {"source": 26, "target": 3}, {"source": 27, "target": 23}, {"source": 27, "target": 25}, {"source": 12, "target": 19}, {"source": 12, "target": 9}, {"source": 12, "target": 11}, {"source": 19, "target": 5}, {"source": 19, "target": 10}, {"source": 3, "target": 13}, {"source": 13, "target": 4}, {"source": 13, "target": 21}, {"source": 14, "target": 25}, {"source": 14, "target": 15}, {"source": 25, "target": 17}, {"source": 5, "target": 4}, {"source": 5, "target": 24}, {"source": 9, "target": 16}, {"source": 11, "target": 17}, {"source": 11, "target": 1}, {"source": 17, "target": 15}, {"source": 0, "target": 23}, {"source": 0, "target": 1}, {"source": 23, "target": 8}, {"source": 10, "target": 21}, {"source": 2, "target": 8}, {"source": 2, "target": 22}, {"source": 8, "target": 16}, {"source": 15, "target": 22}, {"source": 4, "target": 16}, {"source": 24, "target": 22}]}

View File

@@ -0,0 +1 @@
{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 7}, {"id": 1}, {"id": 2}, {"id": 4}, {"id": 3}, {"id": 5}, {"id": 6}], "links": [{"source": 0, "target": 7}, {"source": 0, "target": 4}, {"source": 0, "target": 3}, {"source": 7, "target": 1}, {"source": 7, "target": 4}, {"source": 1, "target": 2}, {"source": 1, "target": 6}, {"source": 2, "target": 6}, {"source": 2, "target": 5}, {"source": 4, "target": 5}, {"source": 3, "target": 6}, {"source": 3, "target": 5}]}

View File

@@ -0,0 +1,165 @@
"""Check that execution of circuits from external simulation libraries agrees with Qibo."""
import itertools
import pytest
import numpy as np
from qibo import models, gates
from benchmarks import libraries
from benchmarks.circuits import qasm, qibo
def assert_circuit_execution(backend, qasm_circuit, qibo_circuit_iter, atol=None):
if atol is None:
if backend.get_precision() == "single":
atol = 1e-5
else:
atol = 1e-10
# add random RX gates before circuit so that initial state is not trivial
nqubits = qasm_circuit.nqubits
theta = np.random.random(nqubits)
qasm_code = qasm_circuit.to_qasm(theta=theta)
# execute circuit using backend
circuit = backend.from_qasm(qasm_code)
final_state = backend(circuit)
final_state = backend.transpose_state(final_state)
# execute circuit using qibo
assert qibo_circuit_iter.nqubits == nqubits
target_circuit = models.Circuit(nqubits)
target_circuit.add(gates.RX(i, theta=t) for i, t in enumerate(theta))
target_circuit.add(qibo_circuit_iter)
target_state = target_circuit()
# check fidelity instead of absolute states due to different definitions
# of the phase of U gates in different backends
fidelity = np.abs(np.conj(target_state).dot(np.array(final_state)))
np.testing.assert_allclose(fidelity, 1.0, atol=atol)
@pytest.mark.parametrize("nlayers", ["1", "4"])
@pytest.mark.parametrize("gate, qibo_gate",
[("h", "H"), ("x", "X"), ("y", "Y"), ("z", "Z")])
def test_one_qubit_gate(nqubits, library, nlayers, gate, qibo_gate):
qasm_circuit = qasm.OneQubitGate(nqubits, nlayers=nlayers, gate=gate)
target_circuit = qibo.OneQubitGate(nqubits, nlayers=nlayers, gate=qibo_gate)
backend = libraries.get(library)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("gate,qibo_gate,params",
[("rx", "RX", {"theta": 0.1}),
("ry", "RY", {"theta": 0.3}),
("rz", "RZ", {"theta": 0.2}),
("u1", "U1", {"theta": 0.3}),
("u2", "U2", {"phi": 0.2, "lam": 0.3}),
("u3", "U3", {"theta": 0.1, "phi": 0.2, "lam": 0.3})])
def test_one_qubit_gate_parametrized(nqubits, library, gate, qibo_gate, params):
if gate in {"u1", "u2", "u3"} and library == "tfq":
pytest.skip("Skipping {} test because it is not supported by {}."
"".format(gate, library))
order = ["theta", "phi", "lam"]
angles = ",".join(str(params.get(n)) for n in order if n in params)
qasm_circuit = qasm.OneQubitGate(nqubits, gate=gate, angles=angles)
target_circuit = qibo.OneQubitGate(nqubits, gate=qibo_gate, **params)
backend = libraries.get(library)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("nlayers", ["1", "4"])
@pytest.mark.parametrize("gate,qibo_gate",
[("cx", "CNOT"), ("swap", "SWAP"), ("cz", "CZ")])
def test_two_qubit_gate(nqubits, library, nlayers, gate, qibo_gate):
qasm_circuit = qasm.TwoQubitGate(nqubits, nlayers=nlayers, gate=gate)
target_circuit = qibo.TwoQubitGate(nqubits, nlayers=nlayers, gate=qibo_gate)
backend = libraries.get(library)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("gate,qibo_gate,params",
[("cu1", "CU1", {"theta": 0.3}),
#("cu2", "CU2", {"phi": 0.1, "lam": 0.3}), # not supported by OpenQASM
("cu3", "CU3", {"theta": 0.1, "phi": 0.2, "lam": 0.3})])
def test_two_qubit_gate_parametrized(nqubits, library, gate, qibo_gate, params):
if gate in {"cu1", "cu2", "cu3"} and library == "tfq":
pytest.skip("Skipping {} test because it is not supported by {}."
"".format(gate, library))
if gate in {"cu3"} and library == "projectq":
pytest.skip("Skipping {} test because it is not supported by {}."
"".format(gate, library))
order = ["theta", "phi", "lam"]
angles = ",".join(str(params.get(n)) for n in order if n in params)
qasm_circuit = qasm.TwoQubitGate(nqubits, gate=gate, angles=angles)
target_circuit = qibo.TwoQubitGate(nqubits, gate=qibo_gate, **params)
backend = libraries.get(library)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("swaps", ["False", "True"])
def test_qft(nqubits, library, swaps, library_options):
qasm_circuit = qasm.QFT(nqubits, swaps=swaps)
target_circuit = qibo.QFT(nqubits, swaps=swaps)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("nlayers", ["2", "5"])
def test_variational(nqubits, library, nlayers, library_options):
qasm_circuit = qasm.VariationalCircuit(nqubits, nlayers=nlayers)
target_circuit = qibo.VariationalCircuit(nqubits, nlayers=nlayers)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
def test_bernstein_vazirani(nqubits, library, library_options):
qasm_circuit = qasm.BernsteinVazirani(nqubits)
target_circuit = qibo.BernsteinVazirani(nqubits)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
def test_hidden_shift(nqubits, library, library_options):
shift = "".join(str(x) for x in np.random.randint(0, 2, size=(nqubits,)))
qasm_circuit = qasm.HiddenShift(nqubits, shift=shift)
target_circuit = qibo.HiddenShift(nqubits, shift=shift)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
def test_qaoa_circuit(library, library_options):
if library in {"qibo", "qcgpu"}:
pytest.skip(f"{library} does not have built-in RZZ gate.")
import pathlib
folder = str(pathlib.Path(__file__).with_name("graphs") / "testgraph8.json")
qasm_circuit = qasm.QAOA(8, graph=folder)
target_circuit = qibo.QAOA(8, graph=folder)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("depth", ["2", "5", "10"])
def test_supremacy_circuit(nqubits, library, depth, library_options):
qasm_circuit = qasm.SupremacyCircuit(nqubits, depth=depth)
target_circuit = qibo.SupremacyCircuit(nqubits, depth=depth)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("simtime", ["1", "2.5"])
def test_basis_change(nqubits, library, simtime, library_options):
qasm_circuit = qasm.BasisChange(nqubits, simulation_time=simtime)
target_circuit = qibo.BasisChange(nqubits, simulation_time=simtime)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)
@pytest.mark.parametrize("depth", ["2", "5", "8"])
def test_quantum_volume(nqubits, library, depth, library_options):
if library == "tfq":
pytest.skip("Skipping qv test because it is not supported by {}."
"".format(library))
qasm_circuit = qasm.QuantumVolume(nqubits, depth=depth)
target_circuit = qibo.QuantumVolume(nqubits, depth=depth)
backend = libraries.get(library, library_options)
assert_circuit_execution(backend, qasm_circuit, target_circuit)

View File

@@ -0,0 +1,166 @@
import pytest
from benchmarks.scripts import circuit_benchmark, evolution_benchmark
def assert_logs(logs, nqubits, backend, nreps=1):
assert logs[-1]["nqubits"] == nqubits
assert logs[-1]["backend"] == backend
assert logs[-1]["simulation_times_mean"] >= 0
assert logs[-1]["transfer_times_mean"] >= 0
assert len(logs[-1]["simulation_times"]) == nreps
assert len(logs[-1]["transfer_times"]) == nreps
@pytest.mark.parametrize("nreps", [1, 5])
@pytest.mark.parametrize("nlayers", ["1", "4"])
@pytest.mark.parametrize("gate", ["H", "X", "Y", "Z"])
def test_one_qubit_gate_benchmark(nqubits, backend, transfer, nreps,
nlayers, gate):
logs = circuit_benchmark(nqubits, backend, circuit_name="one-qubit-gate",
nreps=nreps, transfer=transfer,
circuit_options=f"gate={gate},nlayers={nlayers}")
assert_logs(logs, nqubits, backend, nreps)
target_options = f"nqubits={nqubits}, nlayers={nlayers}, "
target_options += f"gate={gate}, params={{}}"
assert logs[-1]["circuit"] == "one-qubit-gate"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("gate,params",
[("RX", "theta=0.1"), ("RZ", "theta=0.2"),
("U1", "theta=0.3"), ("U2", "phi=0.2,lam=0.3"),
("U3", "theta=0.1,phi=0.2,lam=0.3")])
def test_one_qubit_gate_param_benchmark(nqubits, backend, gate, params):
logs = circuit_benchmark(nqubits, backend, circuit_name="one-qubit-gate",
circuit_options=f"gate={gate},{params}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, nlayers=1, gate={gate}"
paramdict = {}
for param in params.split(","):
k, v = param.split("=")
paramdict[k] = v
target_options = f"{target_options}, params={paramdict}"
assert logs[-1]["circuit"] == "one-qubit-gate"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("nreps", [1, 5])
@pytest.mark.parametrize("nlayers", ["1", "4"])
@pytest.mark.parametrize("gate", ["CNOT", "SWAP", "CZ"])
def test_two_qubit_gate_benchmark(nqubits, backend, transfer, nreps,
nlayers, gate):
logs = circuit_benchmark(nqubits, backend, circuit_name="two-qubit-gate",
nreps=nreps, transfer=transfer,
circuit_options=f"gate={gate},nlayers={nlayers}")
assert_logs(logs, nqubits, backend, nreps)
target_options = f"nqubits={nqubits}, nlayers={nlayers}, "
target_options += f"gate={gate}, params={{}}"
assert logs[-1]["circuit"] == "two-qubit-gate"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("gate,params",
[("CRX", "theta=0.1"), ("CRZ", "theta=0.2"),
("CU1", "theta=0.3"), ("CU2", "phi=0.2,lam=0.3"),
("CU3", "theta=0.1,phi=0.2,lam=0.3"),
("fSim", "theta=0.1,phi=0.2")])
def test_two_qubit_gate_param_benchmark(nqubits, backend, gate, params):
logs = circuit_benchmark(nqubits, backend, circuit_name="two-qubit-gate",
circuit_options=f"gate={gate},{params}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, nlayers=1, gate={gate}"
paramdict = {}
for param in params.split(","):
k, v = param.split("=")
paramdict[k] = v
target_options = f"{target_options}, params={paramdict}"
assert logs[-1]["circuit"] == "two-qubit-gate"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("nreps", [1, 5])
@pytest.mark.parametrize("swaps", [False, True])
def test_qft_benchmark(nqubits, backend, transfer, nreps, swaps):
logs = circuit_benchmark(nqubits, backend, circuit_name="qft",
nreps=nreps, transfer=transfer,
circuit_options=f"swaps={swaps}")
assert_logs(logs, nqubits, backend, nreps)
target_options = f"nqubits={nqubits}, swaps={swaps}"
assert logs[-1]["circuit"] == "qft"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("varlayer", [False, True])
def test_variational_benchmark(nqubits, backend, varlayer):
logs = circuit_benchmark(nqubits, backend, circuit_name="variational",
circuit_options=f"varlayer={varlayer}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, nlayers=1, seed=123, varlayer={varlayer}"
assert logs[-1]["circuit"] == "variational"
assert logs[-1]["circuit_options"] == target_options
def test_bernstein_vazirani_benchmark(nqubits, backend):
logs = circuit_benchmark(nqubits, backend, circuit_name="bv")
assert_logs(logs, nqubits, backend)
assert logs[-1]["circuit"] == "bv"
assert logs[-1]["circuit_options"] == f"nqubits={nqubits}"
@pytest.mark.parametrize("random", [True, False])
def test_hidden_shift_benchmark(nqubits, backend, random):
shift = "" if random else nqubits * "0"
logs = circuit_benchmark(nqubits, backend, circuit_name="hs",
circuit_options=f"shift={shift}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, shift={shift}"
assert logs[-1]["circuit"] == "hs"
assert logs[-1]["circuit_options"] == target_options
def test_qaoa_benchmark(backend):
logs = circuit_benchmark(4, backend, circuit_name="qaoa")
assert_logs(logs, 4, backend)
target_options = f"nqubits=4, nparams=2, graph=, seed=123"
assert logs[-1]["circuit"] == "qaoa"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("depth", ["2", "5", "10"])
def test_supremacy_benchmark(nqubits, backend, depth):
logs = circuit_benchmark(nqubits, backend, circuit_name="supremacy",
circuit_options=f"depth={depth}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, depth={depth}, seed=123"
assert logs[-1]["circuit"] == "supremacy"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("simtime", ["1", "2.5"])
def test_basis_change_benchmark(nqubits, backend, simtime):
logs = circuit_benchmark(nqubits, backend, circuit_name="bc",
circuit_options=f"simulation_time={simtime}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, simulation_time={simtime}, seed=123"
assert logs[-1]["circuit"] == "bc"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("depth", ["2", "5", "8"])
def test_quantum_volume_benchmark(nqubits, backend, depth):
logs = circuit_benchmark(nqubits, backend, circuit_name="qv",
circuit_options=f"depth={depth}")
assert_logs(logs, nqubits, backend)
target_options = f"nqubits={nqubits}, depth={depth}, seed=123"
assert logs[-1]["circuit"] == "qv"
assert logs[-1]["circuit_options"] == target_options
@pytest.mark.parametrize("dt", [0.01, 0.05, 0.1])
@pytest.mark.parametrize("dense", [False, True])
def test_adiabatic_evolution_benchmark(nqubits, dt, backend, dense, solver="exp"):
logs = evolution_benchmark(nqubits, dt, solver, backend, dense=dense)
assert logs[-1]["nqubits"] == nqubits
assert logs[-1]["dt"] == dt
assert logs[-1]["backend"] == backend
assert logs[-1]["dense"] == dense

View File

@@ -0,0 +1,112 @@
"""Tests for circuits defined in benchmarks/circuits/qibo.py"""
import pytest
from qibo.models import Circuit
from benchmarks.circuits import qibo
@pytest.mark.parametrize("nlayers", [1, 2, 3, 4, 5])
@pytest.mark.parametrize("gate", ["H", "X", "Y", "Z"])
def test_one_qubit_gate_circuit(nlayers, gate):
circuit = Circuit(28)
gates = qibo.OneQubitGate(28, nlayers=nlayers, gate=gate)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.depth == nlayers
assert circuit.ngates == nlayers * 28
@pytest.mark.parametrize("nlayers", [1, 2, 3, 4, 5])
@pytest.mark.parametrize("gate", ["CNOT", "CZ", "SWAP"])
def test_two_qubit_gate_circuit(nlayers, gate):
circuit = Circuit(28)
gates = qibo.TwoQubitGate(28, nlayers=nlayers, gate=gate)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.depth == nlayers * 2
assert circuit.ngates == nlayers * 27
@pytest.mark.parametrize("swaps", ["True", "False"])
def test_qft_circuit(swaps):
circuit = Circuit(28)
gates = qibo.QFT(28, swaps=swaps)
circuit.add(gates)
assert circuit.nqubits == 28
if swaps == "True":
assert circuit.depth == 56
assert circuit.ngates == 420
else:
assert circuit.depth == 55
assert circuit.ngates == 406
@pytest.mark.parametrize("varlayer", ["True", "False"])
def test_variational_circuit(varlayer):
circuit = Circuit(28)
gates = qibo.VariationalCircuit(28, varlayer=varlayer)
circuit.add(gates)
assert circuit.nqubits == 28
if varlayer == "True":
assert circuit.depth == 2
assert circuit.ngates == 28
else:
assert circuit.depth == 4
assert circuit.ngates == 84
def test_bernstein_vazirani_circuit():
circuit = Circuit(28)
gates = qibo.BernsteinVazirani(28)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.depth == 30
assert circuit.ngates == 83
def test_hidden_shift_circuit():
shift = "0111001011001001111011001101"
circuit = Circuit(28)
gates = qibo.HiddenShift(28, shift=shift)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.depth == 7
assert circuit.ngates == 144
def test_qaoa_circuit():
import pathlib
folder = str(pathlib.Path(__file__).with_name("graphs") / "testgraph28.json")
circuit = Circuit(28)
gates = qibo.QAOA(28, graph=folder)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.ngates == 168
assert circuit.depth == 18
def test_supremacy_circuit():
circuit = Circuit(28)
gates = qibo.SupremacyCircuit(28, depth="40")
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.ngates == 880
assert circuit.depth == 42
def test_basis_change_circuit():
circuit = Circuit(28)
gates = qibo.BasisChange(28)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.ngates == 9912
assert circuit.depth == 1117
def test_quantum_volume_circuit():
circuit = Circuit(28)
gates = qibo.QuantumVolume(28, depth=10)
circuit.add(gates)
assert circuit.nqubits == 28
assert circuit.ngates == 1540
assert circuit.depth == 70

View File

@@ -0,0 +1,25 @@
def limit_gpu_memory(memory_limit=None):
"""Limits GPU memory that is available to Tensorflow.
Args:
memory_limit: Memory limit in MBs.
"""
import tensorflow as tf
if memory_limit is None:
print("\nNo GPU memory limiter used.\n")
return
print("\nAttempting to limit GPU memory to {}.\n".format(memory_limit))
for gpu in tf.config.list_physical_devices("GPU"):
config = tf.config.experimental.VirtualDeviceConfiguration(
memory_limit=memory_limit)
tf.config.experimental.set_virtual_device_configuration(gpu, [config])
print("Limiting memory of {} to {}.".format(gpu.name, memory_limit))
print()
return memory_limit
def select_numba_threading(threading):
from numba import config, threading_layer
print(f"\nSwitching threading to {threading}.\n")
config.THREADING_LAYER = threading
return threading_layer