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,35 +1,57 @@
"""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
@@ -37,36 +59,32 @@ class QMatchaTeaBackend(QibotnBackend):
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"
@@ -79,25 +97,29 @@ class QMatchaTeaBackend(QibotnBackend):
""" """
# 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:
@@ -110,7 +132,6 @@ class QMatchaTeaBackend(QibotnBackend):
# 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