From 773196fb2fd2fbe3d6ca6856290a4e1161aeb7b2 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 27 Jan 2025 12:26:30 +0100 Subject: [PATCH 01/41] refactor: constructing an abstract QiboTN backend and adapt quimb and cutensornet --- src/qibotn/backends/cutensornet.py | 17 +++-------------- src/qibotn/backends/quimb.py | 16 +++------------- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 1d38520..d4f1bcc 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -1,12 +1,13 @@ import numpy as np -from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error from qibo.result import QuantumState +from qibotn.backends.abstract import QibotnBackend + CUDA_TYPES = {} -class CuTensorNet(NumpyBackend): # pragma: no cover +class CuTensorNet(QibotnBackend): # pragma: no cover # CI does not test for GPU """Creates CuQuantum backend for QiboTN.""" @@ -77,22 +78,10 @@ class CuTensorNet(NumpyBackend): # pragma: no cover ), } - def apply_gate(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def assign_measurements(self, measurement_map, circuit_result): - raise_error(NotImplementedError, "Not implemented in QiboTN.") - def __del__(self): if hasattr(self, "cutn"): self.cutn.destroy(self.handle) - def set_precision(self, precision): - if precision != self.precision: - super().set_precision(precision) def cuda_type(self, dtype="complex64"): """Get CUDA Type. diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index df6488a..20223b3 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -2,8 +2,10 @@ from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error from qibo.result import QuantumState +from qibotn.backends.abstract import QibotnBackend -class QuimbBackend(NumpyBackend): + +class QuimbBackend(QibotnBackend): def __init__(self, runcard): super().__init__() @@ -36,18 +38,6 @@ class QuimbBackend(NumpyBackend): self.platform = "QuimbBackend" self.versions["quimb"] = self.quimb.__version__ - def apply_gate(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover - raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") - - def assign_measurements(self, measurement_map, circuit_result): - raise_error(NotImplementedError, "Not implemented in QiboTN.") - - def set_precision(self, precision): - if precision != self.precision: - super().set_precision(precision) def execute_circuit( self, circuit, initial_state=None, nshots=None, return_array=False From f1aec1f0bfc4fc215b869f3d8091fabe47674277 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 27 Jan 2025 12:27:32 +0100 Subject: [PATCH 02/41] feat: abstract qibotn backend --- src/qibotn/backends/abstract.py | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/qibotn/backends/abstract.py diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py new file mode 100644 index 0000000..6e8088d --- /dev/null +++ b/src/qibotn/backends/abstract.py @@ -0,0 +1,41 @@ +from abc import abstractmethod +from qibo.backends.numpy import NumpyBackend +from qibo.config import raise_error + + +DEFAULT_CONFIGURATION = { + "MPI_enabled": False, # TODO: cutensornet specific, TBRemoved + "NCCL_enabled": False, # TODO: cutensornet specific, TBRemoved + "expectation_enabled": False, + "pauli_string_pattern": None, + "MPS_enabled": False, + "gate_algo": None, + "mps_opts": None, +} + + + +class QibotnBackend(NumpyBackend): + + def __init__(self, runcard:dict = DEFAULT_CONFIGURATION): + super().__init__() + + def apply_gate(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def apply_gate_density_matrix(self, gate, state, nqubits): # pragma: no cover + raise_error(NotImplementedError, "QiboTN cannot apply gates directly.") + + def assign_measurements(self, measurement_map, circuit_result): + raise_error(NotImplementedError, "Not implemented in QiboTN.") + + def set_precision(self, precision): + if precision != self.precision: + super().set_precision(precision) + + @abstractmethod + def configure_tn_simulation(self, **config): + """Configure the TN simulation that will be performed.""" + pass + + From a979d978918a3303bc8a76f5bca5307e0daf9b93 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 27 Jan 2025 12:28:19 +0100 Subject: [PATCH 03/41] feat: drafting qmatchatea backend --- src/qibotn/backends/qmatchatea.py | 116 ++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/qibotn/backends/qmatchatea.py diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py new file mode 100644 index 0000000..ba40475 --- /dev/null +++ b/src/qibotn/backends/qmatchatea.py @@ -0,0 +1,116 @@ +"""Implementation of Quantum Matcha Tea backend""" + +from dataclasses import dataclass +from qiskit import QuantumCircuit + +from qibotn.backends.abstract import QibotnBackend +from qibo.config import raise_error + +@dataclass +class QMatchaTeaBackend(QibotnBackend): + + def __init__(self): + super().__init__() + import qmatchatea # pylint: disable=import-error + import qiskit # pylint: disable=import-error + import qtealeaves # pylint: disable=import-error + + self.qmatchatea = qmatchatea + self.qiskit = qiskit + self.qtleaves = qtealeaves + + + # Set default configurations + self.configure_tn_simulation() + # TODO: update this function whenever ``set_device`` and ``set_precision`` + # are set (?) + self._setup_qmatchatea_backend() + + + def execute_circuit( + self, circuit, initial_state=None, nshots=None, return_array=False + ): + + # 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 + # ``qmatchatea.interface.run_simulation`` function) + if initial_state is not None: + raise_error( + NotImplementedError, + 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 + # idea of "shots" here? + nshots = None + + circuit = self._qibocirc_to_qiskitcirc(circuit) + + run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False) + + results = self.qmatchatea.run_simulation( + circ = circuit, + convergence_parameters = self.convergence_params, + transpilation_parameters = run_qk_params, + backend = self.qmatchatea_backend, + ) + + # TODO: construct a proper TNResult object? + # It does not make sense to reconstruct QuantumState here! + return results.measure_probabilities + + + def configure_tn_simulation( + self, + convergence_params = None, + ansatz: str = "MPS", + ): + """ + Configure TN simulation given Quantum Matcha Tea interface. + + Args: + ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" + or Matrix Product States "MPS" (default). + convergence_params (qmatchatea.utils.QCConvergenceParameters): + 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 + for more instructions. If not passed, the default values proposed + by Quantum Matcha Tea's authors are set. + """ + + # Set configurationsor defaults + self.convergence_params = convergence_params or self.qmatchatea.QCConvergenceParameters() + self.ansatz = ansatz + + # Initializing the TNObservables according to qmatchatea + self.observables = self.qtleaves.observables.TNObservables() + + + def _setup_qmatchatea_backend(self): + """Configure qmatchatea QCBackend object.""" + + self.qmatchatea_device = "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 + self.qmatchatea_backend = self.qmatchatea.QCBackend( + backend = "PY", # The only alternative is Fortran, but we use Python here + precision = self.qmatchatea_precision, + device = self.qmatchatea_device, + ansatz = self.ansatz, + ) + + def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> QuantumCircuit: + """Convert a Qibo Circuit into a Qiskit Circuit.""" + # Convert the circuit to QASM 2.0 to qiskit + qasm_circuit = qibo_circuit.to_qasm() + qiskit_circuit = QuantumCircuit.from_qasm_str(qasm_circuit) + + # Transpile the circuit to adapt it to the linear structure of the MPS, + # with the constraint of having only the gates basis_gates + qiskit_circuit = self.qmatchatea.preprocessing.preprocess( + qiskit_circuit, + qk_params=self.qmatchatea.preprocessing.qk_transpilation_params() + ) + + return qiskit_circuit \ No newline at end of file From 43ebd79cec3a6a63367d7eae4aa8da697fd98f28 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 27 Jan 2025 12:28:54 +0100 Subject: [PATCH 04/41] feat: add qmatchatea backend to __init__ backends file --- src/qibotn/backends/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 48a1570..4e0d026 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -4,10 +4,12 @@ from qibo.config import raise_error from qibotn.backends.cutensornet import CuTensorNet # pylint: disable=E0401 from qibotn.backends.quimb import QuimbBackend # pylint: disable=E0401 +from qibotn.backends.qmatchatea import QMatchaTeaBackend # pylint: disable=E0401 +from qibotn.backends.abstract import QibotnBackend -QibotnBackend = Union[CuTensorNet, QuimbBackend] +QibotnBackend = Union[CuTensorNet, QuimbBackend, QMatchaTeaBackend] -PLATFORMS = ("cutensornet", "qutensornet") +PLATFORMS = ("cutensornet", "qutensornet", "qmatchatea") class MetaBackend: @@ -28,10 +30,12 @@ class MetaBackend: return CuTensorNet(runcard) elif platform == "qutensornet": # pragma: no cover return QuimbBackend(runcard) + elif platform == "qmatchatea": # pragma: no cover + return QMatchaTeaBackend() else: raise_error( NotImplementedError, - f"Unsupported platform {platform}, please pick one in (`cutensornet`, `qutensornet)", + f"Unsupported platform {platform}, please pick one in (`cutensornet`, `qutensornet`, `qmatchatea`)", ) def list_available(self) -> dict: From 80bfc7e8768ae22a5c9434e70bd407c42ae66d05 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 11:29:47 +0000 Subject: [PATCH 05/41] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/backends/__init__.py | 6 +- src/qibotn/backends/abstract.py | 13 ++--- src/qibotn/backends/cutensornet.py | 1 - src/qibotn/backends/qmatchatea.py | 91 ++++++++++++++++-------------- src/qibotn/backends/quimb.py | 2 - 5 files changed, 56 insertions(+), 57 deletions(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 4e0d026..624ed62 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -2,10 +2,10 @@ from typing import Union from qibo.config import raise_error -from qibotn.backends.cutensornet import CuTensorNet # pylint: disable=E0401 -from qibotn.backends.quimb import QuimbBackend # pylint: disable=E0401 -from qibotn.backends.qmatchatea import QMatchaTeaBackend # pylint: disable=E0401 from qibotn.backends.abstract import QibotnBackend +from qibotn.backends.cutensornet import CuTensorNet # pylint: disable=E0401 +from qibotn.backends.qmatchatea import QMatchaTeaBackend # pylint: disable=E0401 +from qibotn.backends.quimb import QuimbBackend # pylint: disable=E0401 QibotnBackend = Union[CuTensorNet, QuimbBackend, QMatchaTeaBackend] diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index 6e8088d..f7d2df2 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -1,11 +1,11 @@ from abc import abstractmethod + from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error - DEFAULT_CONFIGURATION = { - "MPI_enabled": False, # TODO: cutensornet specific, TBRemoved - "NCCL_enabled": False, # TODO: cutensornet specific, TBRemoved + "MPI_enabled": False, # TODO: cutensornet specific, TBRemoved + "NCCL_enabled": False, # TODO: cutensornet specific, TBRemoved "expectation_enabled": False, "pauli_string_pattern": None, "MPS_enabled": False, @@ -14,10 +14,9 @@ DEFAULT_CONFIGURATION = { } - class QibotnBackend(NumpyBackend): - def __init__(self, runcard:dict = DEFAULT_CONFIGURATION): + def __init__(self, runcard: dict = DEFAULT_CONFIGURATION): super().__init__() def apply_gate(self, gate, state, nqubits): # pragma: no cover @@ -32,10 +31,8 @@ class QibotnBackend(NumpyBackend): def set_precision(self, precision): if precision != self.precision: super().set_precision(precision) - + @abstractmethod def configure_tn_simulation(self, **config): """Configure the TN simulation that will be performed.""" pass - - diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index d4f1bcc..d02fdd5 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -82,7 +82,6 @@ class CuTensorNet(QibotnBackend): # pragma: no cover if hasattr(self, "cutn"): self.cutn.destroy(self.handle) - def cuda_type(self, dtype="complex64"): """Get CUDA Type. diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index ba40475..2af64ee 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -1,103 +1,108 @@ -"""Implementation of Quantum Matcha Tea backend""" +"""Implementation of Quantum Matcha Tea backend.""" from dataclasses import dataclass + +from qibo.config import raise_error from qiskit import QuantumCircuit from qibotn.backends.abstract import QibotnBackend -from qibo.config import raise_error + @dataclass class QMatchaTeaBackend(QibotnBackend): - - def __init__(self): + + def __init__(self): super().__init__() + import qiskit # 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.qiskit = qiskit self.qtleaves = qtealeaves - # Set default configurations self.configure_tn_simulation() # TODO: update this function whenever ``set_device`` and ``set_precision`` # are set (?) self._setup_qmatchatea_backend() - def execute_circuit( - self, circuit, initial_state=None, nshots=None, return_array=False + self, circuit, initial_state=None, nshots=None, return_array=False ): - + # 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) if initial_state is not None: raise_error( 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 - # idea of "shots" here? + # TODO: do we want to keep it like this or we aim to implement a different + # idea of "shots" here? nshots = None - + circuit = self._qibocirc_to_qiskitcirc(circuit) run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False) results = self.qmatchatea.run_simulation( - circ = circuit, - convergence_parameters = self.convergence_params, - transpilation_parameters = run_qk_params, - backend = self.qmatchatea_backend, + circ=circuit, + convergence_parameters=self.convergence_params, + transpilation_parameters=run_qk_params, + backend=self.qmatchatea_backend, ) # TODO: construct a proper TNResult object? # It does not make sense to reconstruct QuantumState here! return results.measure_probabilities - def configure_tn_simulation( - self, - convergence_params = None, - ansatz: str = "MPS", - ): - """ - Configure TN simulation given Quantum Matcha Tea interface. - + self, + convergence_params=None, + ansatz: str = "MPS", + ): + """Configure TN simulation given Quantum Matcha Tea interface. + Args: ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" or Matrix Product States "MPS" (default). - convergence_params (qmatchatea.utils.QCConvergenceParameters): - convergence parameters class adapted to the quantum computing + convergence_params (qmatchatea.utils.QCConvergenceParameters): + 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 - 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. """ # Set configurationsor defaults - self.convergence_params = convergence_params or self.qmatchatea.QCConvergenceParameters() + self.convergence_params = ( + convergence_params or self.qmatchatea.QCConvergenceParameters() + ) self.ansatz = ansatz - # Initializing the TNObservables according to qmatchatea + # Initializing the TNObservables according to qmatchatea self.observables = self.qtleaves.observables.TNObservables() - def _setup_qmatchatea_backend(self): """Configure qmatchatea QCBackend object.""" - self.qmatchatea_device = "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" + self.qmatchatea_device = ( + "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 self.qmatchatea_backend = self.qmatchatea.QCBackend( - backend = "PY", # The only alternative is Fortran, but we use Python here - precision = self.qmatchatea_precision, - device = self.qmatchatea_device, - ansatz = self.ansatz, + backend="PY", # The only alternative is Fortran, but we use Python here + precision=self.qmatchatea_precision, + device=self.qmatchatea_device, + ansatz=self.ansatz, ) def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> QuantumCircuit: @@ -106,11 +111,11 @@ class QMatchaTeaBackend(QibotnBackend): qasm_circuit = qibo_circuit.to_qasm() 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 qiskit_circuit = self.qmatchatea.preprocessing.preprocess( - qiskit_circuit, - qk_params=self.qmatchatea.preprocessing.qk_transpilation_params() + qiskit_circuit, + qk_params=self.qmatchatea.preprocessing.qk_transpilation_params(), ) - return qiskit_circuit \ No newline at end of file + return qiskit_circuit diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index 20223b3..30ef888 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -1,4 +1,3 @@ -from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error from qibo.result import QuantumState @@ -38,7 +37,6 @@ class QuimbBackend(QibotnBackend): self.platform = "QuimbBackend" self.versions["quimb"] = self.quimb.__version__ - def execute_circuit( self, circuit, initial_state=None, nshots=None, return_array=False ): # pragma: no cover From 508d5cd5e92a0164e01c689a941a205dc9377d06 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 27 Jan 2025 13:03:27 +0100 Subject: [PATCH 06/41] feat: integrating qmatchatea SimulationResult in the circuit execution --- .pre-commit-config.yaml | 2 +- src/qibotn/backends/qmatchatea.py | 119 ++++++++++++++++++------------ 2 files changed, 71 insertions(+), 50 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42147ca..0d8d655 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - id: isort args: ["--profile", "black"] - repo: https://github.com/PyCQA/docformatter - rev: master + rev: v1.7.5 hooks: - id: docformatter additional_dependencies: [tomli] diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index ba40475..c116d8e 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -1,103 +1,125 @@ -"""Implementation of Quantum Matcha Tea backend""" +"""Implementation of Quantum Matcha Tea backend.""" from dataclasses import dataclass + +from qibo.config import raise_error from qiskit import QuantumCircuit from qibotn.backends.abstract import QibotnBackend -from qibo.config import raise_error + @dataclass class QMatchaTeaBackend(QibotnBackend): - - def __init__(self): + + def __init__(self): super().__init__() + import qiskit # 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.qiskit = qiskit self.qtleaves = qtealeaves - # Set default configurations self.configure_tn_simulation() # TODO: update this function whenever ``set_device`` and ``set_precision`` # are set (?) 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( - 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 - # 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) if initial_state is not None: raise_error( 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 - # idea of "shots" here? - nshots = None - + # TODO: do we want to keep it like this or we aim to implement a different + # idea of "shots" here? circuit = self._qibocirc_to_qiskitcirc(circuit) run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False) results = self.qmatchatea.run_simulation( - circ = circuit, - convergence_parameters = self.convergence_params, - transpilation_parameters = run_qk_params, - backend = self.qmatchatea_backend, + circ=circuit, + convergence_parameters=self.convergence_params, + transpilation_parameters=run_qk_params, + backend=self.qmatchatea_backend, + observables=self._observables, ) - # TODO: construct a proper TNResult object? - # It does not make sense to reconstruct QuantumState here! - return results.measure_probabilities - + # TODO: construct a proper TNResult in Qibo? + return results def configure_tn_simulation( - self, - convergence_params = None, - ansatz: str = "MPS", - ): - """ - Configure TN simulation given Quantum Matcha Tea interface. - + self, + convergence_params=None, + ansatz: str = "MPS", + ): + """Configure TN simulation given Quantum Matcha Tea interface. + Args: ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" or Matrix Product States "MPS" (default). - convergence_params (qmatchatea.utils.QCConvergenceParameters): - convergence parameters class adapted to the quantum computing + convergence_params (qmatchatea.utils.QCConvergenceParameters): + 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 - 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. """ # Set configurationsor defaults - self.convergence_params = convergence_params or self.qmatchatea.QCConvergenceParameters() + self.convergence_params = ( + convergence_params or self.qmatchatea.QCConvergenceParameters() + ) self.ansatz = ansatz - # Initializing the TNObservables according to qmatchatea - self.observables = self.qtleaves.observables.TNObservables() - - def _setup_qmatchatea_backend(self): """Configure qmatchatea QCBackend object.""" - self.qmatchatea_device = "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" + self.qmatchatea_device = ( + "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 self.qmatchatea_backend = self.qmatchatea.QCBackend( - backend = "PY", # The only alternative is Fortran, but we use Python here - precision = self.qmatchatea_precision, - device = self.qmatchatea_device, - ansatz = self.ansatz, + backend="PY", # The only alternative is Fortran, but we use Python here + precision=self.qmatchatea_precision, + device=self.qmatchatea_device, + ansatz=self.ansatz, ) def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> QuantumCircuit: @@ -106,11 +128,10 @@ class QMatchaTeaBackend(QibotnBackend): qasm_circuit = qibo_circuit.to_qasm() 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 qiskit_circuit = self.qmatchatea.preprocessing.preprocess( - qiskit_circuit, - qk_params=self.qmatchatea.preprocessing.qk_transpilation_params() + qiskit_circuit, + qk_params=self.qmatchatea.preprocessing.qk_transpilation_params(), ) - - return qiskit_circuit \ No newline at end of file + return qiskit_circuit From 85e2e3982ac4a1565ba1129e8152230b167986a6 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 14:29:30 +0100 Subject: [PATCH 07/41] feat: improving execute_circuit method by adding a TensorNetworkResult object (which standardize the output with Qibo's) --- src/qibotn/backends/qmatchatea.py | 91 ++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 15 deletions(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index c116d8e..0bf8bd3 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -6,6 +6,7 @@ from qibo.config import raise_error from qiskit import QuantumCircuit from qibotn.backends.abstract import QibotnBackend +from qibotn.result import TensorNetworkResult @dataclass @@ -13,10 +14,15 @@ class QMatchaTeaBackend(QibotnBackend): def __init__(self): super().__init__() + + self.name = "qiboml" + self.platform = "qmatchatea" + import qiskit # pylint: disable=import-error import qmatchatea # pylint: disable=import-error import qtealeaves # pylint: disable=import-error + # TODO: move outside of the class self.qmatchatea = qmatchatea self.qiskit = qiskit self.qtleaves = qtealeaves @@ -34,24 +40,52 @@ class QMatchaTeaBackend(QibotnBackend): return self._observables @observables.setter - def observables(self, observables: list): + def observables(self, observables: dict): """Set the observables to be measured after TN execution. - It accepts a list - of objects among the ones proposed in ``qtealeaves.observables``. + It accepts a dict of objects among the ones proposed in ``qtealeaves.observables``. """ + self._observables = self.qtleaves.observables.TNObservables() 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( - self, circuit, initial_state=None, nshots=None, return_array=False + self, + circuit, + initial_state=None, + nshots=None, + prob_type="U", + **prob_kwargs, ): - """Preserve the Qibo execution interface, but return Quantum Matcha Tea - ``SimulationResult`` object.""" + """Execute a Qibo quantum circuit through a tensor network simulation. + The execution returns a ``TensorNetworkResults`` object, which can be + used to reconstruct the system state (in the system size is < 30), the + frequencies (if a number of shots is provided) and the probabibilities. + Different methods are available for the probabilities computation, + according. + + to the Quantum Matcha Tea implementation; in particular: + - "E": even probability measure, where probabilities are measured going down evenly on the + probability tree, and you neglect a branch (a state) if the probability is too low; + - "G": greedy probability measure, where you follow the state going from the most probable to + the least probable, until you reach a given coverage (sum of probabilities); + - "U": optimal probability measure, called unbiased, since differently from the previous + methods it is unbiased. The explanation of this one is + a bit tough, but it is the best possible. See https://arxiv.org/abs/2401.10330. + + Args: + circuit: the Qibo circuit we want to execute; + initial_state: the initial state, usually the vacuum in tensor network + simulations; + nshots: number of shots, if shot-noise simulation is performed; + prob_type: it can be "E", "G" or "U" (default); + prob_kwargs: extra parameters required by qmatchatea to compute the + probabilities. "U" requires ``num_samples`` while "E" and "G" require + ``prob_threshold``. + """ # 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 @@ -62,27 +96,54 @@ class QMatchaTeaBackend(QibotnBackend): 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 - # idea of "shots" here? + # TODO: check circuit = self._qibocirc_to_qiskitcirc(circuit) - run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False) + # Initialize the TNObservable object + observables = self.qtleaves.observables.TNObservables() + + # Shots + if nshots is not None: + observables += self.qtleaves.observables.TNObsProjective(num_shots=nshots) + + # Probabilities + observables += self.qtleaves.observables.TNObsProbabilities( + prob_type=prob_type, + **prob_kwargs, + ) + + # State + observables += self.qtleaves.observables.TNState2File( + name="temp", formatting="U" + ) + results = self.qmatchatea.run_simulation( circ=circuit, convergence_parameters=self.convergence_params, transpilation_parameters=run_qk_params, backend=self.qmatchatea_backend, - observables=self._observables, + observables=observables, ) - # TODO: construct a proper TNResult in Qibo? - return results + if circuit.num_qubits < 30: + statevector = results.statevector + else: + statevector = None + + return TensorNetworkResult( + nqubits=circuit.num_qubits, + backend=self, + measures=results.measures, + measured_probabilities=results.measure_probabilities, + prob_type=prob_type, + statevector=statevector, + ) def configure_tn_simulation( self, - convergence_params=None, ansatz: str = "MPS", + convergence_params=None, ): """Configure TN simulation given Quantum Matcha Tea interface. @@ -96,7 +157,7 @@ class QMatchaTeaBackend(QibotnBackend): by Quantum Matcha Tea's authors are set. """ - # Set configurationsor defaults + # Set configurations or defaults self.convergence_params = ( convergence_params or self.qmatchatea.QCConvergenceParameters() ) From 1268888478006cc5dec96e8e1a6e761b61bbb22e Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 14:29:58 +0100 Subject: [PATCH 08/41] feat: TensorNetworkResult object implementation --- src/qibotn/result.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/qibotn/result.py diff --git a/src/qibotn/result.py b/src/qibotn/result.py new file mode 100644 index 0000000..b140601 --- /dev/null +++ b/src/qibotn/result.py @@ -0,0 +1,37 @@ +from dataclasses import dataclass +from typing import Union + +from numpy import ndarray +from qibo.config import raise_error + +from qibotn.backends.abstract import QibotnBackend + + +@dataclass +class TensorNetworkResult: + nqubits: int + backend: QibotnBackend + measures: dict + measured_probabilities: Union[dict, ndarray] + prob_type: str + statevector: ndarray + + def __post_init__(self): + # TODO: define the general convention when using backends different from qmatchatea + if self.measured_probabilities is None: + self.measured_probabilities = {"default": self.measured_probabilities} + + def probabilities(self): + return self.measured_probabilities[self.prob_type] + + def frequencies(self): + return self.measures + + def state(self): + if self.nqubits < 30: + return self.statevector + else: + raise_error( + NotImplementedError, + f"Tensor network simulation cannot be used to reconstruct statevector for >= 30 .", + ) From 6fe2c32c0df3d871dd7675f5749cb884e914b708 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 14:31:30 +0100 Subject: [PATCH 09/41] fix: removed unused default_runcard dictionary --- src/qibotn/backends/abstract.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index f7d2df2..74e4cd8 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -3,16 +3,6 @@ from abc import abstractmethod from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error -DEFAULT_CONFIGURATION = { - "MPI_enabled": False, # TODO: cutensornet specific, TBRemoved - "NCCL_enabled": False, # TODO: cutensornet specific, TBRemoved - "expectation_enabled": False, - "pauli_string_pattern": None, - "MPS_enabled": False, - "gate_algo": None, - "mps_opts": None, -} - class QibotnBackend(NumpyBackend): From 91b4b63130ce52ac23a6293fcb447d5f8d12aaa7 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 14:38:27 +0100 Subject: [PATCH 10/41] refactor: move the imports outside of the backend init --- src/qibotn/backends/abstract.py | 10 ++++++ src/qibotn/backends/qmatchatea.py | 53 +++++++++++++------------------ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index 74e4cd8..f7d2df2 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -3,6 +3,16 @@ from abc import abstractmethod from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error +DEFAULT_CONFIGURATION = { + "MPI_enabled": False, # TODO: cutensornet specific, TBRemoved + "NCCL_enabled": False, # TODO: cutensornet specific, TBRemoved + "expectation_enabled": False, + "pauli_string_pattern": None, + "MPS_enabled": False, + "gate_algo": None, + "mps_opts": None, +} + class QibotnBackend(NumpyBackend): diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 0bf8bd3..e2e879f 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -2,8 +2,10 @@ from dataclasses import dataclass +import qiskit +import qmatchatea +import qtealeaves from qibo.config import raise_error -from qiskit import QuantumCircuit from qibotn.backends.abstract import QibotnBackend from qibotn.result import TensorNetworkResult @@ -18,21 +20,12 @@ class QMatchaTeaBackend(QibotnBackend): self.name = "qiboml" self.platform = "qmatchatea" - import qiskit # pylint: disable=import-error - import qmatchatea # pylint: disable=import-error - import qtealeaves # pylint: disable=import-error - - # TODO: move outside of the class - self.qmatchatea = qmatchatea - self.qiskit = qiskit - self.qtleaves = qtealeaves - # Set default configurations self.configure_tn_simulation() # TODO: update this function whenever ``set_device`` and ``set_precision`` # are set (?) self._setup_qmatchatea_backend() - self._observables = self.qtleaves.observables.TNObservables() + self._observables = qtealeaves.observables.TNObservables() @property def observables(self): @@ -45,9 +38,9 @@ class QMatchaTeaBackend(QibotnBackend): It accepts a dict of objects among the ones proposed in ``qtealeaves.observables``. """ - self._observables = self.qtleaves.observables.TNObservables() + self._observables = qtealeaves.observables.TNObservables() for obs in observables: - if isinstance(obs, self.qtleaves.observables.tnobase._TNObsBase): + if isinstance(obs, qtealeaves.observables.tnobase._TNObsBase): self._observables += obs else: raise TypeError("Expected an instance of TNObservables") @@ -98,27 +91,25 @@ class QMatchaTeaBackend(QibotnBackend): # TODO: check circuit = self._qibocirc_to_qiskitcirc(circuit) - run_qk_params = self.qmatchatea.preprocessing.qk_transpilation_params(False) + run_qk_params = qmatchatea.preprocessing.qk_transpilation_params(False) # Initialize the TNObservable object - observables = self.qtleaves.observables.TNObservables() + observables = qtealeaves.observables.TNObservables() # Shots if nshots is not None: - observables += self.qtleaves.observables.TNObsProjective(num_shots=nshots) + observables += qtealeaves.observables.TNObsProjective(num_shots=nshots) # Probabilities - observables += self.qtleaves.observables.TNObsProbabilities( + observables += qtealeaves.observables.TNObsProbabilities( prob_type=prob_type, **prob_kwargs, ) # State - observables += self.qtleaves.observables.TNState2File( - name="temp", formatting="U" - ) + observables += qtealeaves.observables.TNState2File(name="temp", formatting="U") - results = self.qmatchatea.run_simulation( + results = qmatchatea.run_simulation( circ=circuit, convergence_parameters=self.convergence_params, transpilation_parameters=run_qk_params, @@ -159,40 +150,40 @@ class QMatchaTeaBackend(QibotnBackend): # Set configurations or defaults self.convergence_params = ( - convergence_params or self.qmatchatea.QCConvergenceParameters() + convergence_params or qmatchatea.QCConvergenceParameters() ) self.ansatz = ansatz def _setup_qmatchatea_backend(self): """Configure qmatchatea QCBackend object.""" - self.qmatchatea_device = ( + qmatchatea_device = ( "cpu" if "CPU" in self.device else "gpu" if "GPU" in self.device else None ) - self.qmatchatea_precision = ( + 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 - self.qmatchatea_backend = self.qmatchatea.QCBackend( + self.qmatchatea_backend = qmatchatea.QCBackend( backend="PY", # The only alternative is Fortran, but we use Python here - precision=self.qmatchatea_precision, - device=self.qmatchatea_device, + precision=qmatchatea_precision, + device=qmatchatea_device, ansatz=self.ansatz, ) - def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> QuantumCircuit: + def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> qiskit.QuantumCircuit: """Convert a Qibo Circuit into a Qiskit Circuit.""" # Convert the circuit to QASM 2.0 to qiskit qasm_circuit = qibo_circuit.to_qasm() - qiskit_circuit = QuantumCircuit.from_qasm_str(qasm_circuit) + qiskit_circuit = qiskit.QuantumCircuit.from_qasm_str(qasm_circuit) # Transpile the circuit to adapt it to the linear structure of the MPS, # with the constraint of having only the gates basis_gates - qiskit_circuit = self.qmatchatea.preprocessing.preprocess( + qiskit_circuit = qmatchatea.preprocessing.preprocess( qiskit_circuit, - qk_params=self.qmatchatea.preprocessing.qk_transpilation_params(), + qk_params=qmatchatea.preprocessing.qk_transpilation_params(), ) return qiskit_circuit From ce40c7b3f367fd79d4b8f59efeadd409d001449c Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 22:16:11 +0100 Subject: [PATCH 11/41] fix: TensorNetworkResult error raising --- src/qibotn/result.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/qibotn/result.py b/src/qibotn/result.py index b140601..078f2e1 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -22,13 +22,22 @@ class TensorNetworkResult: self.measured_probabilities = {"default": self.measured_probabilities} def probabilities(self): + """Return calculated probabilities according to the given method.""" return self.measured_probabilities[self.prob_type] def frequencies(self): - return self.measures + """Return frequencies if a certain number of shots has been set.""" + if self.measures is None: + raise_error( + ValueError, + f"To access frequencies, circuit has to be executed with a given number of shots != None", + ) + else: + return self.measures def state(self): - if self.nqubits < 30: + """Return the statevector if the number of qubits is less than 30.""" + if self.nqubits < 20: return self.statevector else: raise_error( From 66288ac6ebc3e1b67c347a9cb8ae05d72d538edf Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 22:16:52 +0100 Subject: [PATCH 12/41] feat: add method to compute expectation values from symbolic form of hamiltonians --- src/qibotn/backends/qmatchatea.py | 189 ++++++++++++++++++++++++++---- 1 file changed, 166 insertions(+), 23 deletions(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index e2e879f..0ae7922 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -1,7 +1,9 @@ """Implementation of Quantum Matcha Tea backend.""" +import re from dataclasses import dataclass +import numpy as np import qiskit import qmatchatea import qtealeaves @@ -53,31 +55,38 @@ class QMatchaTeaBackend(QibotnBackend): prob_type="U", **prob_kwargs, ): - """Execute a Qibo quantum circuit through a tensor network simulation. - The execution returns a ``TensorNetworkResults`` object, which can be - used to reconstruct the system state (in the system size is < 30), the - frequencies (if a number of shots is provided) and the probabibilities. - Different methods are available for the probabilities computation, - according. + """Execute a Qibo quantum circuit using tensor network simulation. - to the Quantum Matcha Tea implementation; in particular: - - "E": even probability measure, where probabilities are measured going down evenly on the - probability tree, and you neglect a branch (a state) if the probability is too low; - - "G": greedy probability measure, where you follow the state going from the most probable to - the least probable, until you reach a given coverage (sum of probabilities); - - "U": optimal probability measure, called unbiased, since differently from the previous - methods it is unbiased. The explanation of this one is - a bit tough, but it is the best possible. See https://arxiv.org/abs/2401.10330. + This method returns a ``TensorNetworkResult`` object, which provides: + - Reconstruction of the system state (if the system size is < 20). + - Frequencies (if the number of shots is specified). + - Probabilities computed using various methods. + + The following probability computation methods are available, as implemented + in Quantum Matcha Tea: + - **"E" (Even):** Probabilities are computed by evenly descending the probability tree, + pruning branches (states) with probabilities below a threshold. + - **"G" (Greedy):** Probabilities are computed by following the most probable states + in descending order until reaching a given coverage (sum of probabilities). + - **"U" (Unbiased):** An optimal probability measure that is unbiased and designed + for best performance. See https://arxiv.org/abs/2401.10330 for details. Args: - circuit: the Qibo circuit we want to execute; - initial_state: the initial state, usually the vacuum in tensor network - simulations; - nshots: number of shots, if shot-noise simulation is performed; - prob_type: it can be "E", "G" or "U" (default); - prob_kwargs: extra parameters required by qmatchatea to compute the - probabilities. "U" requires ``num_samples`` while "E" and "G" require - ``prob_threshold``. + circuit: A Qibo circuit to execute. + initial_state: The initial state of the system (default is the vacuum state + for tensor network simulations). + nshots: The number of shots for shot-noise simulation (optional). + prob_type: The probability computation method. Must be one of: + - "E" (Even) + - "G" (Greedy) + - "U" (Unbiased) [default]. + prob_kwargs: Additional parameters required for probability computation: + - For "U", requires ``num_samples``. + - For "E" and "G", requires ``prob_threshold``. + + Returns: + TensorNetworkResult: An object with methods to reconstruct the state, + compute probabilities, and generate frequencies. """ # TODO: verify if the QCIO mechanism of matcha is supported by Fortran only @@ -89,6 +98,9 @@ class QMatchaTeaBackend(QibotnBackend): f"Backend {self.name}-{self.platform} currently does not support initial state.", ) + # To be sure the setup is correct and no modifications have been done + self._setup_qmatchatea_backend() + # TODO: check circuit = self._qibocirc_to_qiskitcirc(circuit) run_qk_params = qmatchatea.preprocessing.qk_transpilation_params(False) @@ -117,7 +129,7 @@ class QMatchaTeaBackend(QibotnBackend): observables=observables, ) - if circuit.num_qubits < 30: + if circuit.num_qubits < 20: statevector = results.statevector else: statevector = None @@ -131,6 +143,47 @@ class QMatchaTeaBackend(QibotnBackend): statevector=statevector, ) + def expectation(self, circuit, observable): + """Compute the expectation value of a Qibo-friendly ``observable`` on + the Tensor Network constructed from a Qibo ``circuit``. + + This method takes a Qibo-style symbolic Hamiltonian (e.g., `X(0)*Z(1) + 2.0*Y(2)*Z(0)`) + as the observable, converts it into a Quantum Matcha Tea (qmatchatea) observable + (using `TNObsTensorProduct` and `TNObsWeightedSum`), and computes its expectation + value using the provided circuit. + + Args: + circuit: A Qibo quantum circuit object on which the expectation value + is computed. The circuit should be compatible with the qmatchatea + Tensor Network backend. + observable: The observable whose expectation value we want to compute. + This must be provided in the symbolic Hamiltonian form supported by Qibo + (e.g., `X(0)*Y(1)` or `Z(0)*Z(1) + 1.5*Y(2)`). + + Returns: + qmatchatea.SimulationResult [TEMPORARY] + """ + + # From Qibo to Qiskit + circuit = self._qibocirc_to_qiskitcirc(circuit) + run_qk_params = qmatchatea.preprocessing.qk_transpilation_params(False) + + operators = qmatchatea.QCOperators() + observables = qtealeaves.observables.TNObservables() + # Add custom observable + observables += self._qiboobs_to_qmatchaobs(hamiltonian_form=observable) + + results = qmatchatea.run_simulation( + circ=circuit, + convergence_parameters=self.convergence_params, + transpilation_parameters=run_qk_params, + backend=self.qmatchatea_backend, + observables=observables, + operators=operators, + ) + + return np.real(results.observables["custom_hamiltonian"]) + def configure_tn_simulation( self, ansatz: str = "MPS", @@ -187,3 +240,93 @@ class QMatchaTeaBackend(QibotnBackend): qk_params=qmatchatea.preprocessing.qk_transpilation_params(), ) return qiskit_circuit + + def _qiboobs_to_qmatchaobs( + self, hamiltonian_form, observable_name="custom_hamiltonian" + ): + """Convert a Qibo-style symbolic expression (e.g. '2.0*Y2*Z0 + Z0*Z2') + into a qmatchatea ``TNObsWeightedSum`` observable. + + The parsing logic here assumes: + - Each term may have an optional leading coefficient (defaults to 1.0). + - Each operator is a single-letter from [XYZI] plus a qubit index (e.g., 'X2' means X on qubit 2). + - Terms are separated by '+' (and optionally '-') signs. If negative, we parse it as a negative coefficient. + + Args: + hamiltonian_form: e.g. 'Y2*Z0 + 2.5*Z0*Z2' + observable_name (str): A name for the resulting ``TNObsWeightedSum``. + + Returns: + TNObsWeightedSum: An observable suitable for qmatchatea. + """ + hamiltonian_form = str(hamiltonian_form) + + # Collect all the simple terms in the string and preserve the sign + # whenever a coefficient is negative + hamiltonian_form = hamiltonian_form.replace("-", "+-") + raw_terms = [t.strip() for t in hamiltonian_form.split("+") if t.strip()] + + coeff_list = [] + + # Regex for leading coefficient: e.g. "2.5*" or "-0.3*" + # group(1) will capture the numeric part, group(0) includes the sign if present + leading_coeff_pattern = re.compile(r"^([+-]?\d+(\.\d+)?)\*") + + for i, hamiltonian_term in enumerate(raw_terms): + # Set default coefficient to 1.0 + coeff = 1.0 + # Look for a leading numeric coefficient + match = leading_coeff_pattern.search(hamiltonian_term) + + if match: + # Parse that coefficient + coeff = float(match.group(1)) + + # Remove that portion from the term string so only operators remain + hamiltonian_term = leading_coeff_pattern.sub( + "", hamiltonian_term, count=1 + ) + + # Now isolate the single terms in the product (if there are more than 1) + operators_qubits = hamiltonian_term.split("*") + + # Prepare lists for qmatchatea + operator_names, acting_on_qubits = [], [] + + # Each sub-term is e.g. "Y2", so operator = "Y", qubit = 2 + # We assume the operator is the single letter, the rest is the qubit index + for operator in operators_qubits: + operator = operator.strip() + + # Use a regex to split the operator and the qubit index + match = re.match(r"([^\d]+)(\d+)", operator) + if match: + operator_name = match.group( + 1 + ) # All characters before the number (e.g., 'XYZ') + qubit_index = int(match.group(2)) # The number part (e.g., 2) + + operator_names.append(operator_name) + acting_on_qubits.append([qubit_index]) + + # Build collection of tensor product operators (tpo) + if i == 0: + tpo = qtealeaves.observables.TNObsTensorProduct( + name=f"{hamiltonian_term}", + operators=operator_names, + sites=acting_on_qubits, + ) + else: + tpo += qtealeaves.observables.TNObsTensorProduct( + name=f"{hamiltonian_term}", + operators=operator_names, + sites=acting_on_qubits, + ) + # And also keep track of coefficients + coeff_list.append(coeff) + + # Combine everything into a WeightedSum + obs_sum = qtealeaves.observables.TNObsWeightedSum( + name=observable_name, tp_operators=tpo, coeffs=coeff_list, use_itpo=False + ) + return obs_sum From dc027645c2c142e1c6d1ca7428f2582d9d561abb Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 28 Jan 2025 22:20:32 +0100 Subject: [PATCH 13/41] doc: add example introduction to qmatchatea backend --- examples/qmatchatea_intro/qibojit_errs.npy | Bin 0 -> 232 bytes examples/qmatchatea_intro/qibojit_times.npy | Bin 0 -> 232 bytes examples/qmatchatea_intro/qmatcha_errs.npy | Bin 0 -> 520 bytes examples/qmatchatea_intro/qmatcha_times.npy | Bin 0 -> 520 bytes .../qmatchatea_introduction.ipynb | 811 ++++++++++++++++++ 5 files changed, 811 insertions(+) create mode 100644 examples/qmatchatea_intro/qibojit_errs.npy create mode 100644 examples/qmatchatea_intro/qibojit_times.npy create mode 100644 examples/qmatchatea_intro/qmatcha_errs.npy create mode 100644 examples/qmatchatea_intro/qmatcha_times.npy create mode 100644 examples/qmatchatea_intro/qmatchatea_introduction.ipynb diff --git a/examples/qmatchatea_intro/qibojit_errs.npy b/examples/qmatchatea_intro/qibojit_errs.npy new file mode 100644 index 0000000000000000000000000000000000000000..50a5db285ebf8b1d89d20523c5c6f1427bee0abe GIT binary patch literal 232 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$-#yXlh3bhL411<&-Xn1A^rWbsM&bXPjIBrY|g7Vh;cl%Pu_t literal 0 HcmV?d00001 diff --git a/examples/qmatchatea_intro/qibojit_times.npy b/examples/qmatchatea_intro/qibojit_times.npy new file mode 100644 index 0000000000000000000000000000000000000000..6b80df624287941403287edb3212edfb8ee14967 GIT binary patch literal 232 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$-#yXlh3bhL411<&-P%*a$(+LNJ|CF&|8CI5Y`NJNQA& z^QfN=<{M-|^j%m6G50_hM87~5L|>Z62CzDTY>2)at0sW?8MU*)w8pf#U|Pg;8JK>O zu?S4Bh~5IG=j?~@J61!?JHfjNEZ%Z?C75QoupUgaxUB`#B40Lx=@Z%8!Su965OdyT rZUysCv2O&^0S6)KYEG{P^BbC$gK3{>tH5+g_Y#P@jgW9~0EPzuFdTIH literal 0 HcmV?d00001 diff --git a/examples/qmatchatea_intro/qmatcha_times.npy b/examples/qmatchatea_intro/qmatcha_times.npy new file mode 100644 index 0000000000000000000000000000000000000000..a1540fd8b363be39ec2c88e0a070036d964e4c3b GIT binary patch literal 520 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+i=qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I#ymO7d`3bhL411<(2xDv3;9z^d5TnDC48EppBHr-pn^fSr*V0z=pgJ61k zD^a@ut;{YUy1Sp4np+hG2tUH3t>1GmOQFn_^nFzwKP z<}p~^5}T)B@y3j2Ao{>Ah8G~(;lA8UFrDlC8Z18{_6=CxR^=UtZeY0zrXMgmeE{(l z4hw$-(GF`Tg6RbQ^`Aie1&K30gJ}WVFCh9s$zupTP5CQ`uTVJS8;EB3;`|*%C)|Gm zrWdqM{{dEiuK5>;cF61e4W`Sw!1Mz(-oGGz!>$WpnxUlnADExz^B+Vr#PBgV0I3fz W_k!sIJ|c`@{^v$8eL(3yqXPgFvyq1Y literal 0 HcmV?d00001 diff --git a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb new file mode 100644 index 0000000..198e982 --- /dev/null +++ b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb @@ -0,0 +1,811 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "656bb283-ac6d-48d2-a029-3c417c9961f8", + "metadata": {}, + "source": [ + "## Introduction to Quantum Matcha Tea backend in QiboTN\n", + "\n", + "#### Some imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6722d94e-e311-48f9-b6df-c6d829bf67fb", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import numpy as np\n", + "from scipy import stats\n", + "\n", + "import qibo\n", + "from qibo import Circuit, gates, hamiltonians\n", + "from qibo.backends import construct_backend\n", + "\n", + "from qmatchatea import QCConvergenceParameters" + ] + }, + { + "cell_type": "markdown", + "id": "a009a5e0-cfd4-4a49-9f7c-e82f252c6147", + "metadata": {}, + "source": [ + "#### Some hyper parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "64162116-1555-4a68-811c-01593739d622", + "metadata": {}, + "outputs": [], + "source": [ + "# construct qibotn backend\n", + "qmatcha_backend = construct_backend(backend=\"qibotn\", platform=\"qmatchatea\")\n", + "\n", + "# set number of qubits\n", + "nqubits = 4\n", + "\n", + "# set numpy random seed\n", + "np.random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "id": "252f5cd1-5932-4de6-8076-4a357d50ebad", + "metadata": {}, + "source": [ + "#### Constructing a parametric quantum circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4a22a172-f50d-411d-afa3-fa61937c7b3a", + "metadata": {}, + "outputs": [], + "source": [ + "def build_circuit(nqubits, nlayers):\n", + " \"\"\"Construct a parametric quantum circuit.\"\"\"\n", + " circ = Circuit(nqubits)\n", + " for _ in range(nlayers):\n", + " for q in range(nqubits):\n", + " circ.add(gates.RY(q=q, theta=0.))\n", + " circ.add(gates.RZ(q=q, theta=0.))\n", + " [circ.add(gates.CNOT(q%nqubits, (q+1)%nqubits) for q in range(nqubits))]\n", + " circ.add(gates.M(*range(nqubits)))\n", + " return circ" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "76f23c57-6d08-496b-9a27-52fb63bbfcb1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0: ─RY─RZ─o─────X─RY─RZ─o─────X─RY─RZ─o─────X─M─\n", + "1: ─RY─RZ─X─o───|─RY─RZ─X─o───|─RY─RZ─X─o───|─M─\n", + "2: ─RY─RZ───X─o─|─RY─RZ───X─o─|─RY─RZ───X─o─|─M─\n", + "3: ─RY─RZ─────X─o─RY─RZ─────X─o─RY─RZ─────X─o─M─\n" + ] + } + ], + "source": [ + "circuit = build_circuit(nqubits=nqubits, nlayers=3)\n", + "circuit.draw()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "07b2c097-cea2-42ec-8f1d-b4bbb5b71d98", + "metadata": {}, + "outputs": [], + "source": [ + "# Setting random parameters\n", + "circuit.set_parameters(\n", + " parameters=np.random.uniform(-np.pi, np.pi, len(circuit.get_parameters())),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "fd0cea52-03f5-4366-a01a-a5a84aa8ebc7", + "metadata": {}, + "source": [ + "#### Setting up the tensor network simulator\n", + "\n", + "Depending on the simulator, various parameters can be set. In the case of Quantum Matcha, we direclty exploit the `qmatchatea` interface and we construct a `QCConvergenceParameters` object (for more info [this is the link to the qmatchatea code](https://baltig.infn.it/quantum_matcha_tea/py_api_quantum_matcha_tea/-/blob/master/qmatchatea/utils/utils.py?ref_type=heads#L540))." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "34452bfd-2287-4b38-8099-e072239eab74", + "metadata": {}, + "outputs": [], + "source": [ + "# We first define the useful objects \n", + "convergence_parameters = QCConvergenceParameters(cut_ratio=1e-6, max_bond_dimension=50)\n", + "\n", + "# And then call the dedicate configuration function\n", + "qmatcha_backend.configure_tn_simulation(\n", + " convergence_params=convergence_parameters,\n", + " ansatz=\"MPS\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "648d85b8-445d-4081-aeed-1691fbae67be", + "metadata": {}, + "source": [ + "#### Executing through the backend\n", + "\n", + "The `backend.execute_circuit` method can be used then. We can simulate results in three ways:\n", + "1. reconstruction of the final state (statevector like, only if `nqubits < 20` due to Quantum Matcha Tea setup);\n", + "2. computation of the relevant probabilities of the final state. There are three way of doing so, but we will see it directly into the docstrings;\n", + "3. reconstruction of the relevant state's frequencies (only if `nshots` is not `None`)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "221ef886-5578-4200-a019-dcafa51aada3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m\n", + "\u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute_circuit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mprob_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'U'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mprob_kwargs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDocstring:\u001b[0m\n", + "Execute a Qibo quantum circuit using tensor network simulation.\n", + "\n", + "This method returns a ``TensorNetworkResult`` object, which provides:\n", + " - Reconstruction of the system state (if the system size is < 20).\n", + " - Frequencies (if the number of shots is specified).\n", + " - Probabilities computed using various methods.\n", + "\n", + "The following probability computation methods are available, as implemented \n", + "in Quantum Matcha Tea:\n", + " - **\"E\" (Even):** Probabilities are computed by evenly descending the probability tree, \n", + " pruning branches (states) with probabilities below a threshold.\n", + " - **\"G\" (Greedy):** Probabilities are computed by following the most probable states \n", + " in descending order until reaching a given coverage (sum of probabilities).\n", + " - **\"U\" (Unbiased):** An optimal probability measure that is unbiased and designed \n", + " for best performance. See https://arxiv.org/abs/2401.10330 for details.\n", + "\n", + "Args:\n", + " circuit: A Qibo circuit to execute.\n", + " initial_state: The initial state of the system (default is the vacuum state \n", + " for tensor network simulations).\n", + " nshots: The number of shots for shot-noise simulation (optional).\n", + " prob_type: The probability computation method. Must be one of:\n", + " - \"E\" (Even)\n", + " - \"G\" (Greedy)\n", + " - \"U\" (Unbiased) [default].\n", + " prob_kwargs: Additional parameters required for probability computation:\n", + " - For \"U\", requires ``num_samples``.\n", + " - For \"E\" and \"G\", requires ``prob_threshold``.\n", + "\n", + "Returns:\n", + " TensorNetworkResult: An object with methods to reconstruct the state, \n", + " compute probabilities, and generate frequencies.\n", + "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", + "\u001b[0;31mType:\u001b[0m method\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "qmatcha_backend.execute_circuit?" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "35a244c3-adba-4b8b-b28c-0ab592b0f7cf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'nqubits': 4,\n", + " 'backend': QMatchaTeaBackend(),\n", + " 'measures': None,\n", + " 'measured_probabilities': {'U': {'0000': (0.0, 0.08390937969317301),\n", + " '0001': (0.08390937969317301, 0.08858639088838134),\n", + " '0010': (0.08858639088838131, 0.1832549957082757),\n", + " '0011': (0.1832549957082757, 0.25896776804349736),\n", + " '0100': (0.2589677680434974, 0.33039716334036867),\n", + " '0101': (0.33039716334036867, 0.386620221067355),\n", + " '0110': (0.3866202210673549, 0.4380808691410473),\n", + " '0111': (0.4380808691410473, 0.47837271988834),\n", + " '1000': (0.47837271988834, 0.5916815553716759),\n", + " '1001': (0.5916815553716759, 0.5972581739037379),\n", + " '1010': (0.5972581739037378, 0.6359857590550054),\n", + " '1011': (0.6359857590550054, 0.6894851559808782),\n", + " '1100': (0.6894851559808783, 0.7030911408535467),\n", + " '1101': (0.7030911408535467, 0.8264027395524797),\n", + " '1110': (0.8264027395524797, 0.8981519382820797),\n", + " '1111': (0.8981519382820797, 0.9999999999999998)},\n", + " 'E': [None],\n", + " 'G': [None]},\n", + " 'prob_type': 'U',\n", + " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", + " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", + " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", + " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", + " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", + " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", + " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", + " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Simple execution (defaults)\n", + "outcome = qmatcha_backend.execute_circuit(circuit=circuit)\n", + "\n", + "# Print outcome\n", + "vars(outcome)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "60501c3d-2a44-421f-b434-4a508714b132", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'nqubits': 4,\n", + " 'backend': QMatchaTeaBackend(),\n", + " 'measures': None,\n", + " 'measured_probabilities': {'U': [None],\n", + " 'E': {'0000': 0.08390937969317301,\n", + " '0010': 0.09466860481989439,\n", + " '0011': 0.07571277233522165,\n", + " '0100': 0.07142939529687124,\n", + " '0101': 0.05622305772698632,\n", + " '0110': 0.05146064807369245,\n", + " '1000': 0.11330883548333581,\n", + " '1011': 0.053499396925872765,\n", + " '1101': 0.12331159869893296,\n", + " '1110': 0.07174919872960005,\n", + " '1111': 0.10184806171792007},\n", + " 'G': [None]},\n", + " 'prob_type': 'E',\n", + " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", + " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", + " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", + " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", + " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", + " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", + " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", + " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Execution with a specific probability type\n", + "# We use here \"E\", which is cutting some of the components if under a threshold\n", + "outcome = qmatcha_backend.execute_circuit(\n", + " circuit=circuit,\n", + " prob_type=\"E\",\n", + " prob_threshold=0.05,\n", + ")\n", + "\n", + "# Print outcome\n", + "vars(outcome)" + ] + }, + { + "cell_type": "markdown", + "id": "84ec0b48-f6b4-495c-93b8-8e42d1a8b0df", + "metadata": {}, + "source": [ + "---\n", + "\n", + "One can access to the specific contents of the simulation outcome." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "c0443efc-21ef-4ed5-9cf4-785d204a1881", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Probabilities:\n", + " {'0000': 0.08390937969317301, '0010': 0.09466860481989439, '0011': 0.07571277233522165, '0100': 0.07142939529687124, '0101': 0.05622305772698632, '0110': 0.05146064807369245, '1000': 0.11330883548333581, '1011': 0.053499396925872765, '1101': 0.12331159869893296, '1110': 0.07174919872960005, '1111': 0.10184806171792007}\n", + "\n", + "State:\n", + " [ 0.08809627-0.27595005j 0.24859731-0.22695421j 0.18807826+0.18988408j\n", + " 0.09444097+0.06846085j 0.00470148+0.30764671j 0.17371395-0.09247188j\n", + " -0.18900305+0.12545316j -0.17359753+0.20399288j -0.0517478 +0.04471215j\n", + " -0.0411739 -0.06230031j 0.22377064+0.07842041j -0.21784975-0.27541439j\n", + " -0.27208941+0.04098933j -0.22748127+0.04185292j 0.17105258-0.10503745j\n", + " -0.01729753-0.31866731j]\n", + "\n" + ] + } + ], + "source": [ + "print(f\"Probabilities:\\n {outcome.probabilities()}\\n\")\n", + "print(f\"State:\\n {outcome.state()}\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "9f477388-ca45-409a-a0ce-6603ec294e42", + "metadata": {}, + "source": [ + "---\n", + "\n", + "But frequencies cannot be accessed, since no shots have been set." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "8a868f3e-8383-47ee-a93a-27c5f85e48c5", + "metadata": {}, + "outputs": [], + "source": [ + "# print(f\"Frequencies:\\n {outcome.frequencies()}\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "8e9413c7-602a-44ed-a50c-1c3dd4dd7494", + "metadata": {}, + "source": [ + "---\n", + "\n", + "We can then repeat the execution by setting the number of shots" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "68122cd3-662f-4fd1-bb9c-d33b6f5448dd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'nqubits': 4,\n", + " 'backend': QMatchaTeaBackend(),\n", + " 'measures': {'0000': 88,\n", + " '0001': 7,\n", + " '0010': 90,\n", + " '0011': 78,\n", + " '0100': 72,\n", + " '0101': 56,\n", + " '0110': 47,\n", + " '0111': 41,\n", + " '1000': 120,\n", + " '1001': 7,\n", + " '1010': 41,\n", + " '1011': 53,\n", + " '1100': 20,\n", + " '1101': 129,\n", + " '1110': 73,\n", + " '1111': 102},\n", + " 'measured_probabilities': {'U': [None],\n", + " 'E': {'0000': 0.08390937969317301,\n", + " '0010': 0.09466860481989439,\n", + " '0011': 0.07571277233522165,\n", + " '0100': 0.07142939529687124,\n", + " '0101': 0.05622305772698632,\n", + " '0110': 0.05146064807369245,\n", + " '1000': 0.11330883548333581,\n", + " '1011': 0.053499396925872765,\n", + " '1101': 0.12331159869893296,\n", + " '1110': 0.07174919872960005,\n", + " '1111': 0.10184806171792007},\n", + " 'G': [None]},\n", + " 'prob_type': 'E',\n", + " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", + " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", + " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", + " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", + " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", + " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", + " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", + " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Execution with a specific probability type\n", + "# We use here \"E\", which is cutting some of the components if under a threshold\n", + "outcome = qmatcha_backend.execute_circuit(\n", + " circuit=circuit,\n", + " nshots=1024,\n", + " prob_type=\"E\",\n", + " prob_threshold=0.05,\n", + ")\n", + "\n", + "# Print outcome\n", + "vars(outcome)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ef0e9591-ccca-4cdd-a81b-2bfb3caaf3d0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Frequencies:\n", + " {'0000': 88, '0001': 7, '0010': 90, '0011': 78, '0100': 72, '0101': 56, '0110': 47, '0111': 41, '1000': 120, '1001': 7, '1010': 41, '1011': 53, '1100': 20, '1101': 129, '1110': 73, '1111': 102}\n", + "\n" + ] + } + ], + "source": [ + "# And then finally access frequencies!\n", + "print(f\"Frequencies:\\n {outcome.frequencies()}\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "7fe725ff-533e-4648-9f8d-9cb6c0826354", + "metadata": {}, + "source": [ + "#### Testing the performances" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9e9632d5-28dd-4846-8457-c579d0cb9453", + "metadata": {}, + "outputs": [], + "source": [ + "qmatcha_range = range(2, 100, 2)\n", + "qibojit_threshold = 28\n", + "qibojit_range = range(2, qibojit_threshold, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4e6b1126-32ab-418d-b760-44ff62048f70", + "metadata": {}, + "outputs": [], + "source": [ + "qmatcha_times, qmatcha_errs = [], []\n", + "qibojit_times, qibojit_errs = [], []\n", + "\n", + "qibojit_backend = construct_backend(\"qibojit\", platform=\"numba\")\n", + "qibo.set_threads(2)\n", + "\n", + "\n", + "for q in qmatcha_range:\n", + " print(f\"Executing for {q} qubits\")\n", + " # Build the circuit\n", + " circuit = build_circuit(q, 1)\n", + "\n", + " reasonable_exp = min(13, int(q / 2))\n", + " \n", + " qmatcha_backend.configure_tn_simulation(\n", + " convergence_params=QCConvergenceParameters(max_bond_dimension=2**reasonable_exp),\n", + " ansatz=\"MPS\"\n", + " )\n", + "\n", + " tmp_qmatcha_times, tmp_qibojit_times = [], [] \n", + " for i in range(20):\n", + " # Set random parameters\n", + " circuit.set_parameters(parameters=np.random.uniform(-np.pi, np.pi, len(circuit.get_parameters())))\n", + " \n", + " # Measure QMatcha execution time\n", + " print(f\"1. ---- QMatcha\") if i == 0 else None\n", + " start_time = time.time()\n", + " outcome = qmatcha_backend.execute_circuit(circuit=circuit)\n", + " tmp_qmatcha_times.append(time.time() - start_time)\n", + "\n", + " if q < qibojit_threshold:\n", + " \n", + " if i == 0:\n", + " outcome = qibojit_backend.execute_circuit(circuit=circuit)\n", + " \n", + " # Measure Qibojit execution time\n", + " print(f\"2. ---- Qibojit\") if i == 0 else None\n", + " start_time = time.time()\n", + " outcome = qibojit_backend.execute_circuit(circuit=circuit)\n", + " tmp_qibojit_times.append(time.time() - start_time)\n", + " \n", + " qmatcha_times.append(np.median(tmp_qmatcha_times))\n", + " qmatcha_errs.append(stats.median_abs_deviation(tmp_qmatcha_times))\n", + " \n", + " if q < qibojit_threshold:\n", + " qibojit_times.append(np.median(tmp_qibojit_times))\n", + " qibojit_errs.append(stats.median_abs_deviation(tmp_qibojit_times))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "5620f476-1bc7-4cf8-9868-b127c715b6ec", + "metadata": {}, + "outputs": [], + "source": [ + "# Saving data\n", + "# np.save(arr=qmatcha_times, file=\"qmatcha_times\")\n", + "# np.save(arr=qibojit_times, file=\"qibojit_times\")\n", + "# np.save(arr=qmatcha_errs, file=\"qmatcha_errs\")\n", + "# np.save(arr=qibojit_errs, file=\"qibojit_errs\")\n", + "\n", + "qmatcha_times = np.load(\"qmatcha_times.npy\")\n", + "qmatcha_errs = np.load(\"qmatcha_errs.npy\")\n", + "qibojit_times = np.load(\"qibojit_times.npy\")\n", + "qibojit_errs = np.load(\"qibojit_errs.npy\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "32d605e9-672a-40b6-8a1a-eb17bbc3c45e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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2meffRZt2rTBc889Z9T2W23duhUlJSXV5phU5/z58wCAli1b1uo4dS05ORmOjo6GYdsnn3wS165dw44dOyod1gbkG+fNmzfx5JNPQqFQYOnSpRgzZgwuXLgAe3t7k4/1v//9D1qtFmPGjDG7fSkpKejfvz8UCgWioqLg6emJrVu34rHHHkN2dnaFD9W33noLSqUS8+bNQ1ZWFpYuXYqJEyfizz//NPsxyystLUVERAT69euH5cuXY+fOnXj77bfRvn17zJw5E56envjggw8wc+ZMPPDAA4bn1q1bNwDAiRMncPvtt6N169ZYsGABnJyc8M0332D06NH47rvv8MADD1T5+P/5z3/wxhtvGP4+3bp1w9tvv4309HQsWLAAK1euxGuvvYbXX38d//nPf3DvvfdWeTxLX88nTpzAoEGD4Orqivnz58Pe3h4ffvghhgwZgt9++w39+vUz2n/WrFnw9PTEokWLkJubC0BOhUyZMgURERH497//jby8PHzwwQcYOHAgYmNjzU44feuttzBnzhxoNBqz9rfEF198AQBmByS1lZycDABGXxJ27twJV1dXQwB85swZODk5YdKkSXjnnXcMz/vYsWMoKSlB7969jY6pVqvRvXt3xMbGVvv4Nnk/qrexmGZs+/btQqVSCZVKJQYMGCDmz58vfv75Z1FUVFRh38DAQKPpB/1wWkREhNDpdIbtAwYMEAqFQjz11FOGbSUlJaJNmzbijjvuMGzTD6FrtVpx5coVw/Y///xTABDPPvusYdutUzYXL14UKpVKvP7660ZtPHbsmLCzs6uw/VamhinNPW5sbKwAIDZu3Fjl41gyZbNy5UoBQHz++eeGbUVFRWLAgAHC2dnZMMQqhPxbmDNl8+yzzwoAIjY21qw26P+mO3fuFKmpqSIxMVF8/fXXomXLlkZ/p7qasimvqimbypw9e1ZoNBoxadIks46jf721bNlSpKenG7b/97//FQDE//73vyofz8PDQ3Tv3r3C9uzsbJGammr4KT+M/dhjjwlfX19x48YNo/uMHz9euLm5Gaar9FMewcHBorCw0LDfqlWrBABx7NixKttW2d9jypQpAoD417/+ZbRvjx49RK9evQzXq5qyueuuu0RoaKgoKCgwbNPpdCI8PNxoesDUlE1gYKDhvq+88ooIDw8Xqamp4uzZs6J3797i+eefF0IIUVhY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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plt.figure(figsize=(6, 6 * 6/8))\n", + "plt.title(\"Simple test of CPU 12th Gen Intel® Core™ i7-1260P\")\n", + "plt.errorbar(qmatcha_range, qmatcha_times, yerr=qmatcha_errs, marker=\"o\", color=\"red\", label=\"QMatcha-numpy\", alpha=0.7)\n", + "plt.errorbar(qibojit_range, qibojit_times, yerr=qibojit_errs, marker=\"o\", color=\"blue\", label=\"Qibojit-numba\", alpha=0.7)\n", + "plt.legend()\n", + "plt.xlabel(\"Qubits\")\n", + "plt.ylabel(\"Time [s]\")\n", + "plt.grid(True)\n", + "plt.yscale(\"log\")" + ] + }, + { + "cell_type": "markdown", + "id": "dd84f1f3-7aa5-4ad1-ae09-81e0aff75b5b", + "metadata": {}, + "source": [ + "### Compute expectation values" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "0b46e315-7786-4247-bd2a-83ea1c5842eb", + "metadata": {}, + "outputs": [], + "source": [ + "from qibo.symbols import Z, X" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "37385485-e8a3-4ab0-ad44-bcc4e9da24ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0: ─RY─RZ─o─────X─RY─RZ─o─────X─RY─RZ─o─────X─M─\n", + "1: ─RY─RZ─X─o───|─RY─RZ─X─o───|─RY─RZ─X─o───|─M─\n", + "2: ─RY─RZ───X─o─|─RY─RZ───X─o─|─RY─RZ───X─o─|─M─\n", + "3: ─RY─RZ─────X─o─RY─RZ─────X─o─RY─RZ─────X─o─M─\n" + ] + } + ], + "source": [ + "# We are going to compute the expval of an Hamiltonian\n", + "# On the state prepared by the following circuit\n", + "circuit.draw()\n", + "\n", + "circuit.set_parameters(\n", + " np.random.randn(len(circuit.get_parameters()))\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ddecc910-7804-4199-8577-a7db38a16db8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle - 1.5 X_{0} Z_{2} + 0.5 Z_{0} Z_{1} + Z_{3}$" + ], + "text/plain": [ + "-1.5*X0*Z2 + 0.5*Z0*Z1 + Z3" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# We can create an Hamiltonian form\n", + "form = 0.5 * Z(0) * Z(1) +- 1.5 * X(0) * Z(2) + Z(3)\n", + "form" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a6599df3-989e-4131-8664-3451d0cc4372", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobservable\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDocstring:\u001b[0m\n", + "Compute the expectation value of a Qibo-friendly ``observable``\n", + "on the Tensor Network constructed from a Qibo ``circuit``.\n", + "\n", + "This method takes a Qibo-style symbolic Hamiltonian (e.g., `X(0)*Z(1) + 2.0*Y(2)*Z(0)`) \n", + "as the observable, converts it into a Quantum Matcha Tea (qmatchatea) observable\n", + "(using `TNObsTensorProduct` and `TNObsWeightedSum`), and computes its expectation \n", + "value using the provided circuit.\n", + "\n", + "Args:\n", + " circuit: A Qibo quantum circuit object on which the expectation value\n", + " is computed. The circuit should be compatible with the qmatchatea\n", + " Tensor Network backend.\n", + " observable: The observable whose expectation value we want to compute. \n", + " This must be provided in the symbolic Hamiltonian form supported by Qibo\n", + " (e.g., `X(0)*Y(1)` or `Z(0)*Z(1) + 1.5*Y(2)`).\n", + "\n", + "Returns:\n", + " qmatchatea.SimulationResult [TEMPORARY]\n", + "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", + "\u001b[0;31mType:\u001b[0m method\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "qmatcha_backend.expectation?" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "163b70a3-814a-4a62-a98a-2ffca933a544", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.41329220819521995" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qmatcha_backend.expectation(\n", + " circuit=circuit,\n", + " observable=form,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "2d8c4a9c-eca3-49d0-bdbf-ab054172c4e5", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.16|INFO|2025-01-28 22:13:05]: Using qibojit (numba) backend on /CPU:0\n" + ] + }, + { + "data": { + "text/plain": [ + "0.4132922081952206" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Try with Qibo\n", + "\n", + "hamiltonian = hamiltonians.SymbolicHamiltonian(form)\n", + "hamiltonian.expectation(circuit().state())" + ] + }, + { + "cell_type": "markdown", + "id": "94df291c-9ddc-4b2e-8442-5fca00784bd8", + "metadata": {}, + "source": [ + "They match! 🥳" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From ad40a8ba7a8180975a5c4e4e70390c33c378676c Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 29 Jan 2025 14:37:48 +0100 Subject: [PATCH 14/41] chore: updating lock --- poetry.lock | 1031 ++++++++++++++++++++++++++++++++++++++---------- pyproject.toml | 2 + 2 files changed, 828 insertions(+), 205 deletions(-) diff --git a/poetry.lock b/poetry.lock index dc2be14..6c1e6a4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. [[package]] name = "alabaster" @@ -6,7 +6,6 @@ version = "0.7.16" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, @@ -18,7 +17,6 @@ version = "1.14.1" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5"}, {file = "alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213"}, @@ -38,7 +36,6 @@ version = "4.13.2" description = "ANTLR 4.13.2 runtime for Python 3" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "antlr4_python3_runtime-4.13.2-py3-none-any.whl", hash = "sha256:fe3835eb8d33daece0e799090eda89719dbccee7aa39ef94eed3818cafa5a7e8"}, {file = "antlr4_python3_runtime-4.13.2.tar.gz", hash = "sha256:909b647e1d2fc2b70180ac586df3933e38919c85f98ccc656a96cd3f25ef3916"}, @@ -50,8 +47,6 @@ version = "0.1.4" description = "Disable App Nap on macOS >= 10.9" optional = false python-versions = ">=3.6" -groups = ["dev"] -markers = "sys_platform == \"darwin\"" files = [ {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, @@ -63,7 +58,6 @@ version = "3.3.8" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" -groups = ["analysis"] files = [ {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, @@ -78,7 +72,6 @@ version = "0.7.0" description = "Abstract your array operations." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "autoray-0.7.0-py3-none-any.whl", hash = "sha256:03103957df3d1b66b8068158056c2909a72095b19d1b24262261276a714a5d07"}, {file = "autoray-0.7.0.tar.gz", hash = "sha256:7829d21258512f87e02f23ce74ae5759af4ce8998069d2cce53468f1d701a219"}, @@ -94,7 +87,6 @@ version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, @@ -109,7 +101,6 @@ version = "0.2.0" description = "Specifications for callback functions passed in to an API" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, @@ -121,7 +112,6 @@ version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" -groups = ["docs"] files = [ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, @@ -143,19 +133,96 @@ version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" -groups = ["docs"] files = [ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] +[[package]] +name = "cffi" +version = "1.17.1" +description = "Foreign Function Interface for Python calling C code." +optional = true +python-versions = ">=3.8" +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[package.dependencies] +pycparser = "*" + [[package]] name = "charset-normalizer" version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -257,7 +324,6 @@ version = "3.4.0" description = "CMA-ES, Covariance Matrix Adaptation Evolution Strategy for non-linear numerical optimization in Python" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "cma-3.4.0-py3-none-any.whl", hash = "sha256:4140e490cc4e68cf8c7b1114e079c0561c9b78b1bf9ec69362c20865636ae5ca"}, {file = "cma-3.4.0.tar.gz", hash = "sha256:a1ebd969b99871be3715d5a24b7bf54cf04ea94e80d6b8536d7147620dd10f6c"}, @@ -276,12 +342,10 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "analysis", "dev", "docs", "tests"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", analysis = "sys_platform == \"win32\"", dev = "sys_platform == \"win32\"", docs = "sys_platform == \"win32\"", tests = "sys_platform == \"win32\""} [[package]] name = "colorlog" @@ -289,7 +353,6 @@ version = "6.9.0" description = "Add colours to the output of Python's logging module." optional = false python-versions = ">=3.6" -groups = ["main"] files = [ {file = "colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff"}, {file = "colorlog-6.9.0.tar.gz", hash = "sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2"}, @@ -307,7 +370,6 @@ version = "1.3.0" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, @@ -392,7 +454,6 @@ version = "0.7.0" description = "Hyper optimized contraction trees for large tensor networks and einsums." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "cotengra-0.7.0-py3-none-any.whl", hash = "sha256:ca1c75fec462fb75130a3a826db2d4565bbbf0641d5ce0a617e643211cd98307"}, {file = "cotengra-0.7.0.tar.gz", hash = "sha256:8ad9fabee899ac2f7b72359ace79a5496f15fd89b19049656082ef7773ca2b69"}, @@ -412,7 +473,6 @@ version = "7.6.10" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" -groups = ["tests"] files = [ {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, @@ -484,14 +544,61 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "cryptography" +version = "43.0.3" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = true +python-versions = ">=3.7" +files = [ + {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, + {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, + {file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, + {file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, + {file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, + {file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, + {file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, + {file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, + {file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, + {file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, + {file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, + {file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, + {file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "cupy-cuda11x" version = "11.6.0" description = "CuPy: NumPy & SciPy for GPU" optional = true python-versions = ">=3.7" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "cupy_cuda11x-11.6.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:1b9914f57868a1559e9bfabfbae8c724585914e8e1f277acb9cdb6aa0756eaa4"}, {file = "cupy_cuda11x-11.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:ac8dd082ddb00996bc4d37cc5765907048f467aadb61bcbff25f3c2a88c50583"}, @@ -520,8 +627,6 @@ version = "23.10.0" description = "NVIDIA cuQuantum Python" optional = true python-versions = ">=3.9" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "cuquantum_python_cu11-23.10.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a0f0dfcb6239ec5fce836fa2f641820d3235ac7d83f391eac90952cf481da03f"}, {file = "cuquantum_python_cu11-23.10.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:ad5e38501cb53d50ba19fc48790f2c79fbc14c22e101d51a0b338f6c6971e6a0"}, @@ -542,8 +647,6 @@ version = "1.7.0" description = "cuStateVec - a component of NVIDIA cuQuantum SDK" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "custatevec_cu11-1.7.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dabdd80bab8e19413904c8377268dcfbc0468b3a8438a04cca422502c70b34d0"}, {file = "custatevec_cu11-1.7.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:11b3a2a79de52afaab1acc970e3d810d219e90170405aaded78ac6dcfa1bb389"}, @@ -555,8 +658,6 @@ version = "2.0.2" description = "NVIDIA cuTENSOR" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "cutensor_cu11-2.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:e17003e5f5cf0e83292e9e7e380b64c87a311f8096b3a287a630cbab743ef52f"}, {file = "cutensor_cu11-2.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6d37a1164cb02d74322b35b09f018ce51aff078dedee10823820b9d878ebb8c3"}, @@ -569,8 +670,6 @@ version = "2.6.0" description = "cuTensorNet - a component of NVIDIA cuQuantum SDK" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "cutensornet_cu11-2.6.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:ded8b2b3124fd2acd6ce9c13060b0ea17692bcbca727314b2930c6ab341035ef"}, {file = "cutensornet_cu11-2.6.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f7be9781de96cd897b59ac81cfb5c407a79a9781d236a25358656e24c9e81b71"}, @@ -585,7 +684,6 @@ version = "0.12.1" description = "Composable style cycles" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, @@ -601,7 +699,6 @@ version = "1.0.1" description = "Cython implementation of Toolz: High performance functional utilities" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "cytoolz-1.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cec9af61f71fc3853eb5dca3d42eb07d1f48a4599fa502cbe92adde85f74b042"}, {file = "cytoolz-1.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:140bbd649dbda01e91add7642149a5987a7c3ccc251f2263de894b89f50b6608"}, @@ -717,7 +814,6 @@ version = "5.1.1" description = "Decorators for Humans" optional = false python-versions = ">=3.5" -groups = ["dev"] files = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, @@ -729,7 +825,6 @@ version = "0.3.9" description = "serialize all of Python" optional = false python-versions = ">=3.8" -groups = ["analysis"] files = [ {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, @@ -745,7 +840,6 @@ version = "0.19" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, @@ -757,8 +851,6 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" -groups = ["tests"] -markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -773,8 +865,6 @@ version = "0.8.3" description = "Fast, re-entrant optimistic lock implemented in Cython" optional = true python-versions = "*" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "fastrlock-0.8.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bbbe31cb60ec32672969651bf68333680dacaebe1a1ec7952b8f5e6e23a70aa5"}, {file = "fastrlock-0.8.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:45055702fe9bff719cdc62caa849aa7dbe9e3968306025f639ec62ef03c65e88"}, @@ -849,62 +939,61 @@ files = [ [[package]] name = "fonttools" -version = "4.55.3" +version = "4.55.7" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ - {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0"}, - {file = "fonttools-4.55.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f"}, - {file = "fonttools-4.55.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841"}, - {file = "fonttools-4.55.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674"}, - {file = "fonttools-4.55.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276"}, - {file = "fonttools-4.55.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5"}, - {file = "fonttools-4.55.3-cp310-cp310-win32.whl", hash = "sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261"}, - {file = "fonttools-4.55.3-cp310-cp310-win_amd64.whl", hash = "sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5"}, - {file = "fonttools-4.55.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e"}, - {file = "fonttools-4.55.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b"}, - {file = "fonttools-4.55.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90"}, - {file = "fonttools-4.55.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0"}, - {file = "fonttools-4.55.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b"}, - {file = "fonttools-4.55.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765"}, - {file = "fonttools-4.55.3-cp311-cp311-win32.whl", hash = "sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f"}, - {file = "fonttools-4.55.3-cp311-cp311-win_amd64.whl", hash = "sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72"}, - {file = "fonttools-4.55.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35"}, - {file = "fonttools-4.55.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c"}, - {file = "fonttools-4.55.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7"}, - {file = "fonttools-4.55.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8d5e8916c0970fbc0f6f1bece0063363bb5857a7f170121a4493e31c3db3314"}, - {file = "fonttools-4.55.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae3b6600565b2d80b7c05acb8e24d2b26ac407b27a3f2e078229721ba5698427"}, - {file = "fonttools-4.55.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:54153c49913f45065c8d9e6d0c101396725c5621c8aee744719300f79771d75a"}, - {file = "fonttools-4.55.3-cp312-cp312-win32.whl", hash = "sha256:827e95fdbbd3e51f8b459af5ea10ecb4e30af50221ca103bea68218e9615de07"}, - {file = "fonttools-4.55.3-cp312-cp312-win_amd64.whl", hash = "sha256:e6e8766eeeb2de759e862004aa11a9ea3d6f6d5ec710551a88b476192b64fd54"}, - {file = "fonttools-4.55.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a430178ad3e650e695167cb53242dae3477b35c95bef6525b074d87493c4bf29"}, - {file = "fonttools-4.55.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:529cef2ce91dc44f8e407cc567fae6e49a1786f2fefefa73a294704c415322a4"}, - {file = "fonttools-4.55.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e75f12c82127486fac2d8bfbf5bf058202f54bf4f158d367e41647b972342ca"}, - {file = "fonttools-4.55.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859c358ebf41db18fb72342d3080bce67c02b39e86b9fbcf1610cca14984841b"}, - {file = "fonttools-4.55.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:546565028e244a701f73df6d8dd6be489d01617863ec0c6a42fa25bf45d43048"}, - {file = "fonttools-4.55.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aca318b77f23523309eec4475d1fbbb00a6b133eb766a8bdc401faba91261abe"}, - {file = "fonttools-4.55.3-cp313-cp313-win32.whl", hash = "sha256:8c5ec45428edaa7022f1c949a632a6f298edc7b481312fc7dc258921e9399628"}, - {file = "fonttools-4.55.3-cp313-cp313-win_amd64.whl", hash = "sha256:11e5de1ee0d95af4ae23c1a138b184b7f06e0b6abacabf1d0db41c90b03d834b"}, - {file = "fonttools-4.55.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:caf8230f3e10f8f5d7593eb6d252a37caf58c480b19a17e250a63dad63834cf3"}, - {file = "fonttools-4.55.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b586ab5b15b6097f2fb71cafa3c98edfd0dba1ad8027229e7b1e204a58b0e09d"}, - {file = "fonttools-4.55.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8c2794ded89399cc2169c4d0bf7941247b8d5932b2659e09834adfbb01589aa"}, - {file = "fonttools-4.55.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf4fe7c124aa3f4e4c1940880156e13f2f4d98170d35c749e6b4f119a872551e"}, - {file = "fonttools-4.55.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:86721fbc389ef5cc1e2f477019e5069e8e4421e8d9576e9c26f840dbb04678de"}, - {file = "fonttools-4.55.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:89bdc5d88bdeec1b15af790810e267e8332d92561dce4f0748c2b95c9bdf3926"}, - {file = "fonttools-4.55.3-cp38-cp38-win32.whl", hash = "sha256:bc5dbb4685e51235ef487e4bd501ddfc49be5aede5e40f4cefcccabc6e60fb4b"}, - {file = "fonttools-4.55.3-cp38-cp38-win_amd64.whl", hash = "sha256:cd70de1a52a8ee2d1877b6293af8a2484ac82514f10b1c67c1c5762d38073e56"}, - {file = "fonttools-4.55.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bdcc9f04b36c6c20978d3f060e5323a43f6222accc4e7fcbef3f428e216d96af"}, - {file = "fonttools-4.55.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c3ca99e0d460eff46e033cd3992a969658c3169ffcd533e0a39c63a38beb6831"}, - {file = "fonttools-4.55.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22f38464daa6cdb7b6aebd14ab06609328fe1e9705bb0fcc7d1e69de7109ee02"}, - {file = "fonttools-4.55.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed63959d00b61959b035c7d47f9313c2c1ece090ff63afea702fe86de00dbed4"}, - {file = "fonttools-4.55.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5e8d657cd7326eeaba27de2740e847c6b39dde2f8d7cd7cc56f6aad404ddf0bd"}, - {file = "fonttools-4.55.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fb594b5a99943042c702c550d5494bdd7577f6ef19b0bc73877c948a63184a32"}, - {file = "fonttools-4.55.3-cp39-cp39-win32.whl", hash = "sha256:dc5294a3d5c84226e3dbba1b6f61d7ad813a8c0238fceea4e09aa04848c3d851"}, - {file = "fonttools-4.55.3-cp39-cp39-win_amd64.whl", hash = "sha256:aedbeb1db64496d098e6be92b2e63b5fac4e53b1b92032dfc6988e1ea9134a4d"}, - {file = "fonttools-4.55.3-py3-none-any.whl", hash = "sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977"}, - {file = "fonttools-4.55.3.tar.gz", hash = "sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45"}, + {file = "fonttools-4.55.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c2680a3e6e2e2d104a7ea81fb89323e1a9122c23b03d6569d0768887d0d76e69"}, + {file = "fonttools-4.55.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7831d16c95b60866772a15fdcc03772625c4bb6d858e0ad8ef3d6e48709b2ef"}, + {file = "fonttools-4.55.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:833927d089e6585019f2c85e3f8f7d87733e3fe81cd704ebaca7afa27e2e7113"}, + {file = "fonttools-4.55.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7858dc6823296a053d85b831fa8428781c6c6f06fca44582bf7b6b2ff32a9089"}, + {file = "fonttools-4.55.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:05568a66b090ed9d79aefdce2ceb180bb64fc856961deaedc29f5ad51355ce2c"}, + {file = "fonttools-4.55.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2dbc08e227fbeb716776905a7bd3c4fc62c8e37c8ef7d481acd10cb5fde12222"}, + {file = "fonttools-4.55.7-cp310-cp310-win32.whl", hash = "sha256:6eb93cbba484a463b5ee83f7dd3211905f27a3871d20d90fb72de84c6c5056e3"}, + {file = "fonttools-4.55.7-cp310-cp310-win_amd64.whl", hash = "sha256:7ff8e606f905048dc91a55a06d994b68065bf35752ae199df54a9bf30013dcaa"}, + {file = "fonttools-4.55.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:916e1d926823b4b3b3815c59fc79f4ed670696fdd5fd9a5e690a0503eef38f79"}, + {file = "fonttools-4.55.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b89da448e0073408d7b2c44935f9fdae4fdc93644899f99f6102ef883ecf083c"}, + {file = "fonttools-4.55.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:087ace2d06894ccdb03e6975d05da6bb9cec0c689b2a9983c059880e33a1464a"}, + {file = "fonttools-4.55.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:775ed0700ee6f781436641f18a0c61b1846a8c1aecae6da6b395c4417e2cb567"}, + {file = "fonttools-4.55.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9ec71d0cc0242899f87e4c230ed0b22c7b8681f288fb80e3d81c2c54c5bd2c79"}, + {file = "fonttools-4.55.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d4b1c5939c0521525f45522823508e6fad21175bca978583688ea3b3736e6625"}, + {file = "fonttools-4.55.7-cp311-cp311-win32.whl", hash = "sha256:23df0f1003abaf8a435543f59583fc247e7ae1b047ee2263510e0654a5f207e0"}, + {file = "fonttools-4.55.7-cp311-cp311-win_amd64.whl", hash = "sha256:82163d58b43eff6e2025a25c32905fdb9042a163cc1ff82dab393e7ffc77a7d5"}, + {file = "fonttools-4.55.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:12e81d44f762156d28b5c93a6b65d98ed73678be45b22546de8ed29736c3cb96"}, + {file = "fonttools-4.55.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c26445a7be689f8b70df7d5d2e2c85ec4407bdb769902a23dd45ac44f767575d"}, + {file = "fonttools-4.55.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2cbafedb9462be7cf68c66b6ca1d8309842fe36b729f1b1969595f5d660e5c2"}, + {file = "fonttools-4.55.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4bde87985012adbd7559bc363d802fb335e92a07ff86a76cf02bebb0b8566d1"}, + {file = "fonttools-4.55.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:69ed0660750993150f7c4d966c0c1ffaa0385f23ccef85c2ff108062d80dd7ea"}, + {file = "fonttools-4.55.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3098355e7a7b5ac48d5dc29684a65271187b865b85675033958b57c40364ee34"}, + {file = "fonttools-4.55.7-cp312-cp312-win32.whl", hash = "sha256:ee7aa8bb716318e3d835ef473978e22b7a39c0f1b3b08cc0b0ee1bba6f73bc1e"}, + {file = "fonttools-4.55.7-cp312-cp312-win_amd64.whl", hash = "sha256:e696d6e2baf4cc57ded34bb87e5d3a9e4da9732f3d9e8e2c6db0746e57a6dc0b"}, + {file = "fonttools-4.55.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e10c7fb80cdfdc32244514cbea0906e9f53e3cc80d64d3389da09502fd999b55"}, + {file = "fonttools-4.55.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1101976c703ff4008a928fc3fef42caf06d035bfc4614230d7e797cbe356feb0"}, + {file = "fonttools-4.55.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e6dffe9cbcd163ef617fab1f81682e4d1629b7a5b9c5e598274dc2d03e88bcd"}, + {file = "fonttools-4.55.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e5115a425d53be6e31cd0fe9210f62a488bccf81eb113ab5dd7f4fa88e4d81"}, + {file = "fonttools-4.55.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f0c45eae32d090763820756b18322a70571dada3f1cbe003debc37a9c35bc260"}, + {file = "fonttools-4.55.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd4ebc475d43f3de2b26e0cf551eff92c24e22d1aee03dc1b33adb52fc2e6cb2"}, + {file = "fonttools-4.55.7-cp313-cp313-win32.whl", hash = "sha256:371197de1283cc99f5f10eb91496520eb0e2d079312d014fd6cef9e802174c6a"}, + {file = "fonttools-4.55.7-cp313-cp313-win_amd64.whl", hash = "sha256:418ece624fbc04e199f58398ffef3eaad645baba65434871b09eb7350a3a346b"}, + {file = "fonttools-4.55.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3976db357484bf4cb533dfd0d1a444b38ad06062458715ebf21e38c71aff325d"}, + {file = "fonttools-4.55.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:30c3501328363b73a90acc8a722dd199c993f2c4369ea16886128d94e91897ec"}, + {file = "fonttools-4.55.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0899cd23967950e7b902ea75af06cfe5f59ac71eb38e98a774c9e596790e6aa"}, + {file = "fonttools-4.55.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f669910b64d27750398f6c56c651367d4954b05c86ff067af1c9949e109cf1e2"}, + {file = "fonttools-4.55.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1d4be8354c245c00aecfc90f5d3da8606226f0ac22e1cb0837b39139e4c2df85"}, + {file = "fonttools-4.55.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9074a2848ea5b607377e16998dfcf90cf5eb614d0c388541b9782d5cc038e149"}, + {file = "fonttools-4.55.7-cp38-cp38-win32.whl", hash = "sha256:5ff0daf8b2e0612e5761fed2e4a2f54eff9d9ec0aeb4091c9f3666f9a118325e"}, + {file = "fonttools-4.55.7-cp38-cp38-win_amd64.whl", hash = "sha256:0ed25d7b5fa4ae6a805c2a9cc0e5307d45cbb3b8e155584fe932d0f3b6a997bf"}, + {file = "fonttools-4.55.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ef5ee98fc320c158e4e459a5ee40d1ac3728d4ce11c3c8dfd854aa0aa5c042f"}, + {file = "fonttools-4.55.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09740feed51f9ed816aebf5d82071b7fecf693ac3a7e0fc8ea433f5dc3bd92f5"}, + {file = "fonttools-4.55.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3d19ea483b3cd8833e9e2ee8115f3d2044d55d3743d84f9c23b48b52d7516d8"}, + {file = "fonttools-4.55.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c135c91d47351b84893fb6fcbb8f178eba14f7cb195850264c0675c85e4238b6"}, + {file = "fonttools-4.55.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bee4920ebeb540849bc3555d871e2a8487e39ce8263c281f74d5b6d44d2bf1df"}, + {file = "fonttools-4.55.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f3b63648600dd0081bdd6856a86d014a7f1d2d11c3c974542f866478d832e103"}, + {file = "fonttools-4.55.7-cp39-cp39-win32.whl", hash = "sha256:d4bd27f0fa5120aaa39f76de5768959bc97300e0f59a3160d466b51436a38aea"}, + {file = "fonttools-4.55.7-cp39-cp39-win_amd64.whl", hash = "sha256:c665df9c9d99937a5bf807bace1c0c95bd13f55de8c82aaf9856b868dcbfe5d9"}, + {file = "fonttools-4.55.7-py3-none-any.whl", hash = "sha256:3304dfcf9ca204dd0ef691a287bd851ddd8e8250108658c0677c3fdfec853a20"}, + {file = "fonttools-4.55.7.tar.gz", hash = "sha256:6899e3d97225a8218f525e9754da0376e1c62953a0d57a76c5abaada51e0d140"}, ] [package.extras] @@ -927,7 +1016,6 @@ version = "2023.3.27" description = "A clean customisable Sphinx documentation theme." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "furo-2023.3.27-py3-none-any.whl", hash = "sha256:4ab2be254a2d5e52792d0ca793a12c35582dd09897228a6dd47885dabd5c9521"}, {file = "furo-2023.3.27.tar.gz", hash = "sha256:b99e7867a5cc833b2b34d7230631dd6558c7a29f93071fdbb5709634bb33c5a5"}, @@ -945,8 +1033,6 @@ version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" -groups = ["main"] -markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\"" files = [ {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, @@ -1027,13 +1113,50 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "h5py" +version = "3.12.1" +description = "Read and write HDF5 files from Python" +optional = true +python-versions = ">=3.9" +files = [ + {file = "h5py-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f0f1a382cbf494679c07b4371f90c70391dedb027d517ac94fa2c05299dacda"}, + {file = "h5py-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb65f619dfbdd15e662423e8d257780f9a66677eae5b4b3fc9dca70b5fd2d2a3"}, + {file = "h5py-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b15d8dbd912c97541312c0e07438864d27dbca857c5ad634de68110c6beb1c2"}, + {file = "h5py-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59685fe40d8c1fbbee088c88cd4da415a2f8bee5c270337dc5a1c4aa634e3307"}, + {file = "h5py-3.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:577d618d6b6dea3da07d13cc903ef9634cde5596b13e832476dd861aaf651f3e"}, + {file = "h5py-3.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ccd9006d92232727d23f784795191bfd02294a4f2ba68708825cb1da39511a93"}, + {file = "h5py-3.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad8a76557880aed5234cfe7279805f4ab5ce16b17954606cca90d578d3e713ef"}, + {file = "h5py-3.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1473348139b885393125126258ae2d70753ef7e9cec8e7848434f385ae72069e"}, + {file = "h5py-3.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:018a4597f35092ae3fb28ee851fdc756d2b88c96336b8480e124ce1ac6fb9166"}, + {file = "h5py-3.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:3fdf95092d60e8130ba6ae0ef7a9bd4ade8edbe3569c13ebbaf39baefffc5ba4"}, + {file = "h5py-3.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06a903a4e4e9e3ebbc8b548959c3c2552ca2d70dac14fcfa650d9261c66939ed"}, + {file = "h5py-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b3b8f3b48717e46c6a790e3128d39c61ab595ae0a7237f06dfad6a3b51d5351"}, + {file = "h5py-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:050a4f2c9126054515169c49cb900949814987f0c7ae74c341b0c9f9b5056834"}, + {file = "h5py-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c4b41d1019322a5afc5082864dfd6359f8935ecd37c11ac0029be78c5d112c9"}, + {file = "h5py-3.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4d51919110a030913201422fb07987db4338eba5ec8c5a15d6fab8e03d443fc"}, + {file = "h5py-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:513171e90ed92236fc2ca363ce7a2fc6f2827375efcbb0cc7fbdd7fe11fecafc"}, + {file = "h5py-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:59400f88343b79655a242068a9c900001a34b63e3afb040bd7cdf717e440f653"}, + {file = "h5py-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3e465aee0ec353949f0f46bf6c6f9790a2006af896cee7c178a8c3e5090aa32"}, + {file = "h5py-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba51c0c5e029bb5420a343586ff79d56e7455d496d18a30309616fdbeed1068f"}, + {file = "h5py-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:52ab036c6c97055b85b2a242cb540ff9590bacfda0c03dd0cf0661b311f522f8"}, + {file = "h5py-3.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2b8dd64f127d8b324f5d2cd1c0fd6f68af69084e9e47d27efeb9e28e685af3e"}, + {file = "h5py-3.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4532c7e97fbef3d029735db8b6f5bf01222d9ece41e309b20d63cfaae2fb5c4d"}, + {file = "h5py-3.12.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdf6d7936fa824acfa27305fe2d9f39968e539d831c5bae0e0d83ed521ad1ac"}, + {file = "h5py-3.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84342bffd1f82d4f036433e7039e241a243531a1d3acd7341b35ae58cdab05bf"}, + {file = "h5py-3.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:62be1fc0ef195891949b2c627ec06bc8e837ff62d5b911b6e42e38e0f20a897d"}, + {file = "h5py-3.12.1.tar.gz", hash = "sha256:326d70b53d31baa61f00b8aa5f95c2fcb9621a3ee8365d770c551a13dbbcbfdf"}, +] + +[package.dependencies] +numpy = ">=1.19.3" + [[package]] name = "idna" version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" -groups = ["docs"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1048,7 +1171,6 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -groups = ["docs"] files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -1056,15 +1178,13 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.5.0" +version = "8.6.1" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.8" -groups = ["main", "docs"] -markers = "python_version < \"3.10\"" +python-versions = ">=3.9" files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, + {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, + {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, ] [package.dependencies] @@ -1076,7 +1196,7 @@ cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -1085,8 +1205,6 @@ version = "6.5.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.9" -groups = ["main"] -markers = "python_version < \"3.10\"" files = [ {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, @@ -1109,7 +1227,6 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" -groups = ["tests"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -1121,7 +1238,6 @@ version = "7.34.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.7" -groups = ["dev"] files = [ {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, @@ -1154,18 +1270,18 @@ test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments" [[package]] name = "isort" -version = "5.13.2" +version = "6.0.0" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.8.0" -groups = ["analysis"] +python-versions = ">=3.9.0" files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, + {file = "isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892"}, + {file = "isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1"}, ] [package.extras] -colors = ["colorama (>=0.4.6)"] +colors = ["colorama"] +plugins = ["setuptools"] [[package]] name = "jedi" @@ -1173,7 +1289,6 @@ version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, @@ -1193,7 +1308,6 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -1211,7 +1325,6 @@ version = "1.4.2" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, @@ -1223,7 +1336,6 @@ version = "1.4.7" description = "A fast implementation of the Cassowary constraint solver" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, @@ -1347,7 +1459,6 @@ version = "3.0.0" description = "A lexer and codec to work with LaTeX code in Python." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "latexcodec-3.0.0-py3-none-any.whl", hash = "sha256:6f3477ad5e61a0a99bd31a6a370c34e88733a6bad9c921a3ffcfacada12f41a7"}, {file = "latexcodec-3.0.0.tar.gz", hash = "sha256:917dc5fe242762cc19d963e6548b42d63a118028cdd3361d62397e3b638b6bc5"}, @@ -1359,7 +1470,6 @@ version = "0.43.0" description = "lightweight wrapper around basic LLVM functionality" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "llvmlite-0.43.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a289af9a1687c6cf463478f0fa8e8aa3b6fb813317b0d70bf1ed0759eab6f761"}, {file = "llvmlite-0.43.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d4fd101f571a31acb1559ae1af30f30b1dc4b3186669f92ad780e17c81e91bc"}, @@ -1390,7 +1500,6 @@ version = "1.3.8" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627"}, {file = "mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8"}, @@ -1410,7 +1519,6 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main", "docs"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1481,7 +1589,6 @@ version = "3.9.4" description = "Python plotting package" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50"}, {file = "matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff"}, @@ -1547,7 +1654,6 @@ version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, @@ -1562,7 +1668,6 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" -groups = ["analysis"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -1574,8 +1679,6 @@ version = "3.1.6" description = "Python bindings for MPI" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -groups = ["main"] -markers = "extra == \"cuda\"" files = [ {file = "mpi4py-3.1.6-cp27-cp27m-win32.whl", hash = "sha256:95f27e00f3951f9c1533cd99ffeae2f384f7ba53cc3870ee06c3c88f9e5bd6c3"}, {file = "mpi4py-3.1.6-cp27-cp27m-win_amd64.whl", hash = "sha256:ea8a65f74707e1be5a0125ae3f4f6c0c475c3d845d623b9f5686a919c1119439"}, @@ -1604,7 +1707,6 @@ version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -1622,7 +1724,6 @@ version = "3.2.1" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, @@ -1641,7 +1742,6 @@ version = "0.60.0" description = "compiling Python code using LLVM" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "numba-0.60.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d761de835cd38fb400d2c26bb103a2726f548dc30368853121d66201672e651"}, {file = "numba-0.60.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:159e618ef213fba758837f9837fb402bbe65326e60ba0633dbe6c7f274d42c1b"}, @@ -1676,7 +1776,6 @@ version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -1722,7 +1821,6 @@ version = "1.0.0" description = "Reference OpenQASM AST in Python" optional = false python-versions = "*" -groups = ["main"] files = [ {file = "openqasm3-1.0.0-py3-none-any.whl", hash = "sha256:d4371737b4a49b0d56248ed3d30766a94000bccfb43303ec9c7ead351a1b6cc3"}, {file = "openqasm3-1.0.0.tar.gz", hash = "sha256:3f2bb1cca855cff114e046bac22d59adbf9b754cac6398961aa6d22588fb688e"}, @@ -1739,14 +1837,13 @@ tests = ["pytest (>=6.0)", "pyyaml"] [[package]] name = "optuna" -version = "4.1.0" +version = "4.2.0" description = "A hyperparameter optimization framework" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ - {file = "optuna-4.1.0-py3-none-any.whl", hash = "sha256:1763856b01c9238594d9d21db92611aac9980e9a6300bd658a7c6464712c704e"}, - {file = "optuna-4.1.0.tar.gz", hash = "sha256:b364e87a2038f9946c5e2770c130597538aac528b4a82c1cab5267f337ea7679"}, + {file = "optuna-4.2.0-py3-none-any.whl", hash = "sha256:539a47fda44104eb2b879c883d4640704b482df416ae99d0f24505f6e2280c28"}, + {file = "optuna-4.2.0.tar.gz", hash = "sha256:6ce226483317cf73df133d9ddf300091f946c0ad0dbdbc50b28891049b8400f2"}, ] [package.dependencies] @@ -1760,10 +1857,10 @@ tqdm = "*" [package.extras] benchmark = ["asv (>=0.5.0)", "cma", "virtualenv"] -checking = ["black", "blackdoc", "flake8", "isort", "mypy", "mypy-boto3-s3", "types-PyYAML", "types-redis", "types-setuptools", "types-tqdm", "typing-extensions (>=3.10.0.0)"] -document = ["ase", "cmaes (>=0.10.0)", "fvcore", "kaleido", "lightgbm", "matplotlib (!=3.6.0)", "pandas", "pillow", "plotly (>=4.9.0)", "scikit-learn", "sphinx", "sphinx-copybutton", "sphinx-gallery", "sphinx-rtd-theme (>=1.2.0)", "torch", "torchvision"] -optional = ["boto3", "cmaes (>=0.10.0)", "google-cloud-storage", "matplotlib (!=3.6.0)", "pandas", "plotly (>=4.9.0)", "redis", "scikit-learn (>=0.24.2)", "scipy", "torch"] -test = ["coverage", "fakeredis[lua]", "kaleido", "moto", "pytest", "scipy (>=1.9.2)", "torch"] +checking = ["black", "blackdoc", "flake8", "isort", "mypy", "mypy_boto3_s3", "types-PyYAML", "types-redis", "types-setuptools", "types-tqdm", "typing_extensions (>=3.10.0.0)"] +document = ["ase", "cmaes (>=0.10.0)", "fvcore", "kaleido (<0.4)", "lightgbm", "matplotlib (!=3.6.0)", "pandas", "pillow", "plotly (>=4.9.0)", "scikit-learn", "sphinx", "sphinx-copybutton", "sphinx-gallery", "sphinx-notfound-page", "sphinx_rtd_theme (>=1.2.0)", "torch", "torchvision"] +optional = ["boto3", "cmaes (>=0.10.0)", "google-cloud-storage", "grpcio", "matplotlib (!=3.6.0)", "pandas", "plotly (>=4.9.0)", "protobuf (>=5.28.1)", "redis", "scikit-learn (>=0.24.2)", "scipy", "torch"] +test = ["coverage", "fakeredis[lua]", "grpcio", "kaleido (<0.4)", "moto", "protobuf (>=5.28.1)", "pytest", "scipy (>=1.9.2)", "torch"] [[package]] name = "packaging" @@ -1771,7 +1868,6 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "docs", "tests"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1783,7 +1879,6 @@ version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" -groups = ["dev"] files = [ {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, @@ -1793,14 +1888,23 @@ files = [ qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["docopt", "pytest"] +[[package]] +name = "pbr" +version = "6.1.0" +description = "Python Build Reasonableness" +optional = true +python-versions = ">=2.6" +files = [ + {file = "pbr-6.1.0-py2.py3-none-any.whl", hash = "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a"}, + {file = "pbr-6.1.0.tar.gz", hash = "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24"}, +] + [[package]] name = "pexpect" version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" -groups = ["dev"] -markers = "sys_platform != \"win32\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -1815,7 +1919,6 @@ version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, @@ -1827,7 +1930,6 @@ version = "11.1.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"}, {file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"}, @@ -1916,7 +2018,6 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" -groups = ["analysis"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -1933,7 +2034,6 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" -groups = ["tests"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1943,16 +2043,26 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "ply" +version = "3.11" +description = "Python Lex & Yacc" +optional = true +python-versions = "*" +files = [ + {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, + {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, +] + [[package]] name = "prompt-toolkit" -version = "3.0.48" +version = "3.0.50" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.7.0" -groups = ["dev"] +python-versions = ">=3.8.0" files = [ - {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, - {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, + {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, + {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, ] [package.dependencies] @@ -1964,7 +2074,6 @@ version = "6.1.1" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["main"] files = [ {file = "psutil-6.1.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9ccc4316f24409159897799b83004cb1e24f9819b0dcf9c0b68bdcb6cefee6a8"}, {file = "psutil-6.1.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ca9609c77ea3b8481ab005da74ed894035936223422dc591d6772b147421f777"}, @@ -1995,8 +2104,6 @@ version = "0.7.0" description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" -groups = ["dev"] -markers = "sys_platform != \"win32\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -2008,7 +2115,6 @@ version = "0.24.0" description = "A BibTeX-compatible bibliography processor in Python" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -groups = ["docs"] files = [ {file = "pybtex-0.24.0-py2.py3-none-any.whl", hash = "sha256:e1e0c8c69998452fea90e9179aa2a98ab103f3eed894405b7264e517cc2fcc0f"}, {file = "pybtex-0.24.0.tar.gz", hash = "sha256:818eae35b61733e5c007c3fcd2cfb75ed1bc8b4173c1f70b56cc4c0802d34755"}, @@ -2028,7 +2134,6 @@ version = "1.0.3" description = "A docutils backend for pybtex." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "pybtex-docutils-1.0.3.tar.gz", hash = "sha256:3a7ebdf92b593e00e8c1c538aa9a20bca5d92d84231124715acc964d51d93c6b"}, {file = "pybtex_docutils-1.0.3-py3-none-any.whl", hash = "sha256:8fd290d2ae48e32fcb54d86b0efb8d573198653c7e2447d5bec5847095f430b9"}, @@ -2038,13 +2143,23 @@ files = [ docutils = ">=0.14" pybtex = ">=0.16" +[[package]] +name = "pycparser" +version = "2.22" +description = "C parser in Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + [[package]] name = "pygments" version = "2.19.1" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["dev", "docs"] files = [ {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, @@ -2055,14 +2170,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.3.3" +version = "3.3.4" description = "python code static checker" optional = false python-versions = ">=3.9.0" -groups = ["analysis"] files = [ - {file = "pylint-3.3.3-py3-none-any.whl", hash = "sha256:26e271a2bc8bce0fc23833805a9076dd9b4d5194e2a02164942cb3cdc37b4183"}, - {file = "pylint-3.3.3.tar.gz", hash = "sha256:07c607523b17e6d16e2ae0d7ef59602e332caa762af64203c24b41c27139f36a"}, + {file = "pylint-3.3.4-py3-none-any.whl", hash = "sha256:289e6a1eb27b453b08436478391a48cd53bb0efb824873f949e709350f3de018"}, + {file = "pylint-3.3.4.tar.gz", hash = "sha256:74ae7a38b177e69a9b525d0794bd8183820bfa7eb68cc1bee6e8ed22a42be4ce"}, ] [package.dependencies] @@ -2073,7 +2187,7 @@ dill = [ {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" mccabe = ">=0.6,<0.8" platformdirs = ">=2.2.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -2090,7 +2204,6 @@ version = "3.2.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "pyparsing-3.2.1-py3-none-any.whl", hash = "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1"}, {file = "pyparsing-3.2.1.tar.gz", hash = "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a"}, @@ -2099,13 +2212,31 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyspnego" +version = "0.11.2" +description = "Windows Negotiate Authentication Client and Server" +optional = true +python-versions = ">=3.8" +files = [ + {file = "pyspnego-0.11.2-py3-none-any.whl", hash = "sha256:74abc1fb51e59360eb5c5c9086e5962174f1072c7a50cf6da0bda9a4bcfdfbd4"}, + {file = "pyspnego-0.11.2.tar.gz", hash = "sha256:994388d308fb06e4498365ce78d222bf4f3570b6df4ec95738431f61510c971b"}, +] + +[package.dependencies] +cryptography = "*" +sspilib = {version = ">=0.1.0", markers = "sys_platform == \"win32\""} + +[package.extras] +kerberos = ["gssapi (>=1.6.0)", "krb5 (>=0.3.0)"] +yaml = ["ruamel.yaml"] + [[package]] name = "pytest" version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" -groups = ["tests"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -2128,7 +2259,6 @@ version = "4.1.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.7" -groups = ["tests"] files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, @@ -2147,7 +2277,6 @@ version = "1.1.5" description = "pytest plugin that allows you to add environment variables." optional = false python-versions = ">=3.8" -groups = ["tests"] files = [ {file = "pytest_env-1.1.5-py3-none-any.whl", hash = "sha256:ce90cf8772878515c24b31cd97c7fa1f4481cd68d588419fd45f10ecaee6bc30"}, {file = "pytest_env-1.1.5.tar.gz", hash = "sha256:91209840aa0e43385073ac464a554ad2947cc2fd663a9debf88d03b01e0cc1cf"}, @@ -2166,7 +2295,6 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -2181,7 +2309,6 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "docs"] files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -2244,7 +2371,6 @@ version = "0.2.15" description = "A framework for quantum computing with hardware acceleration." optional = false python-versions = "<3.13,>=3.9" -groups = ["main"] files = [ {file = "qibo-0.2.15-py3-none-any.whl", hash = "sha256:51625e7b1ac7f21e335b50931230437cf866fae7284bdc6b3433e80925d0f091"}, {file = "qibo-0.2.15.tar.gz", hash = "sha256:e002b9f785e66f1eed3f22fecc98b0d79d60d68842bbee6098d6754da981eac1"}, @@ -2265,13 +2391,221 @@ tabulate = ">=0.9.0,<0.10.0" qulacs = ["qulacs (>=0.6.4,<0.7.0)"] torch = ["torch (>=2.1.1,<2.4)"] +[[package]] +name = "qiskit" +version = "0.38.0" +description = "Software for developing quantum computing programs" +optional = true +python-versions = ">=3.7" +files = [ + {file = "qiskit-0.38.0.tar.gz", hash = "sha256:f7bcd2470bed40b2d41bedb8389a0102cb24427579218ea83a240881b1df0282"}, +] + +[package.dependencies] +qiskit-aer = "0.11.0" +qiskit-ibmq-provider = "0.19.2" +qiskit-terra = "0.21.2" + +[package.extras] +all = ["ipywidgets (>=7.3.0)", "matplotlib (>=2.1)", "pillow (>=4.2.1)", "pydot", "pygments (>=2.4)", "pylatexenc (>=1.4)", "qiskit-experiments (>=0.2.0)", "qiskit-finance (>=0.3.3)", "qiskit-machine-learning (>=0.4.0)", "qiskit-nature (>=0.4.1)", "qiskit-optimization (>=0.4.0)", "seaborn (>=0.9.0)"] +experiments = ["qiskit-experiments (>=0.2.0)"] +finance = ["qiskit-finance (>=0.3.3)"] +machine-learning = ["qiskit-machine-learning (>=0.4.0)"] +nature = ["qiskit-nature (>=0.4.1)"] +optimization = ["qiskit-optimization (>=0.4.0)"] +visualization = ["ipywidgets (>=7.3.0)", "matplotlib (>=2.1)", "pillow (>=4.2.1)", "pydot", "pygments (>=2.4)", "pylatexenc (>=1.4)", "seaborn (>=0.9.0)"] + +[[package]] +name = "qiskit-aer" +version = "0.11.0" +description = "Qiskit Aer - High performance simulators for Qiskit" +optional = true +python-versions = ">=3.7" +files = [ + {file = "qiskit-aer-0.11.0.tar.gz", hash = "sha256:d56aa88da4c702f887c015a5f6752dfe90e67d9d348c2f63620e82ec58e83bee"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff8f8b5628e066a0426412752f89b46d657dc9cd1df3fd996d7d15e9464dcad"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:692eff531a5a2b86fa0e105616740cfcb3cc9aef195bacf56b3709a0fbc17f8d"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c88a6b84daab85272c4e80e4aef04a145b40f9e7408038b7bce26f8e7d8edd95"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71f3b5eea0838c0f54c4abe69bcde9e68959b315324f35a01d9becb60b454fb7"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:caa451d7ab50f85051615b9e1f3de6d1f2157bf3f20f1e4ce32012652d619c3e"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e8f1a8a7feca4b32f7484ee396bd4d78268a7a60dabc1990baaf81b22db811c"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32fae81ebc9038ce460af5cb4c4b94c98ea97ea533c822c5cdb5dae1d1384840"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-win32.whl", hash = "sha256:294da358c1957b297fbc5a3d78b4fb42ce0aa4e36d6d3bf369816d64ad493c39"}, + {file = "qiskit_aer-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6490252343f3a50482c4d9c82ffed9199f6e4af147c5914644c01a67b106c0e"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d61e97788e9ceb31f11b13d5f8beed52b532fc0fbfd68ce0a5227b7cf582016b"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dd41f2f4c264625299a9997c9ad05cc4de6084c4d07a748e3234faf181db9c50"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eca95a4f82296aa6ab4a2c3a4ba97c92bf112027b4b6c0c15625684dbe82af6"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:254c45b616ea276562e5cf48ae7b52d14dd04d224284c4ad80aed2fffca9cf8a"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf326579c8c9ff5d793354e52fd86c6e15df27172ca843bef4920933227f6bf5"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:385da5170a18e042b5582949edd3f7664c88cf0eeb5ba2d27ab706dc44389785"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-win32.whl", hash = "sha256:0c25470ae47d8d65b563288bf4215edad9b872af46ed408191c0eeac84124e2f"}, + {file = "qiskit_aer-0.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f4a3487d0516fb1b6ca8a8737d9bbecbf8a242226265936c1de5626a0bd5bd35"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d4e17ac45da04086590e51cae70af8fb9a350e2b181971b8d7705d869c9860a2"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:51f4d4b3fdd7aaae812e018a67ccb364fc801194a0e2e0f3f6623d9e1c1a8431"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:23c8afb685f1aeacd2a0a25bda65e331eef0278accb39d059b0b7ec9617c1c82"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff116ffc09227a01713e5e20d67f77b6443b2af53b51b6414e2fbc1d0c6727a6"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:070fb1fde1ae66b38fa1417474d3f7c3c38a9e3a5d15c51cc6f7d34ee3aff639"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c62cc3d068aa3f7ef78bb08c0b616e826a411366ee4b483224a50fad7e2b9c06"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7d7eeaab23a07855e671ff169a01f8b1f3954fa9a58718378331a32650b75e"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-win32.whl", hash = "sha256:c3d0c8bd70fcbe65673ea17a7492d3ce9f7462fccd1e28be2ca9ebd59340101a"}, + {file = "qiskit_aer-0.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:5758fe43dd3035d2bf8e0072db1a21207300dc8fe60ededa35f2657318cee341"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d4bbdefd1773822366708fff96ff58c5b2811e93e69007c1d78f1dc27a7f3dcd"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd8b840826970d29514ec2aada2e3f42e59700ab1c05713eb6d75d3ac65fbf5e"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:22652978c9338af4b9690066bb09726151ad7ed176e4a9cf4e7a48851eb798cf"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a4cceac02f00c73fd37b465075c3ade7ab50702859321b02ebe7e18a593236c"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4eac1b583f488475c5bb8e394119fc0c2a833638dced3c3cc5155c0fdcf504d4"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9975959dd0fd0667318a0d41eadde2eb94907296ef8174ee79e380d3ef7ff742"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f67f1ec548d4304306c65b386e44cdba2a3b193197d8d940542cd8084042b27c"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-win32.whl", hash = "sha256:32c9dbe8e7fbfcb241396de9c48b589c358ffc20ba58030e52701db27ac89f07"}, + {file = "qiskit_aer-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:41dccaa1b4eb7567aecefecbe66d7065f1c284c8616290ecd66b6f5ba9d1cb48"}, +] + +[package.dependencies] +numpy = ">=1.16.3" +qiskit-terra = ">=0.21.0" +scipy = ">=1.0" + +[package.extras] +dask = ["dask", "distributed"] + +[[package]] +name = "qiskit-ibmq-provider" +version = "0.19.2" +description = "Qiskit provider for accessing the quantum devices and simulators at IBMQ" +optional = true +python-versions = ">=3.7" +files = [ + {file = "qiskit-ibmq-provider-0.19.2.tar.gz", hash = "sha256:489407bed016a04e9db05a2d2c44b50843621408e392ace4d067bad071aa5fc9"}, + {file = "qiskit_ibmq_provider-0.19.2-py3-none-any.whl", hash = "sha256:d31786cf5a54f334387081d82b5b19c4269c176079fab5ad843f8fc8eadbce84"}, +] + +[package.dependencies] +numpy = ">=1.13" +python-dateutil = ">=2.8.0" +qiskit-terra = ">=0.18.0" +requests = ">=2.19" +requests-ntlm = ">=1.1.0" +urllib3 = ">=1.21.1" +websocket-client = ">=1.0.1" +websockets = {version = ">=10.0", markers = "python_version >= \"3.7\""} + +[package.extras] +visualization = ["ipython (>=5.0.0)", "ipyvue (>=1.4.1)", "ipyvuetify (>=1.1)", "ipywidgets (>=7.3.0)", "matplotlib (>=2.1)", "plotly (>=4.4)", "pyperclip (>=1.7)", "seaborn (>=0.9.0)", "traitlets (!=5.0.5)"] + +[[package]] +name = "qiskit-terra" +version = "0.21.2" +description = "Software for developing quantum computing programs" +optional = true +python-versions = ">=3.7" +files = [ + {file = "qiskit-terra-0.21.2.tar.gz", hash = "sha256:355fd3992965afdf2b402b2071b61175ad98498151e0991d229d7afc822a7ec2"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f3761cbbcc9c956cbb642ae24e1250a6a7db7c19daa98440ecab12c0403dfac6"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8391ecf98a5a4854264e314ef0fcd189258dafffc5cfee9ca8e3d6f98e5a1cd"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0d3f5fe530a65bc74a0357107b387539f892f88c42d36f2c79fa3aa91469154"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:268560694645393be65e85f610ad7ba3f3d37b522cebab8afec9c90b32f384d3"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5e29cf7cfe1498630814e8aa4182d48905306ec3c47cfb4ab30e1f793274d7d"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:66f9cef4cdb8cd01968b1b0eb9eae23dcb693458357cfd2b1fdd3de567eec7e6"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82a766c79a8a6ab392f0a9a8e9a30a0732826796c90718a77460eb8ff2b9f7b0"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52cd8acdb61b56f21a773b3a4f2b01ccec6843f9beb98c8ad97984fcc7c66f9a"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-win32.whl", hash = "sha256:a213ae25513caa656e8708390e2e1b41a744b442bf2f8510df46c90822abf239"}, + {file = "qiskit_terra-0.21.2-cp310-cp310-win_amd64.whl", hash = "sha256:d17051c95adcf3fb5483a7b8e81e2096369e03a55526a1838cf930308a4b8edb"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:afde22a4c1ac224fef9aeb5201ca65a115f703d99f42fbae20b7832fd1b723e9"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65db08b7fcd7a736df7c45c4c0e29a4705df2acbe2014ea78a6bedf4394b29da"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:488f8d4e37eec5370f53c5654f22131d6e5e1a4035ef086da3970901c14a79fd"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c7ce76062662898626846a28df82b9dd29bb4b93318d1036670134efda7d267"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd01554a8d7165dad3d6766853b9d7d8038d0b2441cb0c26ac4d8cdd9e492b24"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1652d93559dae2d1a5d8f3599ec64f6ce5ee8de4e33fedf04e3d29b4d4f3eb52"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-win32.whl", hash = "sha256:0a85d38c1727bd0d03a4f366f67bbeab18f888909a98b63b048f89cadbf52c4c"}, + {file = "qiskit_terra-0.21.2-cp37-cp37m-win_amd64.whl", hash = "sha256:92f530bf07172203ea8a9c169cc2a82d526cc6ca683e3733d14f994f5fb78822"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53ca8d909671a698b55d23a920db1744d3220a073b83a53de41c98a50d72282e"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4c665c16c86355e913b0bdb5778555774a47ef8bda4717ebc0f03e6723a32b5d"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ab589e494306ced49643361322b3dc57921d9e35b2c16db4fb2f90fd33ab4bc2"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7efcb8535fbc518683eec1306cfa1561fb4e966a4d48e1640b5dad7413ba4777"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4feeed7aa3887342d0a648b6823332943097b03cda00eb9f30e42e85e93c8825"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:24a4a8a2520e477cd07dd448509d3651e681c4c9e626ce71011b399ba6315635"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a94358f87ec8bf5d3ef516056877c33a049f49d1c44392751310ad5bfde711c0"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdd40b95f7916473b20c68dd68d0a3e69761262bd7ba2fc87ff44d7412de0831"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-win32.whl", hash = "sha256:5030dac8a714b38b0566634227371323aeb1e5bee0ee6877a37951802aaa1785"}, + {file = "qiskit_terra-0.21.2-cp38-cp38-win_amd64.whl", hash = "sha256:32464b0ab7611ce82f3bfe3ae8b7ebb9fa0ab577459ba01d17e541365bc0dafa"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d59338fd7cfd12f53a9ba57a3eadb0c2539ef8eaabcdd34bc2815a50db0952d1"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:904f0dac9c14fa8104559fa073b4deead679f66e3a9521c4631ddd1f19880909"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:71fa936b3f214d0a0c2394f5122e78f3fd92573ca1814b659648ffc7e65d0bbe"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c9382e94febc68fec737f2c78292d90ed491be99ed1b91ffb37e55e08bbf097"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eefdb6afd459734b1881e3a5b91fc3df3a481705b42f4c49811a04ce20e83c32"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22c562997062b7831b3765a7a9da1504ffdb32af0bde4ef20d0397517696ec4b"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a2c6359d4f05eed1904c3b538db7c6fd8b171224593878d21f9ab60ee9a2117"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4cf59748974a73171b99fbc291313d62546ce25291f31315c4af4af1de57b6f"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-win32.whl", hash = "sha256:73c79654b66e4fc7c953ba094fc1b91973bc814eeeb58d51024b5ffdb88f2347"}, + {file = "qiskit_terra-0.21.2-cp39-cp39-win_amd64.whl", hash = "sha256:59f069f7997726a6fa5ac715d7f667aaf12c54acf26f2146900f673f12b69a4a"}, +] + +[package.dependencies] +dill = ">=0.3" +numpy = ">=1.17" +ply = ">=3.10" +psutil = ">=5" +python-dateutil = ">=2.8.0" +retworkx = ">=0.11.0" +scipy = ">=1.5" +stevedore = ">=3.0.0" +symengine = {version = ">=0.9", markers = "platform_machine == \"x86_64\" or platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"amd64\" or platform_machine == \"arm64\""} +sympy = ">=1.3" +tweedledum = ">=1.1,<2.0" + +[package.extras] +all = ["ipywidgets (>=7.3.0)", "matplotlib (>=3.3)", "pillow (>=4.2.1)", "pydot", "pygments (>=2.4)", "pylatexenc (>=1.4)", "python-constraint (>=1.4)", "seaborn (>=0.9.0)", "z3-solver (>=4.7)"] +bip-mapper = ["cplex", "docplex"] +crosstalk-pass = ["z3-solver (>=4.7)"] +csp-layout-pass = ["python-constraint (>=1.4)"] +toqm = ["qiskit-toqm (>=0.0.4)"] +visualization = ["ipywidgets (>=7.3.0)", "matplotlib (>=3.3)", "pillow (>=4.2.1)", "pydot", "pygments (>=2.4)", "pylatexenc (>=1.4)", "seaborn (>=0.9.0)"] + +[[package]] +name = "qmatchatea" +version = "1.1.4" +description = "Quantum matcha TEA python library for tensor network emulation of quantum circuits." +optional = true +python-versions = ">=3.8" +files = [ + {file = "qmatchatea-1.1.4-py3-none-any.whl", hash = "sha256:544886607e9799719a965701214a5b2ebf0584a746ca553543c03a032bfee60f"}, + {file = "qmatchatea-1.1.4.tar.gz", hash = "sha256:5598fac2ae55497b157a003ad6518df0a898f2448213d6eca8387e2b3b2eb63e"}, +] + +[package.dependencies] +joblib = "*" +matplotlib = ">=3.1.3" +mpi4py = "*" +numpy = ">=1.18.1" +qiskit = "0.38.0" +qtealeaves = ">=1.1.7,<=1.1.12" +scipy = ">=1.4.1" + +[[package]] +name = "qtealeaves" +version = "1.1.12" +description = "Quantum TEA's python tensor network library" +optional = true +python-versions = ">=3.8" +files = [ + {file = "qtealeaves-1.1.12-py3-none-any.whl", hash = "sha256:964e06dd0cd87639918c15cbc0114b4e5ed1dce09957f56df39d4dcc07c9bfbc"}, + {file = "qtealeaves-1.1.12.tar.gz", hash = "sha256:5c2ac927b61b22224480a7713c387bc423f59e57fa7dc7a59f196e8d9aa661e6"}, +] + +[package.dependencies] +h5py = "*" +joblib = "*" +matplotlib = "*" +mpmath = "*" +numpy = "*" +scipy = "*" + [[package]] name = "quimb" version = "1.10.0" description = "Quantum information and many-body library." optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "quimb-1.10.0-py3-none-any.whl", hash = "sha256:4d270e71c9f03a4a41862682fbffcd076c61d7e40c4b3785a31ae869ce8830c4"}, {file = "quimb-1.10.0.tar.gz", hash = "sha256:e79f1be2f9895d966479ccabbd5ec087fc047baad83d3fb08fed264e2d7cc3ff"}, @@ -2301,7 +2635,6 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -2317,13 +2650,75 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-ntlm" +version = "1.3.0" +description = "This package allows for HTTP NTLM authentication using the requests library." +optional = true +python-versions = ">=3.8" +files = [ + {file = "requests_ntlm-1.3.0-py3-none-any.whl", hash = "sha256:4c7534a7d0e482bb0928531d621be4b2c74ace437e88c5a357ceb7452d25a510"}, + {file = "requests_ntlm-1.3.0.tar.gz", hash = "sha256:b29cc2462623dffdf9b88c43e180ccb735b4007228a542220e882c58ae56c668"}, +] + +[package.dependencies] +cryptography = ">=1.3" +pyspnego = ">=0.4.0" +requests = ">=2.0.0" + +[[package]] +name = "retworkx" +version = "0.16.0" +description = "A python graph library implemented in Rust" +optional = true +python-versions = ">=3.9" +files = [ + {file = "retworkx-0.16.0-py3-none-any.whl", hash = "sha256:64fc8f7f00397f76e9add1ff1e1fff2e4560aca6d03a6f4dab9d811a36d0fca3"}, +] + +[package.dependencies] +numpy = ">=1.16.0,<3" +rustworkx = "0.16.0" + +[package.extras] +all = ["matplotlib (>=3.0)", "pillow (>=5.4)"] +graphviz = ["pillow (>=5.4)"] +mpl = ["matplotlib (>=3.0)"] + +[[package]] +name = "rustworkx" +version = "0.16.0" +description = "A python graph library implemented in Rust" +optional = true +python-versions = ">=3.9" +files = [ + {file = "rustworkx-0.16.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:476a6c67b0142acd941691943750cc6737a48372304489969c2b62d30aaf4c27"}, + {file = "rustworkx-0.16.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bef2ef42870f806af93979b457e240f6dfa4f867ca33965c620f3a804409ed3a"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0db3a73bf68b3e66c08322a2fc95d3aa663d037d9b4e49c3509da4898d3529cc"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f12a13d7486234fa2a84746d5e41f436bf9df43548043e7a232f48804ff8c61"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89efd5c3a4653ddacc55ca39f28b261d43deec7d678f8f8fc6b76b5087f1dfea"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec0c12aac8c54910ace20ac6ada4b890cd39f95f69100514715f8ad7af9041e4"}, + {file = "rustworkx-0.16.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d650e39fc1a1534335f7517358ebfc3478bb235428463cfcd7c5750d50377b33"}, + {file = "rustworkx-0.16.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:293180b83509ee9bff4c3af7ccc1024f6528d61b65d0cb7320bd31924f10cb71"}, + {file = "rustworkx-0.16.0-cp39-abi3-win32.whl", hash = "sha256:040c4368729cf502f756a3b0ff5f1c6915fc389f74dcc6afc6c3833688c97c01"}, + {file = "rustworkx-0.16.0-cp39-abi3-win_amd64.whl", hash = "sha256:905df608843c32fa45ac023687769fe13056edf7584474c801d5c50705d76e9b"}, + {file = "rustworkx-0.16.0.tar.gz", hash = "sha256:9f0dcb83f38d5ca2c3a683eb9b6951c8aec3262fbfe5141946a7ee5ba37e0bb6"}, +] + +[package.dependencies] +numpy = ">=1.16.0,<3" + +[package.extras] +all = ["matplotlib (>=3.0)", "pillow (>=5.4)"] +graphviz = ["pillow (>=5.4)"] +mpl = ["matplotlib (>=3.0)"] + [[package]] name = "scipy" version = "1.13.1" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.9" -groups = ["main"] files = [ {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, @@ -2366,7 +2761,6 @@ version = "75.8.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" -groups = ["dev"] files = [ {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, @@ -2387,7 +2781,6 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main", "docs"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -2399,7 +2792,6 @@ version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = false python-versions = "*" -groups = ["docs"] files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, @@ -2411,7 +2803,6 @@ version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" -groups = ["docs"] files = [ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, @@ -2423,7 +2814,6 @@ version = "5.3.0" description = "Python documentation generator" optional = false python-versions = ">=3.6" -groups = ["docs"] files = [ {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, @@ -2459,7 +2849,6 @@ version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, @@ -2477,7 +2866,6 @@ version = "0.5.2" description = "Add a copy button to each of your code cells." optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, @@ -2496,7 +2884,6 @@ version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -2513,7 +2900,6 @@ version = "2.5.0" description = "Sphinx extension for BibTeX style citations." optional = false python-versions = ">=3.6" -groups = ["docs"] files = [ {file = "sphinxcontrib-bibtex-2.5.0.tar.gz", hash = "sha256:71b42e5db0e2e284f243875326bf9936aa9a763282277d75048826fef5b00eaa"}, {file = "sphinxcontrib_bibtex-2.5.0-py3-none-any.whl", hash = "sha256:748f726eaca6efff7731012103417ef130ecdcc09501b4d0c54283bf5f059f76"}, @@ -2532,7 +2918,6 @@ version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -2549,7 +2934,6 @@ version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -2566,7 +2950,6 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" -groups = ["docs"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -2581,7 +2964,6 @@ version = "0.9.10" description = "A Sphinx extension for rendering math in HTML pages" optional = false python-versions = ">=3.7" -groups = ["docs"] files = [ {file = "sphinxcontrib_katex-0.9.10-py3-none-any.whl", hash = "sha256:4e5f0b18761cd2cd058a1b2392f42a7edea4cc5beaa504a44aaee07d17ace9b7"}, {file = "sphinxcontrib_katex-0.9.10.tar.gz", hash = "sha256:309a92dae245dbc584ff7ea5fb6549727bae95e4e52008b74d259d2fd1ad0dec"}, @@ -2596,7 +2978,6 @@ version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -2613,7 +2994,6 @@ version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -2630,7 +3010,6 @@ version = "2.0.37" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e"}, {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069"}, @@ -2720,13 +3099,122 @@ postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] pymysql = ["pymysql"] sqlcipher = ["sqlcipher3_binary"] +[[package]] +name = "sspilib" +version = "0.2.0" +description = "SSPI API bindings for Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "sspilib-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34f566ba8b332c91594e21a71200de2d4ce55ca5a205541d4128ed23e3c98777"}, + {file = "sspilib-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b11e4f030de5c5de0f29bcf41a6e87c9fd90cb3b0f64e446a6e1d1aef4d08f5"}, + {file = "sspilib-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e82f87d77a9da62ce1eac22f752511a99495840177714c772a9d27b75220f78"}, + {file = "sspilib-0.2.0-cp310-cp310-win32.whl", hash = "sha256:e436fa09bcf353a364a74b3ef6910d936fa8cd1493f136e517a9a7e11b319c57"}, + {file = "sspilib-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:850a17c98d2b8579b183ce37a8df97d050bc5b31ab13f5a6d9e39c9692fe3754"}, + {file = "sspilib-0.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:a4d788a53b8db6d1caafba36887d5ac2087e6b6be6f01eb48f8afea6b646dbb5"}, + {file = "sspilib-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e0943204c8ba732966fdc5b69e33cf61d8dc6b24e6ed875f32055d9d7e2f76cd"}, + {file = "sspilib-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1cdfc5ec2f151f26e21aa50ccc7f9848c969d6f78264ae4f38347609f6722df"}, + {file = "sspilib-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a6c33495a3de1552120c4a99219ebdd70e3849717867b8cae3a6a2f98fef405"}, + {file = "sspilib-0.2.0-cp311-cp311-win32.whl", hash = "sha256:400d5922c2c2261009921157c4b43d868e84640ad86e4dc84c95b07e5cc38ac6"}, + {file = "sspilib-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3e7d19c16ba9189ef8687b591503db06cfb9c5eb32ab1ca3bb9ebc1a8a5f35c"}, + {file = "sspilib-0.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:f65c52ead8ce95eb78a79306fe4269ee572ef3e4dcc108d250d5933da2455ecc"}, + {file = "sspilib-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:abac93a90335590b49ef1fc162b538576249c7f58aec0c7bcfb4b860513979b4"}, + {file = "sspilib-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1208720d8e431af674c5645cec365224d035f241444d5faa15dc74023ece1277"}, + {file = "sspilib-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48dceb871ecf9cf83abdd0e6db5326e885e574f1897f6ae87d736ff558f4bfa"}, + {file = "sspilib-0.2.0-cp312-cp312-win32.whl", hash = "sha256:bdf9a4f424add02951e1f01f47441d2e69a9910471e99c2c88660bd8e184d7f8"}, + {file = "sspilib-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:40a97ca83e503a175d1dc9461836994e47e8b9bcf56cab81a2c22e27f1993079"}, + {file = "sspilib-0.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8ffc09819a37005c66a580ff44f544775f9745d5ed1ceeb37df4e5ff128adf36"}, + {file = "sspilib-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:40ff410b64198cf1d704718754fc5fe7b9609e0c49bf85c970f64c6fc2786db4"}, + {file = "sspilib-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:02d8e0b6033de8ccf509ba44fdcda7e196cdedc0f8cf19eb22c5e4117187c82f"}, + {file = "sspilib-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7943fe14f8f6d72623ab6401991aa39a2b597bdb25e531741b37932402480f"}, + {file = "sspilib-0.2.0-cp313-cp313-win32.whl", hash = "sha256:b9044d6020aa88d512e7557694fe734a243801f9a6874e1c214451eebe493d92"}, + {file = "sspilib-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:c39a698491f43618efca8776a40fb7201d08c415c507f899f0df5ada15abefaa"}, + {file = "sspilib-0.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:863b7b214517b09367511c0ef931370f0386ed2c7c5613092bf9b106114c4a0e"}, + {file = "sspilib-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a0ede7afba32f2b681196c0b8520617d99dc5d0691d04884d59b476e31b41286"}, + {file = "sspilib-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bd95df50efb6586054963950c8fa91ef994fb73c5c022c6f85b16f702c5314da"}, + {file = "sspilib-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9460258d3dc3f71cc4dcfd6ac078e2fe26f272faea907384b7dd52cb91d9ddcc"}, + {file = "sspilib-0.2.0-cp38-cp38-win32.whl", hash = "sha256:6fa9d97671348b97567020d82fe36c4211a2cacf02abbccbd8995afbf3a40bfc"}, + {file = "sspilib-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:32422ad7406adece12d7c385019b34e3e35ff88a7c8f3d7c062da421772e7bfa"}, + {file = "sspilib-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6944a0d7fe64f88c9bde3498591acdb25b178902287919b962c398ed145f71b9"}, + {file = "sspilib-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0216344629b0f39c2193adb74d7e1bed67f1bbd619e426040674b7629407eba9"}, + {file = "sspilib-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c5f84b9f614447fc451620c5c44001ed48fead3084c7c9f2b9cefe1f4c5c3d0"}, + {file = "sspilib-0.2.0-cp39-cp39-win32.whl", hash = "sha256:b290eb90bf8b8136b0a61b189629442052e1a664bd78db82928ec1e81b681fb5"}, + {file = "sspilib-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:404c16e698476e500a7fe67be5457fadd52d8bdc9aeb6c554782c8f366cc4fc9"}, + {file = "sspilib-0.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:8697e5dd9229cd3367bca49fba74e02f867759d1d416a717e26c3088041b9814"}, + {file = "sspilib-0.2.0.tar.gz", hash = "sha256:4d6cd4290ca82f40705efeb5e9107f7abcd5e647cb201a3d04371305938615b8"}, +] + +[[package]] +name = "stevedore" +version = "5.4.0" +description = "Manage dynamic plugins for Python applications" +optional = true +python-versions = ">=3.9" +files = [ + {file = "stevedore-5.4.0-py3-none-any.whl", hash = "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857"}, + {file = "stevedore-5.4.0.tar.gz", hash = "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d"}, +] + +[package.dependencies] +pbr = ">=2.0.0" + +[[package]] +name = "symengine" +version = "0.13.0" +description = "Python library providing wrappers to SymEngine" +optional = true +python-versions = "<4,>=3.8" +files = [ + {file = "symengine-0.13.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:259fd4111c7a70c72bdff5686de1949e8132baeb612eacdaf8837720c6fe449b"}, + {file = "symengine-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:44f2eb28a1e36db0bbd6679435412f79da9743bf9c1cb3eff25e0c343b7ddd48"}, + {file = "symengine-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d141712fa14d9138bd19e64b10392f850c68d88cd7db29f1bda33e32d1095559"}, + {file = "symengine-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:830226d933bfcdb93546e4062541627d9a3bc7a178a63fb16c002eb5c5221938"}, + {file = "symengine-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a08090163819a0bbfa97d64bd2d8dac2c5268147ed9c242799d7f7e8728a6f4e"}, + {file = "symengine-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1e435dcd8ed25e4c7c21ab1c0376be910efc7f35da76d532367df27b359f0358"}, + {file = "symengine-0.13.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:da0eba7e106095cdce88eb275c8a9d7c4586ad88f229394c53e1184155c00745"}, + {file = "symengine-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b0c175f4f895a73a925508af03faf7efd6cad8593256bbdb5346bd996d3ec5c8"}, + {file = "symengine-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e58d1e2abd08381aa0cf24c88c0e8b7f592df92619b51e32d36835fbd2dd6ae8"}, + {file = "symengine-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db745f2c7a3c5e83510cf4decb43201f43552dfb05ad8af9787c89708be9ede"}, + {file = "symengine-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2572c98b09ac284db6ecff63f6170461194dc94c4209afd34c092ec67873d85"}, + {file = "symengine-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:12727f02a2919f005aee48e68e0cbb70cf857b19385857b4d985d1c9b075f620"}, + {file = "symengine-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cf91d24f1bfd6d53228593c7804dd106b71b19674d5afc4fa322d516e1793bdd"}, + {file = "symengine-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5615b7eb68890917abd390ebb10434a949165f6064741c1a8cc345fee14e855"}, + {file = "symengine-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb92bdf0890de264abaeacbfbdbd4dd7444b94057bd47958d913b662e549ad8a"}, + {file = "symengine-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3bce486fbc0b87970ed1b10ca9d5cafb1fd6b66382fe631261d83592851d7e"}, + {file = "symengine-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7e6bae9cfcdde2775d92fbb0abe3ef04e32f65ebc4c2d164ca33f4da202d4a7"}, + {file = "symengine-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:bced0a1dbdb94737c299384c85ddbad6944ce8dadc334f7bb8dbbd8f6c965807"}, + {file = "symengine-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d34df77971538e4c29f2d8e5ef7f459c2179465e6cdb7dfd48b79b87ecd8f4d"}, + {file = "symengine-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab2661d9b18867e7c6edbfa7a74b8b0a2a694bd24aa08003dc3214f77cb9d6f2"}, + {file = "symengine-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53f27b9013878ee4419d8e853664d8ae4b68419e3f4b9b5b7f503d32bf904755"}, + {file = "symengine-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:27987f75ce08c64f453e2b9b74fec6ffc5ca418c4deca0b75580979d4a4e242a"}, + {file = "symengine-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9ea9410330ea15ed4137d7a0a3c43caccacb71490e18036ce5182d08c93baf8"}, + {file = "symengine-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:5031eb7a5c6675d5195bb57f93cc7d9ac5a7a9a826d4ad6f6b2927746ed7e6e6"}, + {file = "symengine-0.13.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ce0e5dfb19943bcf3e44a4485bcac4c5533ba3705c63083494eed0b3bf246076"}, + {file = "symengine-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c3b77dc54bf1181f6bd3b3338c4e6e5973a8b0fa20a189d15563ef5626e57b04"}, + {file = "symengine-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca7c3f6c168f6f5b06b421833c3d3baae56067a94b671bdffbe09b8e4fefd9be"}, + {file = "symengine-0.13.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:847523de682416811bacb3ad11507e663b3522fbb35cd27184757e9956d0eaf0"}, + {file = "symengine-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2fc1b7d96426463f0c9011e9fb88459d906477c1baa8a996dde6fb2bfa99d4"}, + {file = "symengine-0.13.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:6e371bb2da3867085779c1c21bbb1c85f9634c76c8a76c08562ea113e3dfcd85"}, + {file = "symengine-0.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf7c62478b19683d54e4d93faa5b89303beae25db0c503a105a70d266dc99fa9"}, + {file = "symengine-0.13.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdb21158cf3e2ba87e441f21ecc7724f108b8db17c0fd1880f9f531602bab1f3"}, + {file = "symengine-0.13.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1848d366b359ff69ef5dac148b30ca04c7339a7d3bcab28419d411e68c0cc011"}, + {file = "symengine-0.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242f817e890a0a50d52ed6b2bfd1aad19636a58db700c7995bbe1ceeaebd9d08"}, + {file = "symengine-0.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:af79cf2b9645fb55216850185987b2e9347db71e42e87b6402e4bbd41710b316"}, + {file = "symengine-0.13.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:17ae9d1c781a60ac48d07fa30f39a2d237f9da95e9e81f6a24b1c16908e9cad2"}, + {file = "symengine-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5264157a95f6d09dd044cee6abcbc176e649c487638b7f32199f387f37ad82a5"}, + {file = "symengine-0.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01a6d0829b4881d5f831ae7848eb0d82b80d8b46b5689f1bf27069672370f75"}, + {file = "symengine-0.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35ec68b11c2df2be1a236d0c028edb5b331909b16666d7a9fe99a4a5810afec7"}, + {file = "symengine-0.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4357fed87083e8719fcffd8bd0e7ddd16172e319343362512f681e472ac5668"}, + {file = "symengine-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a41be31816e5e51e9063bf26de07faf3751de7a133dbbec149632de702a28e18"}, + {file = "symengine-0.13.0.tar.gz", hash = "sha256:ab83a08897ebf12579702c2b71ba73d4732fb706cc4291d810aedf39c690c14c"}, +] + [[package]] name = "sympy" version = "1.13.3" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73"}, {file = "sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9"}, @@ -2744,7 +3232,6 @@ version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -2759,7 +3246,6 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["analysis", "tests"] files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -2794,7 +3280,6 @@ files = [ {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] -markers = {analysis = "python_version < \"3.11\"", tests = "python_full_version <= \"3.11.0a6\""} [[package]] name = "tomlkit" @@ -2802,7 +3287,6 @@ version = "0.13.2" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" -groups = ["analysis"] files = [ {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, @@ -2814,7 +3298,6 @@ version = "1.0.0" description = "List processing tools and functional utilities" optional = false python-versions = ">=3.8" -groups = ["main"] files = [ {file = "toolz-1.0.0-py3-none-any.whl", hash = "sha256:292c8f1c4e7516bf9086f8850935c799a874039c8bcf959d47b600e4c44a6236"}, {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, @@ -2826,7 +3309,6 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -2848,7 +3330,6 @@ version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" -groups = ["dev"] files = [ {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, @@ -2858,18 +3339,68 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] +[[package]] +name = "tweedledum" +version = "1.1.1" +description = "A library for synthesizing and manipulating quantum circuits" +optional = true +python-versions = ">=3.6" +files = [ + {file = "tweedledum-1.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b563c8324bbf5ed9ed57399a1eca34d8a82cb146b3011154e3df749753b75fe5"}, + {file = "tweedledum-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a5d734fed09a479afc0ec8fa91764ac9411821c27396e1b7d4a64393e689271d"}, + {file = "tweedledum-1.1.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a7dc0a9674e90808b26e24410bff7e5385d2b21ddf7068fc9c7d020ac46cefd8"}, + {file = "tweedledum-1.1.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:be97848edb19473eb653c58515605a636da1dc4a4650e291f3f05824c9dac005"}, + {file = "tweedledum-1.1.1-cp310-cp310-win32.whl", hash = "sha256:eae6a32207f3f8daf17806b90b2390e13f418b00a62384d029e13f215249df6b"}, + {file = "tweedledum-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:ab7a800d6266c98a30b0e8dc3e13cf49c8145012dfa199c9cc4d58d598a54218"}, + {file = "tweedledum-1.1.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:6b8fa90b5303a6534ef332019ccdbb93ba969993cd7b78395ab31cb4c48a718e"}, + {file = "tweedledum-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cad30654036a36afee0fb879a9cc3f26b33655d8a833425704b6dbb6d4caddfb"}, + {file = "tweedledum-1.1.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4943f5d628f9adb95244b8bac79b7978f810bdaa5025e9930a625161a0d72dad"}, + {file = "tweedledum-1.1.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:09dbf20f792e97211270dfa2b3033ead0ce11fd65cc03781a542f36bccd7f1c1"}, + {file = "tweedledum-1.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bbe9a20d71576465051720eac72aa7f95fae39b4e4feea44f690e1ba856e99a"}, + {file = "tweedledum-1.1.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57687300cca24c2f8fb340d6fa2233450a054647c736dc84958aac4d235b8542"}, + {file = "tweedledum-1.1.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:828808a47c2c67a10e1cf8692ede0bcf2732e5ace8b910bdcb7a2c0bb82440d8"}, + {file = "tweedledum-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:451a10c32c58bf4726ce03f6cce9a93fb5332e679b7dbf48ef69c6fa93493243"}, + {file = "tweedledum-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f0500f8088cf142bfc4dd07a81f3a344603755602dc5f51acde588a36e538ed5"}, + {file = "tweedledum-1.1.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:86fac8e040d1645cfb3d623d949523eb1d367c2eee51fd5843536955104fd1ed"}, + {file = "tweedledum-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fdee0b3b044db8e5d74001fbe25201e0db31be529d47785d2a70e22b7ff63f4a"}, + {file = "tweedledum-1.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0fd7ca92719fcb6a2437a16fd0281809fc57acb8a86ebf41fd06fe8faca1e14d"}, + {file = "tweedledum-1.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:003d92abb1c49e824b8c05857ae745959981174a082dd8c5a66ab1f55855ced3"}, + {file = "tweedledum-1.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6b2fb3d20e21dbe97e9776d0c0b33c0a3dab8e4ac03a5434e9bfd11c9b3a998"}, + {file = "tweedledum-1.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:157392c983762e8a3f7ab523b0cfa4c78fbe83e28e0f1eee59e623636ddfe1ec"}, + {file = "tweedledum-1.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdb3c526f86fcd3d2c8794d1a3d5836ece2cf6f6c9d8e1ee8036b30d24ce29b1"}, + {file = "tweedledum-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:57201c605b1d9045c135e72c521cbe537d8da6d534daa76e349c27fc1177681c"}, + {file = "tweedledum-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:099b1826f213bd4006dcd02526377b81134538fe1377e4cb70a07ba223ae958a"}, + {file = "tweedledum-1.1.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:80f99d7f9dee78f73796b9df2bc836c02f9bfc5a55eec65dda20899d96d09754"}, + {file = "tweedledum-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6f8cbd4cb6933d867e28ff7efc6030eceb1e4caef5c1bed5dfe7d097f63e6c28"}, + {file = "tweedledum-1.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:86da69130494c972d751ab61fdb209d40f079b77d5b3b833e83f26cee3c1a2fc"}, + {file = "tweedledum-1.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4385265171ee53d12d64429d65f0609f57a171d646a61366e3354eddc5c95778"}, + {file = "tweedledum-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:956f34ca2f6edaaafeaeef5f08db2abd54e4b5371a861ad68065d88b63d157b2"}, + {file = "tweedledum-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a3d4686fd1a8e8c86300e004acd73dd21e35a65f66625d784b2292280e46269"}, + {file = "tweedledum-1.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a08c535ef2ebcb326d2388bb4430d52f630ce43386f8b21a42e761e9e30394c4"}, + {file = "tweedledum-1.1.1-cp38-cp38-win32.whl", hash = "sha256:a032f0b6f6143dccee115b14a72780bc5813ccc552f3b1e9d519cb41e2d3ee50"}, + {file = "tweedledum-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:0da0905cd6c08b99d772b2b97f15ccfa80758c49143c3eff131b9480eba6f3fd"}, + {file = "tweedledum-1.1.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:1c73247309b6853b19906df594f7d0a8664bf3490ee2fb25621f617099525ffc"}, + {file = "tweedledum-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cd6cd64ccfc10db296f17e20713265bd91899774a34bcdf788c002c48514469e"}, + {file = "tweedledum-1.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b6aeba18fad932e7dd7715758c97f5aaa287b2726cb4ca9eea7d769fcd607f90"}, + {file = "tweedledum-1.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e243c2f70a4e4758cbdd45b31cdd72eb4816ace7029bdfe7e706cc37015f72e"}, + {file = "tweedledum-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f60f04dcc6e6162c3ce9eb058bb6853cfdd7c8dfefb1f1b428e94d0633a7cc"}, + {file = "tweedledum-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4710519655c55c0b74b6c8abc39f24493f3a8a6c7854af362f4c7953d16036b"}, + {file = "tweedledum-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c305fe9365f449cc3ad379ecf823f1ba6b734cdec56a618fbef11c83a54cede"}, + {file = "tweedledum-1.1.1-cp39-cp39-win32.whl", hash = "sha256:d4bf1f03d11cdc02c32a7771fa23c7de136e7cfa2920f508b2b3bc93d8b29c50"}, + {file = "tweedledum-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:98818ca8cae7a1d95ca05b219ffbcaf92a4fec200ff245e3ddf3ffc616977490"}, + {file = "tweedledum-1.1.1.tar.gz", hash = "sha256:58d6f7a988b10c31be3faa1faf3e58288ef7e8159584bfa6ded45742f390309f"}, +] + [[package]] name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "analysis"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -markers = {analysis = "python_version < \"3.11\""} [[package]] name = "urllib3" @@ -2877,7 +3408,6 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["docs"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -2895,20 +3425,111 @@ version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" -groups = ["dev"] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] +[[package]] +name = "websocket-client" +version = "1.8.0" +description = "WebSocket client for Python with low level API options" +optional = true +python-versions = ">=3.8" +files = [ + {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, + {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "websockets" +version = "14.2" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = true +python-versions = ">=3.9" +files = [ + {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"}, + {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"}, + {file = "websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610"}, + {file = "websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3"}, + {file = "websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980"}, + {file = "websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8"}, + {file = "websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7"}, + {file = "websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f"}, + {file = "websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d"}, + {file = "websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d"}, + {file = "websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2"}, + {file = "websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166"}, + {file = "websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f"}, + {file = "websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910"}, + {file = "websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c"}, + {file = "websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473"}, + {file = "websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473"}, + {file = "websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56"}, + {file = "websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142"}, + {file = "websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d"}, + {file = "websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a"}, + {file = "websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b"}, + {file = "websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c"}, + {file = "websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967"}, + {file = "websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990"}, + {file = "websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda"}, + {file = "websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95"}, + {file = "websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3"}, + {file = "websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9"}, + {file = "websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267"}, + {file = "websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe"}, + {file = "websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205"}, + {file = "websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce"}, + {file = "websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e"}, + {file = "websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad"}, + {file = "websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03"}, + {file = "websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f"}, + {file = "websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5"}, + {file = "websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a"}, + {file = "websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20"}, + {file = "websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2"}, + {file = "websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307"}, + {file = "websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc"}, + {file = "websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f"}, + {file = "websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe"}, + {file = "websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12"}, + {file = "websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7"}, + {file = "websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5"}, + {file = "websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0"}, + {file = "websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258"}, + {file = "websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0"}, + {file = "websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4"}, + {file = "websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc"}, + {file = "websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661"}, + {file = "websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef"}, + {file = "websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29"}, + {file = "websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c"}, + {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2"}, + {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c"}, + {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a"}, + {file = "websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3"}, + {file = "websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f"}, + {file = "websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42"}, + {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f"}, + {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574"}, + {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270"}, + {file = "websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365"}, + {file = "websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b"}, + {file = "websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5"}, +] + [[package]] name = "zipp" version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" -groups = ["main", "docs"] -markers = "python_version < \"3.10\"" files = [ {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, @@ -2926,6 +3547,6 @@ type = ["pytest-mypy"] cuda = ["cupy-cuda11x", "cuquantum-python-cu11", "mpi4py"] [metadata] -lock-version = "2.1" +lock-version = "2.0" python-versions = "^3.9,<3.13" -content-hash = "9bb131aa93c985589719d05b4a257d3741893cf44b239f5e1d192401635a59dc" +content-hash = "ab02da47daabe387a631d237597dff0ad3dc0041011b300e0bf381385b8e591d" diff --git a/pyproject.toml b/pyproject.toml index 928f5c9..fbea561 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,8 +25,10 @@ qibo = "^0.2.8" quimb = { version = "^1.6.0", extras = ["tensor"] } cupy-cuda11x = { version = "^11.6.0", optional = true } cuquantum-python-cu11 = { version = "^23.3.0", optional = true } +qmatchatea = { version = "^1.1.4", optional = true } mpi4py = { version = "^3.1.5", optional = true } + [tool.poetry.extras] cuda = ["cupy-cuda11x", "cuquantum-python-cu11", "mpi4py"] From a609dfaa10b22c6ae412ee0b39307d9c66c4b023 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 29 Jan 2025 14:44:44 +0100 Subject: [PATCH 15/41] chore: rerun pre-commit --- .pre-commit-config.yaml | 2 +- src/qibotn/backends/qmatchatea.py | 99 +++++++++++++------------------ 2 files changed, 41 insertions(+), 60 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d8d655..42147ca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - id: isort args: ["--profile", "black"] - repo: https://github.com/PyCQA/docformatter - rev: v1.7.5 + rev: master hooks: - id: docformatter additional_dependencies: [tomli] diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 0ae7922..ac3d934 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -27,25 +27,49 @@ class QMatchaTeaBackend(QibotnBackend): # TODO: update this function whenever ``set_device`` and ``set_precision`` # are set (?) self._setup_qmatchatea_backend() - self._observables = qtealeaves.observables.TNObservables() - @property - def observables(self): - """Observables measured after TN execution.""" - return self._observables + def configure_tn_simulation( + self, + ansatz: str = "MPS", + convergence_params=None, + ): + """Configure TN simulation given Quantum Matcha Tea interface. - @observables.setter - def observables(self, observables: dict): - """Set the observables to be measured after TN execution. - - It accepts a dict of objects among the ones proposed in ``qtealeaves.observables``. + Args: + ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" + or Matrix Product States "MPS" (default). + convergence_params (qmatchatea.utils.QCConvergenceParameters): + 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 + for more instructions. If not passed, the default values proposed + by Quantum Matcha Tea's authors are set. """ - self._observables = qtealeaves.observables.TNObservables() - for obs in observables: - if isinstance(obs, qtealeaves.observables.tnobase._TNObsBase): - self._observables += obs - else: - raise TypeError("Expected an instance of TNObservables") + + # Set configurations or defaults + self.convergence_params = ( + convergence_params or qmatchatea.QCConvergenceParameters() + ) + self.ansatz = ansatz + + def _setup_qmatchatea_backend(self): + """Configure qmatchatea QCBackend object.""" + + qmatchatea_device = ( + "cpu" if "CPU" in self.device else "gpu" if "GPU" in self.device else None + ) + 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 + self.qmatchatea_backend = qmatchatea.QCBackend( + backend="PY", # The only alternative is Fortran, but we use Python here + precision=qmatchatea_precision, + device=qmatchatea_device, + ansatz=self.ansatz, + ) def execute_circuit( self, @@ -184,49 +208,6 @@ class QMatchaTeaBackend(QibotnBackend): return np.real(results.observables["custom_hamiltonian"]) - def configure_tn_simulation( - self, - ansatz: str = "MPS", - convergence_params=None, - ): - """Configure TN simulation given Quantum Matcha Tea interface. - - Args: - ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" - or Matrix Product States "MPS" (default). - convergence_params (qmatchatea.utils.QCConvergenceParameters): - 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 - for more instructions. If not passed, the default values proposed - by Quantum Matcha Tea's authors are set. - """ - - # Set configurations or defaults - self.convergence_params = ( - convergence_params or qmatchatea.QCConvergenceParameters() - ) - self.ansatz = ansatz - - def _setup_qmatchatea_backend(self): - """Configure qmatchatea QCBackend object.""" - - qmatchatea_device = ( - "cpu" if "CPU" in self.device else "gpu" if "GPU" in self.device else None - ) - 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 - self.qmatchatea_backend = qmatchatea.QCBackend( - backend="PY", # The only alternative is Fortran, but we use Python here - precision=qmatchatea_precision, - device=qmatchatea_device, - ansatz=self.ansatz, - ) - def _qibocirc_to_qiskitcirc(self, qibo_circuit) -> qiskit.QuantumCircuit: """Convert a Qibo Circuit into a Qiskit Circuit.""" # Convert the circuit to QASM 2.0 to qiskit From 97a15689771aed28ef500992fd225375b771decd Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 29 Jan 2025 14:52:02 +0100 Subject: [PATCH 16/41] chore: updating pre-commit once more --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42147ca..0d8d655 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - id: isort args: ["--profile", "black"] - repo: https://github.com/PyCQA/docformatter - rev: master + rev: v1.7.5 hooks: - id: docformatter additional_dependencies: [tomli] From 13789e5e42c03ef38be1634dfa282932bc2586c1 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 29 Jan 2025 14:56:09 +0100 Subject: [PATCH 17/41] chore: aligning pre-commit with Qibo's --- .pre-commit-config.yaml | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d8d655..a06d469 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,16 +14,10 @@ repos: hooks: - id: black - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 6.0.0 hooks: - id: isort args: ["--profile", "black"] - - repo: https://github.com/PyCQA/docformatter - rev: v1.7.5 - hooks: - - id: docformatter - additional_dependencies: [tomli] - args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.19.1 hooks: @@ -32,17 +26,4 @@ repos: rev: v2.5.0 hooks: - id: pycln - args: - - --config=pyproject.toml - - --all - - repo: https://github.com/adamchainz/blacken-docs - rev: 1.19.1 - hooks: - - id: blacken-docs - - repo: https://github.com/pycqa/pydocstyle - rev: 6.3.0 - hooks: - - id: pydocstyle - args: - - --select=D103,D200,D206,D300,D301 - files: ^src/ + args: [--config=pyproject.toml] From 29ca5044e949f461266e4b9cfb2861cb6871591f Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 30 Jan 2025 11:35:02 +0100 Subject: [PATCH 18/41] test: add conftest and first observable test --- tests/conftest.py | 66 +++++++++++++++++++++++++++++++++++++++ tests/test_observables.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 tests/conftest.py create mode 100644 tests/test_observables.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..0a18bfa --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,66 @@ +"""conftest.py. + +Pytest fixtures. +""" + +import sys + +import pytest + +# backends to be tested +# TODO: add cutensornet and quimb here as well +BACKENDS = ["qmatchatea"] + + +def get_backend(backend_name): + + from qibotn.backends.qmatchatea import QMatchaTeaBackend + + NAME2BACKEND = { + "qmatchatea": QMatchaTeaBackend, + } + + return NAME2BACKEND[backend_name]() + + +AVAILABLE_BACKENDS = [] +for backend_name in BACKENDS: + try: + _backend = get_backend(backend_name) + AVAILABLE_BACKENDS.append(backend_name) + except (ModuleNotFoundError, ImportError): + pass + + +def pytest_runtest_setup(item): + ALL = {"darwin", "linux"} + supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers()) + plat = sys.platform + if supported_platforms and plat not in supported_platforms: # pragma: no cover + # case not covered by workflows + pytest.skip(f"Cannot run test on platform {plat}.") + + +@pytest.fixture +def backend(backend_name): + yield get_backend(backend_name) + + +def pytest_runtest_setup(item): + ALL = {"darwin", "linux"} + supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers()) + plat = sys.platform + if supported_platforms and plat not in supported_platforms: # pragma: no cover + # case not covered by workflows + pytest.skip(f"Cannot run test on platform {plat}.") + + +def pytest_configure(config): + config.addinivalue_line("markers", "linux: mark test to run only on linux") + + +def pytest_generate_tests(metafunc): + module_name = metafunc.module.__name__ + + if "backend_name" in metafunc.fixturenames: + metafunc.parametrize("backend_name", AVAILABLE_BACKENDS) diff --git a/tests/test_observables.py b/tests/test_observables.py new file mode 100644 index 0000000..d89c6f9 --- /dev/null +++ b/tests/test_observables.py @@ -0,0 +1,47 @@ +import math +import random + +import pytest +from qibo import Circuit, construct_backend, gates, hamiltonians +from qibo.symbols import X, Z + + +def build_observable(nqubits): + """Helper function to construct a target observable.""" + hamiltonian_form = 0 + for i in range(nqubits): + hamiltonian_form += 0.5 * X(i % nqubits) * Z((i + 1) % nqubits) + + hamiltonian = hamiltonians.SymbolicHamiltonian(form=hamiltonian_form) + return hamiltonian, hamiltonian_form + + +def build_circuit(nqubits, nlayers, seed=42): + """Helper function to construct a layered quantum circuit.""" + random.seed(seed) + + circ = Circuit(nqubits) + for _ in range(nlayers): + for q in range(nqubits): + circ.add(gates.RY(q=q, theta=random.uniform(-math.pi, math.pi))) + circ.add(gates.RZ(q=q, theta=random.uniform(-math.pi, math.pi))) + [circ.add(gates.CNOT(q % nqubits, (q + 1) % nqubits) for q in range(nqubits))] + circ.add(gates.M(*range(nqubits))) + return circ + + +@pytest.mark.parametrize("nqubits", [2, 5, 10]) +def test_observable_expval(backend, nqubits): + numpy_backend = construct_backend("numpy") + ham, ham_form = build_observable(nqubits) + circ = build_circuit(nqubits=nqubits, nlayers=1) + + exact_expval = numpy_backend.calculate_expectation_state( + hamiltonian=ham, + state=circ().state(), + normalize=False, + ) + + tn_expval = backend.expectation(circuit=circ, observable=ham_form) + + assert math.isclose(exact_expval, tn_expval, abs_tol=1e-7) From 4cc14524e6b8de0e151772db0628a1f57599d54a Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 30 Jan 2025 11:37:31 +0100 Subject: [PATCH 19/41] adapt pre-commit to qibo's one --- .pre-commit-config.yaml | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8372706..a06d469 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,12 +18,6 @@ repos: hooks: - id: isort args: ["--profile", "black"] - - repo: https://github.com/PyCQA/docformatter - rev: v1.7.5 - hooks: - - id: docformatter - additional_dependencies: [tomli] - args: [--in-place, --config, ./pyproject.toml] - repo: https://github.com/asottile/pyupgrade rev: v3.19.1 hooks: @@ -32,17 +26,4 @@ repos: rev: v2.5.0 hooks: - id: pycln - args: - - --config=pyproject.toml - - --all - - repo: https://github.com/adamchainz/blacken-docs - rev: 1.19.1 - hooks: - - id: blacken-docs - - repo: https://github.com/pycqa/pydocstyle - rev: 6.3.0 - hooks: - - id: pydocstyle - args: - - --select=D103,D200,D206,D300,D301 - files: ^src/ + args: [--config=pyproject.toml] From 67b900b6216425bb0025734ce8d282952a79a66c Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 12:11:39 +0100 Subject: [PATCH 20/41] fix: set config_backend as normal method and not abstract method (for now) --- src/qibotn/backends/abstract.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index f7d2df2..1ee3c5b 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -1,5 +1,3 @@ -from abc import abstractmethod - from qibo.backends.numpy import NumpyBackend from qibo.config import raise_error @@ -32,7 +30,7 @@ class QibotnBackend(NumpyBackend): if precision != self.precision: super().set_precision(precision) - @abstractmethod + # @abstractmethod def configure_tn_simulation(self, **config): """Configure the TN simulation that will be performed.""" pass From 08ad49eb195b4fd13846665ad7867c32a99f520f Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 16:54:24 +0400 Subject: [PATCH 21/41] feat: make statevector calculation optional --- src/qibotn/backends/qmatchatea.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index ac3d934..74e29a3 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -77,6 +77,7 @@ class QMatchaTeaBackend(QibotnBackend): initial_state=None, nshots=None, prob_type="U", + return_array=False, **prob_kwargs, ): """Execute a Qibo quantum circuit using tensor network simulation. @@ -153,7 +154,7 @@ class QMatchaTeaBackend(QibotnBackend): observables=observables, ) - if circuit.num_qubits < 20: + if circuit.num_qubits < 20 and return_array: statevector = results.statevector else: statevector = None From 4a2362e0c16b4582a46b615136dd681fe99f96dd Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 16:57:50 +0400 Subject: [PATCH 22/41] feat: add set_device method to TN abstract backend --- src/qibotn/backends/abstract.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index 1ee3c5b..9b77f20 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -30,6 +30,9 @@ class QibotnBackend(NumpyBackend): if precision != self.precision: super().set_precision(precision) + def set_device(self, device): + self.device = device + # @abstractmethod def configure_tn_simulation(self, **config): """Configure the TN simulation that will be performed.""" From 1036075c9cbaa8c8a9f907a1f3aed5bcfcc6f903 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 17:18:48 +0100 Subject: [PATCH 23/41] fix: probabilities results in case of prob_type=U --- src/qibotn/result.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/qibotn/result.py b/src/qibotn/result.py index 078f2e1..74c3f5a 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -23,7 +23,16 @@ class TensorNetworkResult: def probabilities(self): """Return calculated probabilities according to the given method.""" - return self.measured_probabilities[self.prob_type] + if self.prob_type == "U": + for bitstring in self.measured_probabilities[self.prob_type]: + self.measured_probabilities[self.prob_type][bitstring] = ( + self.measured_probabilities[self.prob_type][bitstring][1] + - self.measured_probabilities[self.prob_type][bitstring][0] + ) + probabilities = self.measured_probabilities[self.prob_type] + else: + probabilities = self.measured_probabilities[self.prob_type] + return probabilities def frequencies(self): """Return frequencies if a certain number of shots has been set.""" From d15a3d952dc768c848b2a48b41ae6ca3f39e1a29 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 17:19:17 +0100 Subject: [PATCH 24/41] refactor: renaming expectation test --- tests/{test_observables.py => test_expectation.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_observables.py => test_expectation.py} (100%) diff --git a/tests/test_observables.py b/tests/test_expectation.py similarity index 100% rename from tests/test_observables.py rename to tests/test_expectation.py From f420a9403681e2470cfcc87aac1a47c525ebd6f3 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 3 Feb 2025 17:19:39 +0100 Subject: [PATCH 25/41] test: circuit execution --- tests/test_circuit_execution.py | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/test_circuit_execution.py diff --git a/tests/test_circuit_execution.py b/tests/test_circuit_execution.py new file mode 100644 index 0000000..4ee396e --- /dev/null +++ b/tests/test_circuit_execution.py @@ -0,0 +1,92 @@ +import math + +import pytest +from qibo import Circuit, gates, hamiltonians +from qibo.symbols import X, Z + +from qibotn.backends.qmatchatea import QMatchaTeaBackend + + +def build_observable(nqubits): + """Helper function to construct a target observable.""" + hamiltonian_form = 0 + for i in range(nqubits): + hamiltonian_form += 0.5 * X(i % nqubits) * Z((i + 1) % nqubits) + + hamiltonian = hamiltonians.SymbolicHamiltonian(form=hamiltonian_form) + return hamiltonian, hamiltonian_form + + +def build_GHZ(nqubits): + """Helper function to construct a layered quantum circuit.""" + circ = Circuit(nqubits) + circ.add(gates.H(0)) + [circ.add(gates.CNOT(q, q + 1)) for q in range(nqubits - 1)] + return circ + + +def construct_targets(nqubits): + """Construct strings of 1s and 0s of size `nqubits`.""" + ones = "1" * nqubits + zeros = "0" * nqubits + return ones, zeros + + +@pytest.mark.parametrize("nqubits", [2, 10, 40]) +def test_probabilities(backend, nqubits): + + circ = build_GHZ(nqubits=nqubits) + ones, zeros = construct_targets(nqubits) + + if isinstance(backend, QMatchaTeaBackend): + # unbiased prob + out_u = backend.execute_circuit( + circuit=circ, + prob_type="U", + num_samples=1000, + ).probabilities() + + math.isclose(out_u[ones], 0.5, abs_tol=1e-7) + math.isclose(out_u[zeros], 0.5, abs_tol=1e-7) + + out_g = backend.execute_circuit( + circuit=circ, + prob_type="G", + prob_threshold=1.0, + ).probabilities() + + math.isclose(out_g[ones], 0.5, abs_tol=1e-7) + math.isclose(out_g[zeros], 0.5, abs_tol=1e-7) + + out_e = backend.execute_circuit( + circuit=circ, + prob_type="E", + prob_threshold=0.2, + ).probabilities() + + math.isclose(out_e[ones], 0.5, abs_tol=1e-7) + math.isclose(out_e[zeros], 0.5, abs_tol=1e-7) + + +@pytest.mark.parametrize("nqubits", [2, 10, 40]) +@pytest.mark.parametrize("nshots", [100, 1000]) +def test_shots(backend, nqubits, nshots): + circ = build_GHZ(nqubits=nqubits) + ones, zeros = construct_targets(nqubits) + + # For p = 0.5, sigma = sqrt(nshots * 0.5 * 0.5) = sqrt(nshots)/2. + sigma_threshold = 3 * (math.sqrt(nshots) / 2) + + outcome = backend.execute_circuit(circ, nshots=nshots) + frequencies = outcome.frequencies() + + shots_ones = frequencies.get(ones, 0) + shots_zeros = frequencies.get(zeros, 0) + + # Check that the counts for both outcomes are within the 3-sigma threshold of nshots/2. + assert ( + abs(shots_ones - (nshots / 2)) < sigma_threshold + ), f"Count for {ones} deviates too much: {shots_ones} vs expected {nshots/2}" + assert ( + abs(shots_zeros - (nshots / 2)) < sigma_threshold + ), f"Count for {zeros} deviates too much: {shots_zeros} vs expected {nshots/2}" From 8b7bb9ee7c98c46188486022d1e323a8247c2050 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 4 Feb 2025 11:20:08 +0100 Subject: [PATCH 26/41] doc: improving the docs --- doc/source/QiboTN.png | Bin 0 -> 148816 bytes doc/source/getting-started/index.rst | 1 + doc/source/getting-started/quickstart.rst | 72 ++++++++++++++++++++-- doc/source/index.rst | 55 ++++++++++++----- 4 files changed, 109 insertions(+), 19 deletions(-) create mode 100644 doc/source/QiboTN.png diff --git a/doc/source/QiboTN.png b/doc/source/QiboTN.png new file mode 100644 index 0000000000000000000000000000000000000000..be6d9001fdf49f391f6690d582f5701a6de3ce90 GIT binary patch literal 148816 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 backend to support large-scale simulation of quantum circuits and acceleration. +Qibotn is an high-level library which integrates tensor network simulation within +the `Qibo `_ ecosystem. -Supported Computation: +If you are familiar with Qibo, you will be well aware of the modularity we provide +through the use of our backends: after building a specific algorithm or quantum +circuit, any of our backends can be selected to perform operations on the +desired hardware (classical or quantum). -- Tensornet (TN) -- Matrix Product States (MPS) +Here, we extend this modularity to one of the most famous quantum inspired simulation +technique. -Tensor Network contractions to: +We do this by relying on well-known and maintained packages, and integrating their +operation into our own dedicated backends. -- dense vectors -- expecation values of given Pauli string +.. image:: QiboTN.png + + +As shown in the figure above, we currently support three different backends, which +correspond to the three mentioned packages: + - `cuQuantum `_: an NVIDIA SDK of optimized libraries and tools for accelerating quantum computing workflows (we refer to the specific `Cutensornet `_ library); + - `quimb `_: an easy but fast python library for ‘quantum information many-body’ calculations, focusing primarily on tensor networks; + - `Quantum Matcha Tea `_: a logical quantum computer emulator powered by matrix product states. + +.. warning:: + + There are currently two ways to use the three backends (`qmatchatea` is + slightly different from the others), but we are working to standardize the interface. + +Thanks to the mentioned packages, we currently support some tensor network ansatze: +Matrix Product States (MPS) on any mentioned backend, Tree Tensor Networks (TTN) +through the Quantum Matcha Tea backend and a more general Tensor Network (TN) ansatz through +Cutensornet and Quimb. + +Supported simulation features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We support Tensor Network contractions to: + +- dense vectors (all the backends) +- expecation values of given Pauli string (Cutensornet and Qmatchatea) The supported HPC configurations are: -- single-node CPU -- single-node GPU or GPUs -- multi-node multi-GPU with Message Passing Interface (MPI) -- multi-node multi-GPU with NVIDIA Collective Communications Library (NCCL) +- single-node CPU through Quimb and Qmatchatea +- single-node GPU or GPUs through Cutensornet and Qmatchatea +- multi-node multi-GPU with Message Passing Interface (MPI) through Cutensornet +- multi-node multi-GPU with NVIDIA Collective Communications Library (NCCL) through Cutensornet -Currently, the supported tensor network libraries are: - -- `cuQuantum `_, an NVIDIA SDK of optimized libraries and tools for accelerating quantum computing workflows. -- `quimb `_, an easy but fast python library for ‘quantum information many-body’ calculations, focusing primarily on tensor networks. How to Use the Documentation ============================ From 65b60e0fb979ad1a4ab8318251b3c626d1e83dd8 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 4 Feb 2025 11:20:46 +0100 Subject: [PATCH 27/41] fix: probabilities method in result class --- src/qibotn/backends/qmatchatea.py | 6 +++++- src/qibotn/result.py | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 74e29a3..9353a34 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -76,7 +76,7 @@ class QMatchaTeaBackend(QibotnBackend): circuit, initial_state=None, nshots=None, - prob_type="U", + prob_type=None, return_array=False, **prob_kwargs, ): @@ -123,6 +123,10 @@ class QMatchaTeaBackend(QibotnBackend): f"Backend {self.name}-{self.platform} currently does not support initial state.", ) + if prob_type == None: + prob_type = "U" + prob_kwargs = {"num_samples": 500} + # To be sure the setup is correct and no modifications have been done self._setup_qmatchatea_backend() diff --git a/src/qibotn/result.py b/src/qibotn/result.py index 74c3f5a..b6d0b52 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -1,3 +1,4 @@ +from copy import deepcopy from dataclasses import dataclass from typing import Union @@ -24,12 +25,13 @@ class TensorNetworkResult: def probabilities(self): """Return calculated probabilities according to the given method.""" if self.prob_type == "U": + measured_probabilities = deepcopy(self.measured_probabilities) for bitstring in self.measured_probabilities[self.prob_type]: - self.measured_probabilities[self.prob_type][bitstring] = ( + measured_probabilities[self.prob_type][bitstring] = ( self.measured_probabilities[self.prob_type][bitstring][1] - self.measured_probabilities[self.prob_type][bitstring][0] ) - probabilities = self.measured_probabilities[self.prob_type] + probabilities = measured_probabilities[self.prob_type] else: probabilities = self.measured_probabilities[self.prob_type] return probabilities From 39ed3ebbabb2b626cae0545cbe9ae1858bfbcfa8 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 10:07:26 +0100 Subject: [PATCH 28/41] doc: use qibo's GHZ state function --- doc/source/getting-started/quickstart.rst | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/doc/source/getting-started/quickstart.rst b/doc/source/getting-started/quickstart.rst index 2d07834..8dc766d 100644 --- a/doc/source/getting-started/quickstart.rst +++ b/doc/source/getting-started/quickstart.rst @@ -95,24 +95,17 @@ used to execute a quantum circuit:: # We need Qibo to setup the circuit and the backend from qibo import Circuit, gates + from qibo.models.encodings import ghz_state from qibo.backends import construct_backend # We need Quantum Matcha Tea to customize the tensor network simulation from qmatchatea import QCConvergenceParameters - # Constructing the circuit preparing a GHZ state - def build_GHZ(nqubits): - """Helper function to construct a circuit preparing the GHZ circuit.""" - circ = Circuit(nqubits) - circ.add(gates.H(0)) - [circ.add(gates.CNOT(q, q+1)) for q in range(nqubits-1)] - return circ - # Set the number of qubits nqubits = 40 - # Construct the circuit preparing the GHZ state - circuit = build_GHZ(nqubits) + # Construct a circuit preparing a Quantum Fourier Transform + circuit = ghz_state(nqubits) # Construct the backend backend = construct_backend(backend="qibotn", platform="qmatchatea") From 72ba4165be437e622b983a170ec05871c8c8b80a Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Mon, 10 Feb 2025 10:11:59 +0100 Subject: [PATCH 29/41] Apply suggestions from code review Co-authored-by: BrunoLiegiBastonLiegi <45011234+BrunoLiegiBastonLiegi@users.noreply.github.com> --- src/qibotn/backends/__init__.py | 2 +- src/qibotn/backends/qmatchatea.py | 2 +- src/qibotn/result.py | 15 +++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index 624ed62..b1b13a4 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -35,7 +35,7 @@ class MetaBackend: else: raise_error( NotImplementedError, - f"Unsupported platform {platform}, please pick one in (`cutensornet`, `qutensornet`, `qmatchatea`)", + f"Unsupported platform {platform}, please pick one in {PLATFORMS}", ) def list_available(self) -> dict: diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 9353a34..ddeb0b8 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -19,7 +19,7 @@ class QMatchaTeaBackend(QibotnBackend): def __init__(self): super().__init__() - self.name = "qiboml" + self.name = "qibotn" self.platform = "qmatchatea" # Set default configurations diff --git a/src/qibotn/result.py b/src/qibotn/result.py index b6d0b52..90a164c 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -26,10 +26,10 @@ class TensorNetworkResult: """Return calculated probabilities according to the given method.""" if self.prob_type == "U": measured_probabilities = deepcopy(self.measured_probabilities) - for bitstring in self.measured_probabilities[self.prob_type]: + for bitstring, prob in self.measured_probabilities[self.prob_type].items(): measured_probabilities[self.prob_type][bitstring] = ( - self.measured_probabilities[self.prob_type][bitstring][1] - - self.measured_probabilities[self.prob_type][bitstring][0] + prob[1] + - prob[0] ) probabilities = measured_probabilities[self.prob_type] else: @@ -50,8 +50,7 @@ class TensorNetworkResult: """Return the statevector if the number of qubits is less than 30.""" if self.nqubits < 20: return self.statevector - else: - raise_error( - NotImplementedError, - f"Tensor network simulation cannot be used to reconstruct statevector for >= 30 .", - ) + raise_error( + NotImplementedError, + f"Tensor network simulation cannot be used to reconstruct statevector for >= 30 .", + ) From 938bf136f67beff9c38e014a35769158a727e2a2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 09:12:06 +0000 Subject: [PATCH 30/41] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qibotn/result.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/qibotn/result.py b/src/qibotn/result.py index 90a164c..dd4e326 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -27,10 +27,7 @@ class TensorNetworkResult: if self.prob_type == "U": measured_probabilities = deepcopy(self.measured_probabilities) for bitstring, prob in self.measured_probabilities[self.prob_type].items(): - measured_probabilities[self.prob_type][bitstring] = ( - prob[1] - - prob[0] - ) + measured_probabilities[self.prob_type][bitstring] = prob[1] - prob[0] probabilities = measured_probabilities[self.prob_type] else: probabilities = self.measured_probabilities[self.prob_type] From 3e9189e49ff015ef5113c13ae88ed407b2fd5dd6 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 10:23:01 +0100 Subject: [PATCH 31/41] refactor: probabilities are now returned as an array, being aligned with Qibo --- src/qibotn/result.py | 6 +++--- tests/test_circuit_execution.py | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/qibotn/result.py b/src/qibotn/result.py index dd4e326..0d440d3 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -31,7 +31,7 @@ class TensorNetworkResult: probabilities = measured_probabilities[self.prob_type] else: probabilities = self.measured_probabilities[self.prob_type] - return probabilities + return self.backend.cast(list(probabilities.values()), dtype="double") def frequencies(self): """Return frequencies if a certain number of shots has been set.""" @@ -44,10 +44,10 @@ class TensorNetworkResult: return self.measures def state(self): - """Return the statevector if the number of qubits is less than 30.""" + """Return the statevector if the number of qubits is less than 20.""" if self.nqubits < 20: return self.statevector raise_error( NotImplementedError, - f"Tensor network simulation cannot be used to reconstruct statevector for >= 30 .", + f"Tensor network simulation cannot be used to reconstruct statevector for >= 20 .", ) diff --git a/tests/test_circuit_execution.py b/tests/test_circuit_execution.py index 4ee396e..c5dfc86 100644 --- a/tests/test_circuit_execution.py +++ b/tests/test_circuit_execution.py @@ -36,7 +36,6 @@ def construct_targets(nqubits): def test_probabilities(backend, nqubits): circ = build_GHZ(nqubits=nqubits) - ones, zeros = construct_targets(nqubits) if isinstance(backend, QMatchaTeaBackend): # unbiased prob @@ -46,8 +45,8 @@ def test_probabilities(backend, nqubits): num_samples=1000, ).probabilities() - math.isclose(out_u[ones], 0.5, abs_tol=1e-7) - math.isclose(out_u[zeros], 0.5, abs_tol=1e-7) + math.isclose(out_u[0], 0.5, abs_tol=1e-7) + math.isclose(out_u[1], 0.5, abs_tol=1e-7) out_g = backend.execute_circuit( circuit=circ, @@ -55,8 +54,8 @@ def test_probabilities(backend, nqubits): prob_threshold=1.0, ).probabilities() - math.isclose(out_g[ones], 0.5, abs_tol=1e-7) - math.isclose(out_g[zeros], 0.5, abs_tol=1e-7) + math.isclose(out_g[0], 0.5, abs_tol=1e-7) + math.isclose(out_g[1], 0.5, abs_tol=1e-7) out_e = backend.execute_circuit( circuit=circ, @@ -64,8 +63,8 @@ def test_probabilities(backend, nqubits): prob_threshold=0.2, ).probabilities() - math.isclose(out_e[ones], 0.5, abs_tol=1e-7) - math.isclose(out_e[zeros], 0.5, abs_tol=1e-7) + math.isclose(out_e[0], 0.5, abs_tol=1e-7) + math.isclose(out_e[1], 0.5, abs_tol=1e-7) @pytest.mark.parametrize("nqubits", [2, 10, 40]) From d854d150681fcb63dc7223e16abadc0b0da688ee Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 10:43:29 +0100 Subject: [PATCH 32/41] fix: add link to api reference --- doc/source/getting-started/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting-started/quickstart.rst b/doc/source/getting-started/quickstart.rst index 8dc766d..7500e6a 100644 --- a/doc/source/getting-started/quickstart.rst +++ b/doc/source/getting-started/quickstart.rst @@ -135,4 +135,4 @@ used to execute a quantum circuit:: By default, the simulator is choosing a specific method to compute the probabilities, and for further information we recommend the user to refer to our High-Level-API -docstrings. +docstrings: :doc:`/api-reference/qibotn.backends`. From a5f7d1fb144d59fb386d3516b20588d09f66569f Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 10:49:30 +0100 Subject: [PATCH 33/41] refactor: make QibotnBackend abstract and inherit both this and NumpyBackend in Qibotn concrete backends --- src/qibotn/backends/abstract.py | 18 ++++-------------- src/qibotn/backends/cutensornet.py | 3 ++- src/qibotn/backends/qmatchatea.py | 3 ++- src/qibotn/backends/quimb.py | 3 ++- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index 9b77f20..8747313 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -1,20 +1,11 @@ -from qibo.backends.numpy import NumpyBackend +from abc import ABC + from qibo.config import raise_error -DEFAULT_CONFIGURATION = { - "MPI_enabled": False, # TODO: cutensornet specific, TBRemoved - "NCCL_enabled": False, # TODO: cutensornet specific, TBRemoved - "expectation_enabled": False, - "pauli_string_pattern": None, - "MPS_enabled": False, - "gate_algo": None, - "mps_opts": None, -} +class QibotnBackend(ABC): -class QibotnBackend(NumpyBackend): - - def __init__(self, runcard: dict = DEFAULT_CONFIGURATION): + def __init__(self): super().__init__() def apply_gate(self, gate, state, nqubits): # pragma: no cover @@ -33,7 +24,6 @@ class QibotnBackend(NumpyBackend): def set_device(self, device): self.device = device - # @abstractmethod def configure_tn_simulation(self, **config): """Configure the TN simulation that will be performed.""" pass diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index d02fdd5..83c6b46 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -1,4 +1,5 @@ import numpy as np +from qibo.backends import NumpyBackend from qibo.config import raise_error from qibo.result import QuantumState @@ -7,7 +8,7 @@ from qibotn.backends.abstract import QibotnBackend CUDA_TYPES = {} -class CuTensorNet(QibotnBackend): # pragma: no cover +class CuTensorNet(NumpyBackend, QibotnBackend): # pragma: no cover # CI does not test for GPU """Creates CuQuantum backend for QiboTN.""" diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index ddeb0b8..00a5124 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -7,6 +7,7 @@ import numpy as np import qiskit import qmatchatea import qtealeaves +from qibo.backends import NumpyBackend from qibo.config import raise_error from qibotn.backends.abstract import QibotnBackend @@ -14,7 +15,7 @@ from qibotn.result import TensorNetworkResult @dataclass -class QMatchaTeaBackend(QibotnBackend): +class QMatchaTeaBackend(NumpyBackend, QibotnBackend): def __init__(self): super().__init__() diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index 30ef888..951bec1 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -1,10 +1,11 @@ +from qibo.backends import NumpyBackend from qibo.config import raise_error from qibo.result import QuantumState from qibotn.backends.abstract import QibotnBackend -class QuimbBackend(QibotnBackend): +class QuimbBackend(NumpyBackend, QibotnBackend): def __init__(self, runcard): super().__init__() From 30c3bba23add31914db2d180872b7496d9074292 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 11:18:09 +0100 Subject: [PATCH 34/41] fix: change the order or the inheritance to impose the correct __mro__ --- src/qibotn/backends/abstract.py | 6 ++++++ src/qibotn/backends/cutensornet.py | 2 +- src/qibotn/backends/qmatchatea.py | 8 +++----- src/qibotn/backends/quimb.py | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/qibotn/backends/abstract.py b/src/qibotn/backends/abstract.py index 8747313..24e87f6 100644 --- a/src/qibotn/backends/abstract.py +++ b/src/qibotn/backends/abstract.py @@ -20,10 +20,16 @@ class QibotnBackend(ABC): def set_precision(self, precision): if precision != self.precision: super().set_precision(precision) + self._setup_backend_specifics() def set_device(self, device): self.device = device + self._setup_backend_specifics() def configure_tn_simulation(self, **config): """Configure the TN simulation that will be performed.""" pass + + def _setup_backend_specifics(self): + """Configure the backend specific according to the used package.""" + pass diff --git a/src/qibotn/backends/cutensornet.py b/src/qibotn/backends/cutensornet.py index 83c6b46..553fc51 100644 --- a/src/qibotn/backends/cutensornet.py +++ b/src/qibotn/backends/cutensornet.py @@ -8,7 +8,7 @@ from qibotn.backends.abstract import QibotnBackend CUDA_TYPES = {} -class CuTensorNet(NumpyBackend, QibotnBackend): # pragma: no cover +class CuTensorNet(QibotnBackend, NumpyBackend): # pragma: no cover # CI does not test for GPU """Creates CuQuantum backend for QiboTN.""" diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 00a5124..42559fb 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -15,7 +15,7 @@ from qibotn.result import TensorNetworkResult @dataclass -class QMatchaTeaBackend(NumpyBackend, QibotnBackend): +class QMatchaTeaBackend(QibotnBackend, NumpyBackend): def __init__(self): super().__init__() @@ -25,9 +25,7 @@ class QMatchaTeaBackend(NumpyBackend, QibotnBackend): # Set default configurations self.configure_tn_simulation() - # TODO: update this function whenever ``set_device`` and ``set_precision`` - # are set (?) - self._setup_qmatchatea_backend() + self._setup_backend_specifics() def configure_tn_simulation( self, @@ -52,7 +50,7 @@ class QMatchaTeaBackend(NumpyBackend, QibotnBackend): ) self.ansatz = ansatz - def _setup_qmatchatea_backend(self): + def _setup_backend_specifics(self): """Configure qmatchatea QCBackend object.""" qmatchatea_device = ( diff --git a/src/qibotn/backends/quimb.py b/src/qibotn/backends/quimb.py index 951bec1..13a8cf1 100644 --- a/src/qibotn/backends/quimb.py +++ b/src/qibotn/backends/quimb.py @@ -5,7 +5,7 @@ from qibo.result import QuantumState from qibotn.backends.abstract import QibotnBackend -class QuimbBackend(NumpyBackend, QibotnBackend): +class QuimbBackend(QibotnBackend, NumpyBackend): def __init__(self, runcard): super().__init__() From 97d2c793009b17b4b840669e47c8585feb37534c Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 12:17:37 +0100 Subject: [PATCH 35/41] doc: improve docstrings related to TensorNetworkResult --- src/qibotn/backends/qmatchatea.py | 7 +++---- src/qibotn/result.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 42559fb..f6b8ad4 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -126,9 +126,6 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): prob_type = "U" prob_kwargs = {"num_samples": 500} - # To be sure the setup is correct and no modifications have been done - self._setup_qmatchatea_backend() - # TODO: check circuit = self._qibocirc_to_qiskitcirc(circuit) run_qk_params = qmatchatea.preprocessing.qk_transpilation_params(False) @@ -189,7 +186,9 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): (e.g., `X(0)*Y(1)` or `Z(0)*Z(1) + 1.5*Y(2)`). Returns: - qmatchatea.SimulationResult [TEMPORARY] + qibotn.TensorNetworkResult class, providing methods to retrieve + probabilities, frequencies and state always according to the chosen + simulation setup. """ # From Qibo to Qiskit diff --git a/src/qibotn/result.py b/src/qibotn/result.py index 0d440d3..412dbc0 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -10,6 +10,20 @@ from qibotn.backends.abstract import QibotnBackend @dataclass class TensorNetworkResult: + """ + Object to store and process the output of a Tensor Network simulation of a quantum circuit. + + Args: + nqubits (int): number of qubits involved in the simulation; + backend (QibotnBackend): specific backend on which the simulation has been performed; + measures (dict): measures (if performed) during the tensor network simulation; + measured_probabilities (Union[dict, ndarray]): probabilities of the final state + according to the simulation; + prob_type (str): string identifying the method used to compute the probabilities. + Especially useful in case the `QmatchateaBackend` is selected. + statevector (ndarray): if computed, the reconstructed statevector. + """ + nqubits: int backend: QibotnBackend measures: dict From 309fdc996b7046e97414bdd1d86c63893f503df5 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 12:48:21 +0100 Subject: [PATCH 36/41] fix: importing QmatchateaBackend inside proper condition in the __init__ file --- src/qibotn/backends/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/qibotn/backends/__init__.py b/src/qibotn/backends/__init__.py index b1b13a4..8fdd367 100644 --- a/src/qibotn/backends/__init__.py +++ b/src/qibotn/backends/__init__.py @@ -4,11 +4,8 @@ from qibo.config import raise_error from qibotn.backends.abstract import QibotnBackend from qibotn.backends.cutensornet import CuTensorNet # pylint: disable=E0401 -from qibotn.backends.qmatchatea import QMatchaTeaBackend # pylint: disable=E0401 from qibotn.backends.quimb import QuimbBackend # pylint: disable=E0401 -QibotnBackend = Union[CuTensorNet, QuimbBackend, QMatchaTeaBackend] - PLATFORMS = ("cutensornet", "qutensornet", "qmatchatea") @@ -31,6 +28,8 @@ class MetaBackend: elif platform == "qutensornet": # pragma: no cover return QuimbBackend(runcard) elif platform == "qmatchatea": # pragma: no cover + from qibotn.backends.qmatchatea import QMatchaTeaBackend + return QMatchaTeaBackend() else: raise_error( From 4683c607ed00391e025a6995981561714905b0a5 Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Mon, 10 Feb 2025 12:50:04 +0100 Subject: [PATCH 37/41] Update src/qibotn/result.py Co-authored-by: BrunoLiegiBastonLiegi <45011234+BrunoLiegiBastonLiegi@users.noreply.github.com> --- src/qibotn/result.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibotn/result.py b/src/qibotn/result.py index 412dbc0..a37cc63 100644 --- a/src/qibotn/result.py +++ b/src/qibotn/result.py @@ -54,8 +54,7 @@ class TensorNetworkResult: ValueError, f"To access frequencies, circuit has to be executed with a given number of shots != None", ) - else: - return self.measures + return self.measures def state(self): """Return the statevector if the number of qubits is less than 20.""" From afc6c5d19654598093f5409b26dd515729791676 Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Mon, 10 Feb 2025 12:53:28 +0100 Subject: [PATCH 38/41] Update src/qibotn/backends/qmatchatea.py Co-authored-by: BrunoLiegiBastonLiegi <45011234+BrunoLiegiBastonLiegi@users.noreply.github.com> --- src/qibotn/backends/qmatchatea.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index f6b8ad4..fc169b5 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -119,7 +119,7 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): if initial_state is not None: raise_error( NotImplementedError, - f"Backend {self.name}-{self.platform} currently does not support initial state.", + f"Backend {self} currently does not support initial state.", ) if prob_type == None: From bb76e2b64d380464d7f16025f96be32985a07592 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 13:17:19 +0100 Subject: [PATCH 39/41] refactor: rewrite the expectation method using Qibo built in features --- src/qibotn/backends/qmatchatea.py | 118 ++++++++++++------------------ 1 file changed, 47 insertions(+), 71 deletions(-) diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index fc169b5..348c40a 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -198,7 +198,7 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): operators = qmatchatea.QCOperators() observables = qtealeaves.observables.TNObservables() # Add custom observable - observables += self._qiboobs_to_qmatchaobs(hamiltonian_form=observable) + observables += self._qiboobs_to_qmatchaobs(hamiltonian=observable) results = qmatchatea.run_simulation( circ=circuit, @@ -225,92 +225,68 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): ) return qiskit_circuit - def _qiboobs_to_qmatchaobs( - self, hamiltonian_form, observable_name="custom_hamiltonian" - ): - """Convert a Qibo-style symbolic expression (e.g. '2.0*Y2*Z0 + Z0*Z2') - into a qmatchatea ``TNObsWeightedSum`` observable. + def _qiboobs_to_qmatchaobs(self, hamiltonian, observable_name="custom_hamiltonian"): + """ + Convert a Qibo SymbolicHamiltonian into a qmatchatea TNObsWeightedSum observable. - The parsing logic here assumes: - - Each term may have an optional leading coefficient (defaults to 1.0). - - Each operator is a single-letter from [XYZI] plus a qubit index (e.g., 'X2' means X on qubit 2). - - Terms are separated by '+' (and optionally '-') signs. If negative, we parse it as a negative coefficient. + The SymbolicHamiltonian is expected to have a collection of terms, where each term has: + - `coefficient`: A numeric value. + - `factors`: A list of factors, each a string such as "X2" or "Z0", representing an operator + and the qubit it acts on. Args: - hamiltonian_form: e.g. 'Y2*Z0 + 2.5*Z0*Z2' - observable_name (str): A name for the resulting ``TNObsWeightedSum``. + hamiltonian (qibo.SymbolicHamiltonian): The symbolic Hamiltonian containing the terms. + observable_name (str): The name for the resulting TNObsWeightedSum observable. Returns: - TNObsWeightedSum: An observable suitable for qmatchatea. + TNObsWeightedSum: An observable suitable for use with qmatchatea. """ - hamiltonian_form = str(hamiltonian_form) - - # Collect all the simple terms in the string and preserve the sign - # whenever a coefficient is negative - hamiltonian_form = hamiltonian_form.replace("-", "+-") - raw_terms = [t.strip() for t in hamiltonian_form.split("+") if t.strip()] - coeff_list = [] + tensor_product_obs = None - # Regex for leading coefficient: e.g. "2.5*" or "-0.3*" - # group(1) will capture the numeric part, group(0) includes the sign if present - leading_coeff_pattern = re.compile(r"^([+-]?\d+(\.\d+)?)\*") + # Regex to split an operator factor (e.g., "X2" -> operator "X", qubit 2) + factor_pattern = re.compile(r"([^\d]+)(\d+)") - for i, hamiltonian_term in enumerate(raw_terms): - # Set default coefficient to 1.0 - coeff = 1.0 - # Look for a leading numeric coefficient - match = leading_coeff_pattern.search(hamiltonian_term) + # Iterate over each term in the symbolic Hamiltonian + for i, term in enumerate(hamiltonian.terms): + # Store the term's coefficient + coeff_list.append(term.coefficient) - if match: - # Parse that coefficient - coeff = float(match.group(1)) + operator_names = [] + acting_on_qubits = [] - # Remove that portion from the term string so only operators remain - hamiltonian_term = leading_coeff_pattern.sub( - "", hamiltonian_term, count=1 - ) - - # Now isolate the single terms in the product (if there are more than 1) - operators_qubits = hamiltonian_term.split("*") - - # Prepare lists for qmatchatea - operator_names, acting_on_qubits = [], [] - - # Each sub-term is e.g. "Y2", so operator = "Y", qubit = 2 - # We assume the operator is the single letter, the rest is the qubit index - for operator in operators_qubits: - operator = operator.strip() - - # Use a regex to split the operator and the qubit index - match = re.match(r"([^\d]+)(\d+)", operator) + # Process each factor in the term + for factor in term.factors: + # Assume each factor is a string like "Y2" or "Z0" + match = factor_pattern.match(str(factor)) if match: - operator_name = match.group( - 1 - ) # All characters before the number (e.g., 'XYZ') - qubit_index = int(match.group(2)) # The number part (e.g., 2) + operator_name = match.group(1) + qubit_index = int(match.group(2)) + operator_names.append(operator_name) + acting_on_qubits.append([qubit_index]) + else: + raise ValueError( + f"Factor '{str(factor)}' does not match the expected format." + ) - operator_names.append(operator_name) - acting_on_qubits.append([qubit_index]) + # Create a TNObsTensorProduct for this term. + term_tensor_prod = qtealeaves.observables.TNObsTensorProduct( + name=f"term_{i}", + operators=operator_names, + sites=acting_on_qubits, + ) - # Build collection of tensor product operators (tpo) - if i == 0: - tpo = qtealeaves.observables.TNObsTensorProduct( - name=f"{hamiltonian_term}", - operators=operator_names, - sites=acting_on_qubits, - ) + # Combine tensor products from each term + if tensor_product_obs is None: + tensor_product_obs = term_tensor_prod else: - tpo += qtealeaves.observables.TNObsTensorProduct( - name=f"{hamiltonian_term}", - operators=operator_names, - sites=acting_on_qubits, - ) - # And also keep track of coefficients - coeff_list.append(coeff) + tensor_product_obs += term_tensor_prod - # Combine everything into a WeightedSum + # Combine all terms into a weighted sum observable obs_sum = qtealeaves.observables.TNObsWeightedSum( - name=observable_name, tp_operators=tpo, coeffs=coeff_list, use_itpo=False + name=observable_name, + tp_operators=tensor_product_obs, + coeffs=coeff_list, + use_itpo=False, ) return obs_sum From 5b6387787631a5ff76b5e52c3fab4d7ab7e9ea0f Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 13:20:35 +0100 Subject: [PATCH 40/41] refactor: adapt the example to the new expectation function --- .../qmatchatea_introduction.ipynb | 314 +++++++++--------- 1 file changed, 153 insertions(+), 161 deletions(-) diff --git a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb index 198e982..3702fad 100644 --- a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb +++ b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 14, "id": "6722d94e-e311-48f9-b6df-c6d829bf67fb", "metadata": {}, "outputs": [], @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 15, "id": "64162116-1555-4a68-811c-01593739d622", "metadata": {}, "outputs": [], @@ -63,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 16, "id": "4a22a172-f50d-411d-afa3-fa61937c7b3a", "metadata": {}, "outputs": [], @@ -82,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 18, "id": "76f23c57-6d08-496b-9a27-52fb63bbfcb1", "metadata": {}, "outputs": [ @@ -104,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 19, "id": "07b2c097-cea2-42ec-8f1d-b4bbb5b71d98", "metadata": {}, "outputs": [], @@ -127,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 20, "id": "34452bfd-2287-4b38-8099-e072239eab74", "metadata": {}, "outputs": [], @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 21, "id": "221ef886-5578-4200-a019-dcafa51aada3", "metadata": {}, "outputs": [ @@ -169,7 +169,8 @@ "\u001b[0;34m\u001b[0m \u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0minitial_state\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mnshots\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0mprob_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'U'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mprob_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mreturn_array\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mprob_kwargs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mDocstring:\u001b[0m\n", @@ -180,18 +181,18 @@ " - Frequencies (if the number of shots is specified).\n", " - Probabilities computed using various methods.\n", "\n", - "The following probability computation methods are available, as implemented \n", + "The following probability computation methods are available, as implemented\n", "in Quantum Matcha Tea:\n", - " - **\"E\" (Even):** Probabilities are computed by evenly descending the probability tree, \n", + " - **\"E\" (Even):** Probabilities are computed by evenly descending the probability tree,\n", " pruning branches (states) with probabilities below a threshold.\n", - " - **\"G\" (Greedy):** Probabilities are computed by following the most probable states \n", + " - **\"G\" (Greedy):** Probabilities are computed by following the most probable states\n", " in descending order until reaching a given coverage (sum of probabilities).\n", - " - **\"U\" (Unbiased):** An optimal probability measure that is unbiased and designed \n", + " - **\"U\" (Unbiased):** An optimal probability measure that is unbiased and designed\n", " for best performance. See https://arxiv.org/abs/2401.10330 for details.\n", "\n", "Args:\n", " circuit: A Qibo circuit to execute.\n", - " initial_state: The initial state of the system (default is the vacuum state \n", + " initial_state: The initial state of the system (default is the vacuum state\n", " for tensor network simulations).\n", " nshots: The number of shots for shot-noise simulation (optional).\n", " prob_type: The probability computation method. Must be one of:\n", @@ -203,7 +204,7 @@ " - For \"E\" and \"G\", requires ``prob_threshold``.\n", "\n", "Returns:\n", - " TensorNetworkResult: An object with methods to reconstruct the state, \n", + " TensorNetworkResult: An object with methods to reconstruct the state,\n", " compute probabilities, and generate frequencies.\n", "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", "\u001b[0;31mType:\u001b[0m method\n" @@ -219,7 +220,31 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 22, + "id": "f271d3f3-c7f2-49b3-94e2-d6e843a169d0", + "metadata": {}, + "outputs": [], + "source": [ + "circuit = build_circuit(nqubits=4, nlayers=2)\n", + "\n", + "# Setting random parameters\n", + "circuit.set_parameters(\n", + " parameters=np.random.uniform(-np.pi, np.pi, len(circuit.get_parameters())),\n", + ")\n", + "\n", + "# We first define the useful objects \n", + "convergence_parameters = QCConvergenceParameters(cut_ratio=1e-12, max_bond_dimension=1100)\n", + "\n", + "# And then call the dedicate configuration function\n", + "qmatcha_backend.configure_tn_simulation(\n", + " convergence_params=convergence_parameters,\n", + " ansatz=\"MPS\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, "id": "35a244c3-adba-4b8b-b28c-0ab592b0f7cf", "metadata": {}, "outputs": [ @@ -229,43 +254,43 @@ "{'nqubits': 4,\n", " 'backend': QMatchaTeaBackend(),\n", " 'measures': None,\n", - " 'measured_probabilities': {'U': {'0000': (0.0, 0.08390937969317301),\n", - " '0001': (0.08390937969317301, 0.08858639088838134),\n", - " '0010': (0.08858639088838131, 0.1832549957082757),\n", - " '0011': (0.1832549957082757, 0.25896776804349736),\n", - " '0100': (0.2589677680434974, 0.33039716334036867),\n", - " '0101': (0.33039716334036867, 0.386620221067355),\n", - " '0110': (0.3866202210673549, 0.4380808691410473),\n", - " '0111': (0.4380808691410473, 0.47837271988834),\n", - " '1000': (0.47837271988834, 0.5916815553716759),\n", - " '1001': (0.5916815553716759, 0.5972581739037379),\n", - " '1010': (0.5972581739037378, 0.6359857590550054),\n", - " '1011': (0.6359857590550054, 0.6894851559808782),\n", - " '1100': (0.6894851559808783, 0.7030911408535467),\n", - " '1101': (0.7030911408535467, 0.8264027395524797),\n", - " '1110': (0.8264027395524797, 0.8981519382820797),\n", - " '1111': (0.8981519382820797, 0.9999999999999998)},\n", + " 'measured_probabilities': {'U': {'0000': (0.0, 0.06042361322153613),\n", + " '0001': (0.06042361322153613, 0.16103484648184754),\n", + " '0010': (0.1610348464818476, 0.3885436985884956),\n", + " '0011': (0.3885436985884956, 0.4596882048691001),\n", + " '0100': (0.4596882048691, 0.46449216980829905),\n", + " '0101': (0.46449216980829905, 0.47672801247815266),\n", + " '0110': (0.47672801247815266, 0.5016979158974251),\n", + " '0111': (0.5016979158974251, 0.5398303779135739),\n", + " '1000': (0.539830377913574, 0.6539396910265846),\n", + " '1001': (0.6539396910265846, 0.7584098289087688),\n", + " '1010': (0.7584098289087688, 0.7781986778747929),\n", + " '1011': (0.7781986778747929, 0.8661307878495997),\n", + " '1100': (0.8661307878495996, 0.8908084103632258),\n", + " '1101': (0.8908084103632258, 0.9107085647613623),\n", + " '1110': (0.9107085647613623, 0.9481703292728131),\n", + " '1111': (0.9481703292728131, 0.9999999999999999)},\n", " 'E': [None],\n", " 'G': [None]},\n", " 'prob_type': 'U',\n", - " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", - " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", - " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", - " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", - " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", - " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", - " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", - " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" + " 'statevector': array([-0.13931487-0.20252155j, -0.21974897+0.25655351j,\n", + " 0.02884164-0.06302479j, -0.15237615+0.03819857j,\n", + " -0.3172761 -0.35615268j, -0.10121749+0.09769272j,\n", + " 0.14826026-0.0546699j , -0.1814861 -0.06726486j,\n", + " -0.10907416+0.29784906j, 0.30276076+0.11316387j,\n", + " 0.07771728+0.07871383j, -0.12379363+0.0676409j ,\n", + " -0.21860099+0.15283362j, 0.06862868+0.28848261j,\n", + " -0.04640848-0.18968056j, -0.22262942-0.04760056j])}" ] }, - "execution_count": 8, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Simple execution (defaults)\n", - "outcome = qmatcha_backend.execute_circuit(circuit=circuit)\n", + "outcome = qmatcha_backend.execute_circuit(circuit=circuit, return_array=True)\n", "\n", "# Print outcome\n", "vars(outcome)" @@ -273,7 +298,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 26, "id": "60501c3d-2a44-421f-b434-4a508714b132", "metadata": {}, "outputs": [ @@ -284,30 +309,25 @@ " 'backend': QMatchaTeaBackend(),\n", " 'measures': None,\n", " 'measured_probabilities': {'U': [None],\n", - " 'E': {'0000': 0.08390937969317301,\n", - " '0010': 0.09466860481989439,\n", - " '0011': 0.07571277233522165,\n", - " '0100': 0.07142939529687124,\n", - " '0101': 0.05622305772698632,\n", - " '0110': 0.05146064807369245,\n", - " '1000': 0.11330883548333581,\n", - " '1011': 0.053499396925872765,\n", - " '1101': 0.12331159869893296,\n", - " '1110': 0.07174919872960005,\n", - " '1111': 0.10184806171792007},\n", - " 'G': [None]},\n", - " 'prob_type': 'E',\n", - " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", - " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", - " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", - " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", - " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", - " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", - " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", - " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" + " 'E': [None],\n", + " 'G': {'0010': 0.227508852106648,\n", + " '0011': 0.07114450628060452,\n", + " '1000': 0.227508852106648,\n", + " '1001': 0.07114450628060452,\n", + " '0000': 0.06042361322153613,\n", + " '0001': 0.1006112332603114}},\n", + " 'prob_type': 'G',\n", + " 'statevector': array([-0.13931487-0.20252155j, -0.21974897+0.25655351j,\n", + " 0.02884164-0.06302479j, -0.15237615+0.03819857j,\n", + " -0.3172761 -0.35615268j, -0.10121749+0.09769272j,\n", + " 0.14826026-0.0546699j , -0.1814861 -0.06726486j,\n", + " -0.10907416+0.29784906j, 0.30276076+0.11316387j,\n", + " 0.07771728+0.07871383j, -0.12379363+0.0676409j ,\n", + " -0.21860099+0.15283362j, 0.06862868+0.28848261j,\n", + " -0.04640848-0.18968056j, -0.22262942-0.04760056j])}" ] }, - "execution_count": 9, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -317,8 +337,9 @@ "# We use here \"E\", which is cutting some of the components if under a threshold\n", "outcome = qmatcha_backend.execute_circuit(\n", " circuit=circuit,\n", - " prob_type=\"E\",\n", - " prob_threshold=0.05,\n", + " prob_type=\"G\",\n", + " prob_threshold=0.9,\n", + " return_array=True,\n", ")\n", "\n", "# Print outcome\n", @@ -337,7 +358,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 27, "id": "c0443efc-21ef-4ed5-9cf4-785d204a1881", "metadata": {}, "outputs": [ @@ -346,15 +367,15 @@ "output_type": "stream", "text": [ "Probabilities:\n", - " {'0000': 0.08390937969317301, '0010': 0.09466860481989439, '0011': 0.07571277233522165, '0100': 0.07142939529687124, '0101': 0.05622305772698632, '0110': 0.05146064807369245, '1000': 0.11330883548333581, '1011': 0.053499396925872765, '1101': 0.12331159869893296, '1110': 0.07174919872960005, '1111': 0.10184806171792007}\n", + " [0.22750885 0.07114451 0.22750885 0.07114451 0.06042361 0.10061123]\n", "\n", "State:\n", - " [ 0.08809627-0.27595005j 0.24859731-0.22695421j 0.18807826+0.18988408j\n", - " 0.09444097+0.06846085j 0.00470148+0.30764671j 0.17371395-0.09247188j\n", - " -0.18900305+0.12545316j -0.17359753+0.20399288j -0.0517478 +0.04471215j\n", - " -0.0411739 -0.06230031j 0.22377064+0.07842041j -0.21784975-0.27541439j\n", - " -0.27208941+0.04098933j -0.22748127+0.04185292j 0.17105258-0.10503745j\n", - " -0.01729753-0.31866731j]\n", + " [-0.13931487-0.20252155j -0.21974897+0.25655351j 0.02884164-0.06302479j\n", + " -0.15237615+0.03819857j -0.3172761 -0.35615268j -0.10121749+0.09769272j\n", + " 0.14826026-0.0546699j -0.1814861 -0.06726486j -0.10907416+0.29784906j\n", + " 0.30276076+0.11316387j 0.07771728+0.07871383j -0.12379363+0.0676409j\n", + " -0.21860099+0.15283362j 0.06862868+0.28848261j -0.04640848-0.18968056j\n", + " -0.22262942-0.04760056j]\n", "\n" ] } @@ -376,7 +397,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 28, "id": "8a868f3e-8383-47ee-a93a-27c5f85e48c5", "metadata": {}, "outputs": [], @@ -396,7 +417,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 29, "id": "68122cd3-662f-4fd1-bb9c-d33b6f5448dd", "metadata": {}, "outputs": [ @@ -405,47 +426,37 @@ "text/plain": [ "{'nqubits': 4,\n", " 'backend': QMatchaTeaBackend(),\n", - " 'measures': {'0000': 88,\n", - " '0001': 7,\n", - " '0010': 90,\n", - " '0011': 78,\n", - " '0100': 72,\n", - " '0101': 56,\n", - " '0110': 47,\n", - " '0111': 41,\n", - " '1000': 120,\n", - " '1001': 7,\n", - " '1010': 41,\n", - " '1011': 53,\n", - " '1100': 20,\n", - " '1101': 129,\n", - " '1110': 73,\n", - " '1111': 102},\n", + " 'measures': {'0000': 75,\n", + " '0001': 113,\n", + " '0010': 202,\n", + " '0011': 59,\n", + " '0100': 9,\n", + " '0101': 14,\n", + " '0110': 30,\n", + " '0111': 37,\n", + " '1000': 130,\n", + " '1001': 113,\n", + " '1010': 19,\n", + " '1011': 95,\n", + " '1100': 19,\n", + " '1101': 26,\n", + " '1110': 39,\n", + " '1111': 44},\n", " 'measured_probabilities': {'U': [None],\n", - " 'E': {'0000': 0.08390937969317301,\n", - " '0010': 0.09466860481989439,\n", - " '0011': 0.07571277233522165,\n", - " '0100': 0.07142939529687124,\n", - " '0101': 0.05622305772698632,\n", - " '0110': 0.05146064807369245,\n", - " '1000': 0.11330883548333581,\n", - " '1011': 0.053499396925872765,\n", - " '1101': 0.12331159869893296,\n", - " '1110': 0.07174919872960005,\n", - " '1111': 0.10184806171792007},\n", + " 'E': {'0000': 0.06042361322153613,\n", + " '0001': 0.1006112332603114,\n", + " '0010': 0.227508852106648,\n", + " '0011': 0.07114450628060452,\n", + " '1000': 0.11410931311301056,\n", + " '1001': 0.10447013788218423,\n", + " '1011': 0.08793210997480687,\n", + " '1111': 0.05182967072718676},\n", " 'G': [None]},\n", " 'prob_type': 'E',\n", - " 'statevector': array([ 0.08809627-0.27595005j, 0.24859731-0.22695421j,\n", - " 0.18807826+0.18988408j, 0.09444097+0.06846085j,\n", - " 0.00470148+0.30764671j, 0.17371395-0.09247188j,\n", - " -0.18900305+0.12545316j, -0.17359753+0.20399288j,\n", - " -0.0517478 +0.04471215j, -0.0411739 -0.06230031j,\n", - " 0.22377064+0.07842041j, -0.21784975-0.27541439j,\n", - " -0.27208941+0.04098933j, -0.22748127+0.04185292j,\n", - " 0.17105258-0.10503745j, -0.01729753-0.31866731j])}" + " 'statevector': None}" ] }, - "execution_count": 12, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -466,7 +477,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 30, "id": "ef0e9591-ccca-4cdd-a81b-2bfb3caaf3d0", "metadata": {}, "outputs": [ @@ -475,7 +486,7 @@ "output_type": "stream", "text": [ "Frequencies:\n", - " {'0000': 88, '0001': 7, '0010': 90, '0011': 78, '0100': 72, '0101': 56, '0110': 47, '0111': 41, '1000': 120, '1001': 7, '1010': 41, '1011': 53, '1100': 20, '1101': 129, '1110': 73, '1111': 102}\n", + " {'0000': 75, '0001': 113, '0010': 202, '0011': 59, '0100': 9, '0101': 14, '0110': 30, '0111': 37, '1000': 130, '1001': 113, '1010': 19, '1011': 95, '1100': 19, '1101': 26, '1110': 39, '1111': 44}\n", "\n" ] } @@ -495,7 +506,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 31, "id": "9e9632d5-28dd-4846-8457-c579d0cb9453", "metadata": {}, "outputs": [], @@ -563,7 +574,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 32, "id": "5620f476-1bc7-4cf8-9868-b127c715b6ec", "metadata": {}, "outputs": [], @@ -582,7 +593,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 33, "id": "32d605e9-672a-40b6-8a1a-eb17bbc3c45e", "metadata": {}, "outputs": [ @@ -621,7 +632,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 34, "id": "0b46e315-7786-4247-bd2a-83ea1c5842eb", "metadata": {}, "outputs": [], @@ -631,7 +642,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 35, "id": "37385485-e8a3-4ab0-ad44-bcc4e9da24ca", "metadata": {}, "outputs": [ @@ -639,10 +650,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "0: ─RY─RZ─o─────X─RY─RZ─o─────X─RY─RZ─o─────X─M─\n", - "1: ─RY─RZ─X─o───|─RY─RZ─X─o───|─RY─RZ─X─o───|─M─\n", - "2: ─RY─RZ───X─o─|─RY─RZ───X─o─|─RY─RZ───X─o─|─M─\n", - "3: ─RY─RZ─────X─o─RY─RZ─────X─o─RY─RZ─────X─o─M─\n" + "0: ─RY─RZ─o─────X─RY─RZ─o─────X─M─\n", + "1: ─RY─RZ─X─o───|─RY─RZ─X─o───|─M─\n", + "2: ─RY─RZ───X─o─|─RY─RZ───X─o─|─M─\n", + "3: ─RY─RZ─────X─o─RY─RZ─────X─o─M─\n" ] } ], @@ -658,33 +669,19 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 36, "id": "ddecc910-7804-4199-8577-a7db38a16db8", "metadata": {}, - "outputs": [ - { - "data": { - "text/latex": [ - "$\\displaystyle - 1.5 X_{0} Z_{2} + 0.5 Z_{0} Z_{1} + Z_{3}$" - ], - "text/plain": [ - "-1.5*X0*Z2 + 0.5*Z0*Z1 + Z3" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# We can create an Hamiltonian form\n", "form = 0.5 * Z(0) * Z(1) +- 1.5 * X(0) * Z(2) + Z(3)\n", - "form" + "hamiltonian = hamiltonians.SymbolicHamiltonian(form)" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 37, "id": "a6599df3-989e-4131-8664-3451d0cc4372", "metadata": {}, "outputs": [ @@ -693,24 +690,26 @@ "text/plain": [ "\u001b[0;31mSignature:\u001b[0m \u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpectation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcircuit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mobservable\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mDocstring:\u001b[0m\n", - "Compute the expectation value of a Qibo-friendly ``observable``\n", - "on the Tensor Network constructed from a Qibo ``circuit``.\n", + "Compute the expectation value of a Qibo-friendly ``observable`` on\n", + "the Tensor Network constructed from a Qibo ``circuit``.\n", "\n", - "This method takes a Qibo-style symbolic Hamiltonian (e.g., `X(0)*Z(1) + 2.0*Y(2)*Z(0)`) \n", + "This method takes a Qibo-style symbolic Hamiltonian (e.g., `X(0)*Z(1) + 2.0*Y(2)*Z(0)`)\n", "as the observable, converts it into a Quantum Matcha Tea (qmatchatea) observable\n", - "(using `TNObsTensorProduct` and `TNObsWeightedSum`), and computes its expectation \n", + "(using `TNObsTensorProduct` and `TNObsWeightedSum`), and computes its expectation\n", "value using the provided circuit.\n", "\n", "Args:\n", " circuit: A Qibo quantum circuit object on which the expectation value\n", " is computed. The circuit should be compatible with the qmatchatea\n", " Tensor Network backend.\n", - " observable: The observable whose expectation value we want to compute. \n", + " observable: The observable whose expectation value we want to compute.\n", " This must be provided in the symbolic Hamiltonian form supported by Qibo\n", " (e.g., `X(0)*Y(1)` or `Z(0)*Z(1) + 1.5*Y(2)`).\n", "\n", "Returns:\n", - " qmatchatea.SimulationResult [TEMPORARY]\n", + " qibotn.TensorNetworkResult class, providing methods to retrieve\n", + " probabilities, frequencies and state always according to the chosen\n", + " simulation setup.\n", "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", "\u001b[0;31mType:\u001b[0m method\n" ] @@ -725,17 +724,17 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 38, "id": "163b70a3-814a-4a62-a98a-2ffca933a544", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "0.41329220819521995" + "-0.27540575238693377" ] }, - "execution_count": 22, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -743,30 +742,23 @@ "source": [ "qmatcha_backend.expectation(\n", " circuit=circuit,\n", - " observable=form,\n", + " observable=hamiltonian,\n", ")" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 39, "id": "2d8c4a9c-eca3-49d0-bdbf-ab054172c4e5", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Qibo 0.2.16|INFO|2025-01-28 22:13:05]: Using qibojit (numba) backend on /CPU:0\n" - ] - }, { "data": { "text/plain": [ - "0.4132922081952206" + "-0.275405752386936" ] }, - "execution_count": 23, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } From 292d3573a6e946a5fbe3002a3b3190f10bd31874 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Mon, 10 Feb 2025 16:45:26 +0100 Subject: [PATCH 41/41] feat: improve the configure_tn_simulation function so that we don't need to use external objects --- .../qmatchatea_introduction.ipynb | 302 ++++++++++-------- src/qibotn/backends/qmatchatea.py | 43 ++- 2 files changed, 202 insertions(+), 143 deletions(-) diff --git a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb index 3702fad..645ab1c 100644 --- a/examples/qmatchatea_intro/qmatchatea_introduction.ipynb +++ b/examples/qmatchatea_intro/qmatchatea_introduction.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 2, "id": "6722d94e-e311-48f9-b6df-c6d829bf67fb", "metadata": {}, "outputs": [], @@ -23,9 +23,7 @@ "\n", "import qibo\n", "from qibo import Circuit, gates, hamiltonians\n", - "from qibo.backends import construct_backend\n", - "\n", - "from qmatchatea import QCConvergenceParameters" + "from qibo.backends import construct_backend" ] }, { @@ -38,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 3, "id": "64162116-1555-4a68-811c-01593739d622", "metadata": {}, "outputs": [], @@ -63,7 +61,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 4, "id": "4a22a172-f50d-411d-afa3-fa61937c7b3a", "metadata": {}, "outputs": [], @@ -82,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 5, "id": "76f23c57-6d08-496b-9a27-52fb63bbfcb1", "metadata": {}, "outputs": [ @@ -104,7 +102,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 6, "id": "07b2c097-cea2-42ec-8f1d-b4bbb5b71d98", "metadata": {}, "outputs": [], @@ -127,18 +125,75 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 10, "id": "34452bfd-2287-4b38-8099-e072239eab74", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m\n", + "\u001b[0mqmatcha_backend\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfigure_tn_simulation\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mansatz\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'MPS'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mmax_bond_dimension\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mcut_ratio\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mfloat\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1e-09\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mtrunc_tracking_mode\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'C'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0msvd_control\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'A'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mini_bond_dimension\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDocstring:\u001b[0m\n", + "Configure TN simulation given Quantum Matcha Tea interface.\n", + "\n", + "Args:\n", + " ansatz (str): tensor network ansatz. It can be tree tensor network \"TTN\"\n", + " or Matrix Product States \"MPS\" (default).\n", + " max_bond_dimension : int, optional Maximum bond dimension of the problem. Default to 10.\n", + " cut_ratio : float, optional\n", + " Cut ratio for singular values. If :math:`\\lambda_n/\\lambda_1 <` cut_ratio then\n", + " :math:`\\lambda_n` is neglected. Default to 1e-9.\n", + " trunc_tracking_mode : str, optional\n", + " Modus for storing truncation, 'M' for maximum, 'C' for\n", + " cumulated (default).\n", + " svd_ctrl : character, optional\n", + " Control for the SVD algorithm. Available:\n", + " - \"A\" : automatic. Some heuristic is run to choose the best mode for the algorithm.\n", + " - \"V\" : gesvd. Safe but slow method.\n", + " - \"D\" : gesdd. Fast iterative method. It might fail. Resort to gesvd if it fails\n", + " - \"E\" : eigenvalue decomposition method. Faster on GPU. Available only when\n", + " contracting the singular value to left or right\n", + " - \"X\" : sparse eigenvalue decomposition method. Used when you reach the maximum\n", + " bond dimension.\n", + " - \"R\" : random svd method. Used when you reach the maximum bond dimension.\n", + " Default to 'A'.\n", + " ini_bond_dimension: int, optional\n", + " Initial bond dimension of the simulation. It is used if the initial state is random.\n", + " Default to 1.\n", + "\u001b[0;31mFile:\u001b[0m ~/Documents/PhD/qibotn/src/qibotn/backends/qmatchatea.py\n", + "\u001b[0;31mType:\u001b[0m method\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# And then call the dedicate configuration function\n", + "# which is especially written to match the QuantumMatchaTea requirements\n", + "qmatcha_backend.configure_tn_simulation?" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2ee03e94-d794-4a51-9e76-01e8d8a259ba", + "metadata": {}, "outputs": [], "source": [ - "# We first define the useful objects \n", - "convergence_parameters = QCConvergenceParameters(cut_ratio=1e-6, max_bond_dimension=50)\n", - "\n", - "# And then call the dedicate configuration function\n", + "# Let's set a simple customization\n", "qmatcha_backend.configure_tn_simulation(\n", - " convergence_params=convergence_parameters,\n", " ansatz=\"MPS\",\n", + " max_bond_dimension=10,\n", + " cut_ratio=1e-6,\n", ")" ] }, @@ -157,7 +212,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 14, "id": "221ef886-5578-4200-a019-dcafa51aada3", "metadata": {}, "outputs": [ @@ -220,31 +275,7 @@ }, { "cell_type": "code", - "execution_count": 22, - "id": "f271d3f3-c7f2-49b3-94e2-d6e843a169d0", - "metadata": {}, - "outputs": [], - "source": [ - "circuit = build_circuit(nqubits=4, nlayers=2)\n", - "\n", - "# Setting random parameters\n", - "circuit.set_parameters(\n", - " parameters=np.random.uniform(-np.pi, np.pi, len(circuit.get_parameters())),\n", - ")\n", - "\n", - "# We first define the useful objects \n", - "convergence_parameters = QCConvergenceParameters(cut_ratio=1e-12, max_bond_dimension=1100)\n", - "\n", - "# And then call the dedicate configuration function\n", - "qmatcha_backend.configure_tn_simulation(\n", - " convergence_params=convergence_parameters,\n", - " ansatz=\"MPS\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 23, + "execution_count": 17, "id": "35a244c3-adba-4b8b-b28c-0ab592b0f7cf", "metadata": {}, "outputs": [ @@ -254,36 +285,36 @@ "{'nqubits': 4,\n", " 'backend': QMatchaTeaBackend(),\n", " 'measures': None,\n", - " 'measured_probabilities': {'U': {'0000': (0.0, 0.06042361322153613),\n", - " '0001': (0.06042361322153613, 0.16103484648184754),\n", - " '0010': (0.1610348464818476, 0.3885436985884956),\n", - " '0011': (0.3885436985884956, 0.4596882048691001),\n", - " '0100': (0.4596882048691, 0.46449216980829905),\n", - " '0101': (0.46449216980829905, 0.47672801247815266),\n", - " '0110': (0.47672801247815266, 0.5016979158974251),\n", - " '0111': (0.5016979158974251, 0.5398303779135739),\n", - " '1000': (0.539830377913574, 0.6539396910265846),\n", - " '1001': (0.6539396910265846, 0.7584098289087688),\n", - " '1010': (0.7584098289087688, 0.7781986778747929),\n", - " '1011': (0.7781986778747929, 0.8661307878495997),\n", - " '1100': (0.8661307878495996, 0.8908084103632258),\n", - " '1101': (0.8908084103632258, 0.9107085647613623),\n", - " '1110': (0.9107085647613623, 0.9481703292728131),\n", - " '1111': (0.9481703292728131, 0.9999999999999999)},\n", + " 'measured_probabilities': {'U': {'0000': (0.0, 0.009075021799813076),\n", + " '0001': (0.009075021799813076, 0.013505513590974613),\n", + " '0010': (0.01350551359097461, 0.03969337383808732),\n", + " '0011': (0.03969337383808732, 0.3501239760030636),\n", + " '0100': (0.3501239760030635, 0.3525850615313512),\n", + " '0101': (0.3525850615313512, 0.37831917247243874),\n", + " '0110': (0.37831917247243874, 0.5888353440545037),\n", + " '0111': (0.5888353440545037, 0.5934703289984129),\n", + " '1000': (0.5934703289984128, 0.6896713158046865),\n", + " '1001': (0.6896713158046865, 0.7110042815132214),\n", + " '1010': (0.7110042815132213, 0.7242989335460854),\n", + " '1011': (0.7242989335460854, 0.7640693817193864),\n", + " '1100': (0.7640693817193864, 0.7641799598480784),\n", + " '1101': (0.7641799598480784, 0.9530705920006985),\n", + " '1110': (0.9530705920006985, 0.989871146703016),\n", + " '1111': (0.989871146703016, 1.0000000000000002)},\n", " 'E': [None],\n", " 'G': [None]},\n", " 'prob_type': 'U',\n", - " 'statevector': array([-0.13931487-0.20252155j, -0.21974897+0.25655351j,\n", - " 0.02884164-0.06302479j, -0.15237615+0.03819857j,\n", - " -0.3172761 -0.35615268j, -0.10121749+0.09769272j,\n", - " 0.14826026-0.0546699j , -0.1814861 -0.06726486j,\n", - " -0.10907416+0.29784906j, 0.30276076+0.11316387j,\n", - " 0.07771728+0.07871383j, -0.12379363+0.0676409j ,\n", - " -0.21860099+0.15283362j, 0.06862868+0.28848261j,\n", - " -0.04640848-0.18968056j, -0.22262942-0.04760056j])}" + " 'statevector': array([-0.0381929 -0.08727155j, -0.25811667+0.17197899j,\n", + " -0.04960402-0.00072563j, 0.00323629-0.01000523j,\n", + " 0.0625107 +0.14926578j, -0.11530027-0.00070762j,\n", + " -0.35885958-0.28589503j, -0.08233943+0.17326504j,\n", + " 0.06345512-0.02009824j, 0.03609859+0.14152688j,\n", + " -0.11045046+0.11633919j, -0.27587224+0.33583499j,\n", + " 0.46347251+0.30923103j, 0.02322259-0.19806857j,\n", + " -0.00893755+0.06749152j, -0.07071717-0.0716096j ])}" ] }, - "execution_count": 23, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -298,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 19, "id": "60501c3d-2a44-421f-b434-4a508714b132", "metadata": {}, "outputs": [ @@ -310,24 +341,24 @@ " 'measures': None,\n", " 'measured_probabilities': {'U': [None],\n", " 'E': [None],\n", - " 'G': {'0010': 0.227508852106648,\n", - " '0011': 0.07114450628060452,\n", - " '1000': 0.227508852106648,\n", - " '1001': 0.07114450628060452,\n", - " '0000': 0.06042361322153613,\n", - " '0001': 0.1006112332603114}},\n", + " 'G': {'0010': 0.02618786024711271,\n", + " '0011': 0.3104306021649763,\n", + " '1100': 0.00011057812869196484,\n", + " '1101': 0.18889063215262014,\n", + " '0110': 0.21051617158206493,\n", + " '0111': 0.0046349849439092155}},\n", " 'prob_type': 'G',\n", - " 'statevector': array([-0.13931487-0.20252155j, -0.21974897+0.25655351j,\n", - " 0.02884164-0.06302479j, -0.15237615+0.03819857j,\n", - " -0.3172761 -0.35615268j, -0.10121749+0.09769272j,\n", - " 0.14826026-0.0546699j , -0.1814861 -0.06726486j,\n", - " -0.10907416+0.29784906j, 0.30276076+0.11316387j,\n", - " 0.07771728+0.07871383j, -0.12379363+0.0676409j ,\n", - " -0.21860099+0.15283362j, 0.06862868+0.28848261j,\n", - " -0.04640848-0.18968056j, -0.22262942-0.04760056j])}" + " 'statevector': array([-0.0381929 -0.08727155j, -0.25811667+0.17197899j,\n", + " -0.04960402-0.00072563j, 0.00323629-0.01000523j,\n", + " 0.0625107 +0.14926578j, -0.11530027-0.00070762j,\n", + " -0.35885958-0.28589503j, -0.08233943+0.17326504j,\n", + " 0.06345512-0.02009824j, 0.03609859+0.14152688j,\n", + " -0.11045046+0.11633919j, -0.27587224+0.33583499j,\n", + " 0.46347251+0.30923103j, 0.02322259-0.19806857j,\n", + " -0.00893755+0.06749152j, -0.07071717-0.0716096j ])}" ] }, - "execution_count": 26, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -338,7 +369,7 @@ "outcome = qmatcha_backend.execute_circuit(\n", " circuit=circuit,\n", " prob_type=\"G\",\n", - " prob_threshold=0.9,\n", + " prob_threshold=0.7,\n", " return_array=True,\n", ")\n", "\n", @@ -358,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 20, "id": "c0443efc-21ef-4ed5-9cf4-785d204a1881", "metadata": {}, "outputs": [ @@ -367,15 +398,16 @@ "output_type": "stream", "text": [ "Probabilities:\n", - " [0.22750885 0.07114451 0.22750885 0.07114451 0.06042361 0.10061123]\n", + " [2.61878602e-02 3.10430602e-01 1.10578129e-04 1.88890632e-01\n", + " 2.10516172e-01 4.63498494e-03]\n", "\n", "State:\n", - " [-0.13931487-0.20252155j -0.21974897+0.25655351j 0.02884164-0.06302479j\n", - " -0.15237615+0.03819857j -0.3172761 -0.35615268j -0.10121749+0.09769272j\n", - " 0.14826026-0.0546699j -0.1814861 -0.06726486j -0.10907416+0.29784906j\n", - " 0.30276076+0.11316387j 0.07771728+0.07871383j -0.12379363+0.0676409j\n", - " -0.21860099+0.15283362j 0.06862868+0.28848261j -0.04640848-0.18968056j\n", - " -0.22262942-0.04760056j]\n", + " [-0.0381929 -0.08727155j -0.25811667+0.17197899j -0.04960402-0.00072563j\n", + " 0.00323629-0.01000523j 0.0625107 +0.14926578j -0.11530027-0.00070762j\n", + " -0.35885958-0.28589503j -0.08233943+0.17326504j 0.06345512-0.02009824j\n", + " 0.03609859+0.14152688j -0.11045046+0.11633919j -0.27587224+0.33583499j\n", + " 0.46347251+0.30923103j 0.02322259-0.19806857j -0.00893755+0.06749152j\n", + " -0.07071717-0.0716096j ]\n", "\n" ] } @@ -397,7 +429,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 21, "id": "8a868f3e-8383-47ee-a93a-27c5f85e48c5", "metadata": {}, "outputs": [], @@ -417,7 +449,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 22, "id": "68122cd3-662f-4fd1-bb9c-d33b6f5448dd", "metadata": {}, "outputs": [ @@ -426,37 +458,33 @@ "text/plain": [ "{'nqubits': 4,\n", " 'backend': QMatchaTeaBackend(),\n", - " 'measures': {'0000': 75,\n", - " '0001': 113,\n", - " '0010': 202,\n", - " '0011': 59,\n", - " '0100': 9,\n", - " '0101': 14,\n", - " '0110': 30,\n", - " '0111': 37,\n", - " '1000': 130,\n", - " '1001': 113,\n", - " '1010': 19,\n", - " '1011': 95,\n", - " '1100': 19,\n", - " '1101': 26,\n", - " '1110': 39,\n", - " '1111': 44},\n", + " 'measures': {'0000': 12,\n", + " '0001': 5,\n", + " '0010': 20,\n", + " '0011': 307,\n", + " '0100': 5,\n", + " '0101': 35,\n", + " '0110': 207,\n", + " '0111': 4,\n", + " '1000': 107,\n", + " '1001': 19,\n", + " '1010': 11,\n", + " '1011': 41,\n", + " '1100': 1,\n", + " '1101': 199,\n", + " '1110': 37,\n", + " '1111': 14},\n", " 'measured_probabilities': {'U': [None],\n", - " 'E': {'0000': 0.06042361322153613,\n", - " '0001': 0.1006112332603114,\n", - " '0010': 0.227508852106648,\n", - " '0011': 0.07114450628060452,\n", - " '1000': 0.11410931311301056,\n", - " '1001': 0.10447013788218423,\n", - " '1011': 0.08793210997480687,\n", - " '1111': 0.05182967072718676},\n", + " 'E': {'0011': 0.3104306021649763,\n", + " '0110': 0.21051617158206493,\n", + " '1000': 0.09620098680627374,\n", + " '1101': 0.18889063215262014},\n", " 'G': [None]},\n", " 'prob_type': 'E',\n", " 'statevector': None}" ] }, - "execution_count": 29, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -477,7 +505,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 23, "id": "ef0e9591-ccca-4cdd-a81b-2bfb3caaf3d0", "metadata": {}, "outputs": [ @@ -486,7 +514,7 @@ "output_type": "stream", "text": [ "Frequencies:\n", - " {'0000': 75, '0001': 113, '0010': 202, '0011': 59, '0100': 9, '0101': 14, '0110': 30, '0111': 37, '1000': 130, '1001': 113, '1010': 19, '1011': 95, '1100': 19, '1101': 26, '1110': 39, '1111': 44}\n", + " {'0000': 12, '0001': 5, '0010': 20, '0011': 307, '0100': 5, '0101': 35, '0110': 207, '0111': 4, '1000': 107, '1001': 19, '1010': 11, '1011': 41, '1100': 1, '1101': 199, '1110': 37, '1111': 14}\n", "\n" ] } @@ -506,7 +534,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 24, "id": "9e9632d5-28dd-4846-8457-c579d0cb9453", "metadata": {}, "outputs": [], @@ -574,7 +602,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 25, "id": "5620f476-1bc7-4cf8-9868-b127c715b6ec", "metadata": {}, "outputs": [], @@ -593,7 +621,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 26, "id": "32d605e9-672a-40b6-8a1a-eb17bbc3c45e", "metadata": {}, "outputs": [ @@ -632,7 +660,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 27, "id": "0b46e315-7786-4247-bd2a-83ea1c5842eb", "metadata": {}, "outputs": [], @@ -642,7 +670,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 28, "id": "37385485-e8a3-4ab0-ad44-bcc4e9da24ca", "metadata": {}, "outputs": [ @@ -669,10 +697,18 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 29, "id": "ddecc910-7804-4199-8577-a7db38a16db8", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.15|INFO|2025-02-10 16:44:30]: Using qibojit (numba) backend on /CPU:0\n" + ] + } + ], "source": [ "# We can create an Hamiltonian form\n", "form = 0.5 * Z(0) * Z(1) +- 1.5 * X(0) * Z(2) + Z(3)\n", @@ -681,7 +717,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 30, "id": "a6599df3-989e-4131-8664-3451d0cc4372", "metadata": {}, "outputs": [ @@ -724,17 +760,17 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 31, "id": "163b70a3-814a-4a62-a98a-2ffca933a544", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "-0.27540575238693377" + "-0.21133814688683614" ] }, - "execution_count": 38, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -748,17 +784,17 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 32, "id": "2d8c4a9c-eca3-49d0-bdbf-ab054172c4e5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "-0.275405752386936" + "-0.2113381468868367" ] }, - "execution_count": 39, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } diff --git a/src/qibotn/backends/qmatchatea.py b/src/qibotn/backends/qmatchatea.py index 348c40a..27239ec 100644 --- a/src/qibotn/backends/qmatchatea.py +++ b/src/qibotn/backends/qmatchatea.py @@ -30,23 +30,46 @@ class QMatchaTeaBackend(QibotnBackend, NumpyBackend): def configure_tn_simulation( self, ansatz: str = "MPS", - convergence_params=None, + max_bond_dimension: int = 10, + cut_ratio: float = 1e-9, + trunc_tracking_mode: str = "C", + svd_control: str = "A", + ini_bond_dimension: int = 1, ): """Configure TN simulation given Quantum Matcha Tea interface. Args: ansatz (str): tensor network ansatz. It can be tree tensor network "TTN" - or Matrix Product States "MPS" (default). - convergence_params (qmatchatea.utils.QCConvergenceParameters): - 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 - for more instructions. If not passed, the default values proposed - by Quantum Matcha Tea's authors are set. + or Matrix Product States "MPS" (default). + max_bond_dimension : int, optional Maximum bond dimension of the problem. Default to 10. + cut_ratio : float, optional + Cut ratio for singular values. If :math:`\\lambda_n/\\lambda_1 <` cut_ratio then + :math:`\\lambda_n` is neglected. Default to 1e-9. + trunc_tracking_mode : str, optional + Modus for storing truncation, 'M' for maximum, 'C' for + cumulated (default). + svd_ctrl : character, optional + Control for the SVD algorithm. Available: + - "A" : automatic. Some heuristic is run to choose the best mode for the algorithm. + - "V" : gesvd. Safe but slow method. + - "D" : gesdd. Fast iterative method. It might fail. Resort to gesvd if it fails + - "E" : eigenvalue decomposition method. Faster on GPU. Available only when + contracting the singular value to left or right + - "X" : sparse eigenvalue decomposition method. Used when you reach the maximum + bond dimension. + - "R" : random svd method. Used when you reach the maximum bond dimension. + Default to 'A'. + ini_bond_dimension: int, optional + Initial bond dimension of the simulation. It is used if the initial state is random. + Default to 1. """ - # Set configurations or defaults - self.convergence_params = ( - convergence_params or qmatchatea.QCConvergenceParameters() + self.convergence_params = qmatchatea.QCConvergenceParameters( + max_bond_dimension=max_bond_dimension, + cut_ratio=cut_ratio, + trunc_tracking_mode=trunc_tracking_mode, + svd_ctrl=svd_control, + ini_bond_dimension=ini_bond_dimension, ) self.ansatz = ansatz