feat: integrating qmatchatea SimulationResult in the circuit execution

This commit is contained in:
MatteoRobbiati
2025-01-27 13:03:27 +01:00
parent 43ebd79cec
commit 508d5cd5e9
2 changed files with 71 additions and 50 deletions

View File

@@ -19,7 +19,7 @@ repos:
- id: isort - id: isort
args: ["--profile", "black"] args: ["--profile", "black"]
- repo: https://github.com/PyCQA/docformatter - repo: https://github.com/PyCQA/docformatter
rev: master rev: v1.7.5
hooks: hooks:
- id: docformatter - id: docformatter
additional_dependencies: [tomli] additional_dependencies: [tomli]

View File

@@ -1,103 +1,125 @@
"""Implementation of Quantum Matcha Tea backend""" """Implementation of Quantum Matcha Tea backend."""
from dataclasses import dataclass from dataclasses import dataclass
from qibo.config import raise_error
from qiskit import QuantumCircuit from qiskit import QuantumCircuit
from qibotn.backends.abstract import QibotnBackend from qibotn.backends.abstract import QibotnBackend
from qibo.config import raise_error
@dataclass @dataclass
class QMatchaTeaBackend(QibotnBackend): class QMatchaTeaBackend(QibotnBackend):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
import qiskit # pylint: disable=import-error
import qmatchatea # pylint: disable=import-error import qmatchatea # pylint: disable=import-error
import qiskit # pylint: disable=import-error import qtealeaves # pylint: disable=import-error
import qtealeaves # pylint: disable=import-error
self.qmatchatea = qmatchatea self.qmatchatea = qmatchatea
self.qiskit = qiskit self.qiskit = qiskit
self.qtleaves = qtealeaves self.qtleaves = qtealeaves
# Set default configurations # Set default configurations
self.configure_tn_simulation() self.configure_tn_simulation()
# TODO: update this function whenever ``set_device`` and ``set_precision`` # TODO: update this function whenever ``set_device`` and ``set_precision``
# are set (?) # are set (?)
self._setup_qmatchatea_backend() self._setup_qmatchatea_backend()
self._observables = self.qtleaves.observables.TNObservables()
@property
def observables(self):
"""Observables measured after TN execution."""
return self._observables
@observables.setter
def observables(self, observables: list):
"""Set the observables to be measured after TN execution.
It accepts a list
of objects among the ones proposed in ``qtealeaves.observables``.
"""
for obs in observables:
if isinstance(obs, self.qtleaves.observables.tnobase._TNObsBase):
self._observables = self.qtleaves.observables.TNObservables()
self._observables += obs
else:
raise TypeError("Expected an instance of TNObservables")
def execute_circuit( def execute_circuit(
self, circuit, initial_state=None, nshots=None, return_array=False self, circuit, initial_state=None, nshots=None, return_array=False
): ):
"""Preserve the Qibo execution interface, but return Quantum Matcha Tea
``SimulationResult`` object."""
# TODO: verify if the QCIO mechanism of matcha is supported by Fortran only # TODO: verify if the QCIO mechanism of matcha is supported by Fortran only
# as written in the docstrings or by Python too (see ``io_info`` argument of # as written in the docstrings or by Python too (see ``io_info`` argument of
# ``qmatchatea.interface.run_simulation`` function) # ``qmatchatea.interface.run_simulation`` function)
if initial_state is not None: if initial_state is not None:
raise_error( raise_error(
NotImplementedError, NotImplementedError,
f"Backend {self.name}-{self.platform} currently does not support initial state." f"Backend {self.name}-{self.platform} currently does not support initial state.",
) )
# TODO: do we want to keep it like this or we aim to implement a different # TODO: do we want to keep it like this or we aim to implement a different
# idea of "shots" here? # idea of "shots" here?
nshots = None
circuit = self._qibocirc_to_qiskitcirc(circuit) circuit = self._qibocirc_to_qiskitcirc(circuit)
run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False) run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False)
results = self.qmatchatea.run_simulation( results = self.qmatchatea.run_simulation(
circ = circuit, circ=circuit,
convergence_parameters = self.convergence_params, convergence_parameters=self.convergence_params,
transpilation_parameters = run_qk_params, transpilation_parameters=run_qk_params,
backend = self.qmatchatea_backend, backend=self.qmatchatea_backend,
observables=self._observables,
) )
# TODO: construct a proper TNResult object? # TODO: construct a proper TNResult in Qibo?
# It does not make sense to reconstruct QuantumState here! return results
return results.measure_probabilities
def configure_tn_simulation( def configure_tn_simulation(
self, self,
convergence_params = None, convergence_params=None,
ansatz: str = "MPS", ansatz: str = "MPS",
): ):
""" """Configure TN simulation given Quantum Matcha Tea interface.
Configure TN simulation given Quantum Matcha Tea interface.
Args: Args:
ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" ansatz (str): tensor network ansatz. It can be tree tensor network "TTN"
or Matrix Product States "MPS" (default). or Matrix Product States "MPS" (default).
convergence_params (qmatchatea.utils.QCConvergenceParameters): convergence_params (qmatchatea.utils.QCConvergenceParameters):
convergence parameters class adapted to the quantum computing convergence parameters class adapted to the quantum computing
execution. See https://baltig.infn.it/quantum_matcha_tea/py_api_quantum_matcha_tea/-/blob/master/qmatchatea/utils/utils.py?ref_type=heads#L540 execution. See https://baltig.infn.it/quantum_matcha_tea/py_api_quantum_matcha_tea/-/blob/master/qmatchatea/utils/utils.py?ref_type=heads#L540
for more instructions. If not passed, the default values proposed for more instructions. If not passed, the default values proposed
by Quantum Matcha Tea's authors are set. by Quantum Matcha Tea's authors are set.
""" """
# Set configurationsor defaults # Set configurationsor defaults
self.convergence_params = convergence_params or self.qmatchatea.QCConvergenceParameters() self.convergence_params = (
convergence_params or self.qmatchatea.QCConvergenceParameters()
)
self.ansatz = ansatz self.ansatz = ansatz
# Initializing the TNObservables according to qmatchatea
self.observables = self.qtleaves.observables.TNObservables()
def _setup_qmatchatea_backend(self): def _setup_qmatchatea_backend(self):
"""Configure qmatchatea QCBackend object.""" """Configure qmatchatea QCBackend object."""
self.qmatchatea_device = "cpu" if "CPU" in self.device else "gpu" if "GPU" in self.device else None self.qmatchatea_device = (
self.qmatchatea_precision = "C" if self.precision == "single" else "Z" if self.precision == "double" else "A" "cpu" if "CPU" in self.device else "gpu" if "GPU" in self.device else None
)
self.qmatchatea_precision = (
"C"
if self.precision == "single"
else "Z" if self.precision == "double" else "A"
)
# TODO: once MPI is available for Python, integrate it here # TODO: once MPI is available for Python, integrate it here
self.qmatchatea_backend = self.qmatchatea.QCBackend( self.qmatchatea_backend = self.qmatchatea.QCBackend(
backend = "PY", # The only alternative is Fortran, but we use Python here backend="PY", # The only alternative is Fortran, but we use Python here
precision = self.qmatchatea_precision, precision=self.qmatchatea_precision,
device = self.qmatchatea_device, device=self.qmatchatea_device,
ansatz = self.ansatz, ansatz=self.ansatz,
) )
def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> QuantumCircuit: def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> QuantumCircuit:
@@ -106,11 +128,10 @@ class QMatchaTeaBackend(QibotnBackend):
qasm_circuit = qibo_circuit.to_qasm() qasm_circuit = qibo_circuit.to_qasm()
qiskit_circuit = QuantumCircuit.from_qasm_str(qasm_circuit) qiskit_circuit = QuantumCircuit.from_qasm_str(qasm_circuit)
# Transpile the circuit to adapt it to the linear structure of the MPS, # Transpile the circuit to adapt it to the linear structure of the MPS,
# with the constraint of having only the gates basis_gates # with the constraint of having only the gates basis_gates
qiskit_circuit = self.qmatchatea.preprocessing.preprocess( qiskit_circuit = self.qmatchatea.preprocessing.preprocess(
qiskit_circuit, qiskit_circuit,
qk_params=self.qmatchatea.preprocessing.qk_transpilation_params() qk_params=self.qmatchatea.preprocessing.qk_transpilation_params(),
) )
return qiskit_circuit
return qiskit_circuit