Files
qibo-final/qibojit-benchmarks/benchmarks/libraries/abstract.py
2026-05-19 17:19:36 +08:00

174 lines
7.2 KiB
Python

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