From 08c26a75382ccf4d9468e5002913672a25f0d207 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 14:39:44 +0800 Subject: [PATCH 01/28] tweak train_QAOA to also allow for non-shot based calculations --- src/qiboopt/opt_class/opt_class.py | 55 +++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index 35e3645..afb77fb 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -523,7 +523,8 @@ def train_QAOA( betas (List[float], optional): parameters for X mixers. alphas (List[float], optional): parameters for Y mixers for XQAOA. Defaults to None. p (int, optional): number of layers. - nshots (int, optional): number of shots + nshots (int, optional): Number of shots for sampled execution. + If ``None`` or ``0``, uses exact (no-shot) execution. regular_loss (Bool, optional): If False, Conditional Variance at Risk (CVaR) is used as cost function. Defaults to True, where expected value is used as cost function. maxiter (int, optional): Maximum number of iterations used in the minimiser. Defaults to 10. @@ -544,7 +545,9 @@ def train_QAOA( - params (List[float]): Optimised QAOA parameters. - extra (dict): Additional metadata (e.g., convergence info). - circuit (:class:`qibo.models.Circuit`): Final circuit used for evaluation. - - frequencies (dict): Bitstring outcome frequencies from measurement. + - frequencies (dict): Bitstring outcome statistics. + In sampled mode (``nshots`` > 0), values are counts. + In exact mode (``nshots`` is ``None`` or ``0``), values are probabilities. Example: .. testcode:: @@ -560,6 +563,7 @@ def train_QAOA( """ backend = _check_backend(backend) + use_exact = (nshots is None) or (nshots == 0) if p is None and gammas is None: raise_error( @@ -614,7 +618,11 @@ def myloss(parameters): if noise_model is not None: circuit = noise_model.apply(circuit) - result = circuit(None, nshots) + if use_exact: + hamiltonian = self.qubo_to_qaoa_object().hamiltonian + return hamiltonian.expectation(circuit, nshots=None) + + result = backend.execute_circuit(circuit, nshots=nshots) result_counter = result.frequencies(binary=True) energy_dict = defaultdict(int) for key in result_counter: @@ -650,20 +658,28 @@ def myloss(parameters, delta=cvar_delta): ) if noise_model is not None: circuit = noise_model.apply(circuit) - result = backend.execute_circuit(circuit, nshots=nshots) - result_counter = result.frequencies(binary=True) + if use_exact: + result = backend.execute_circuit(circuit) + result_probs = _probability_dict_from_state(result) + energy_probs = defaultdict(float) + for key, probability in result_probs.items(): + x = [int(sub_key) for sub_key in key] + energy_probs[self.evaluate_f(x)] += probability + else: + result = backend.execute_circuit(circuit, nshots=nshots) + result_counter = result.frequencies(binary=True) - energy_dict = defaultdict(int) - for key in result_counter: - # key is the binary string, value is the frequency - x = [int(sub_key) for sub_key in key] - energy_dict[self.evaluate_f(x)] += result_counter[key] + energy_dict = defaultdict(int) + for key in result_counter: + # key is the binary string, value is the frequency + x = [int(sub_key) for sub_key in key] + energy_dict[self.evaluate_f(x)] += result_counter[key] - # Normalize frequencies to probabilities - total_counts = sum(energy_dict.values()) - energy_probs = { - key: value / total_counts for key, value in energy_dict.items() - } + # Normalize frequencies to probabilities + total_counts = sum(energy_dict.values()) + energy_probs = { + key: value / total_counts for key, value in energy_dict.items() + } # Sort energies and compute cumulative probability sorted_energies = sorted( @@ -708,7 +724,12 @@ def myloss(parameters, delta=cvar_delta): if noise_model is not None: circuit = noise_model.apply(circuit) - result = backend.execute_circuit(circuit, nshots=nshots) + if use_exact: + result = backend.execute_circuit(circuit) + statistics = _probability_dict_from_state(result) + else: + result = backend.execute_circuit(circuit, nshots=nshots) + statistics = result.frequencies(binary=True) if noise_model is not None: return ( @@ -719,7 +740,7 @@ def myloss(parameters, delta=cvar_delta): result.frequencies(binary=True), original_circuit, ) - return best, params, extra, circuit, result.frequencies(binary=True) + return best, params, extra, circuit, statistics def qubo_to_qaoa_object(self, params: list = None): """ From 6282279fa5e342eb4a7e431da9ccb1623da20404 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:31:15 +0800 Subject: [PATCH 02/28] added adapter to use qiboml in QAOA optimisation --- src/qiboopt/integrations/qiboml_adapter.py | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/qiboopt/integrations/qiboml_adapter.py diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py new file mode 100644 index 0000000..9a881e8 --- /dev/null +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -0,0 +1,129 @@ +"""qiboml integration helpers for QAOA training.""" + +from __future__ import annotations + +from typing import Any, Dict, Optional + +import numpy as np + + +def _energy_shift(qubo) -> float: + """Constant shift between Ising expectation and QUBO objective value.""" + _h, _J, constant = qubo.qubo_to_ising() + return float(constant) + + +def _get_differentiation_class(name: Optional[str]): + if name is None or name == 'torch': + return None + from qiboml.operations.differentiation import Adjoint, Jax, PSR + + mapping = { + "psr": PSR, + "jax": Jax, + "adjoint": Adjoint, + "torch": None, + } + diff = mapping.get(name.lower()) + if diff is None: + raise ValueError( + "Unknown qiboml differentiation method. " + "Supported values are: None, 'psr', 'jax', 'adjoint', 'torch'." + ) + return diff + + +def optimize_qaoa_with_qiboml( + *, + qubo, + parameters, + p: int, + nshots: Optional[int], + noise_model, + custom_mixer, + has_alphas: bool, + optimizer: str, + lr: float, + epochs: int, + differentiation: Optional[str], + backend, +) -> tuple[float, np.ndarray, Dict[str, Any]]: + """Optimize QAOA parameters using qiboml's pytorch interface.""" + try: + import torch + except ImportError as exc: + raise ImportError( + "engine='qiboml' requires torch. Install optional dependencies, " + "for example with `poetry install --with qiboml`." + ) from exc + + try: + from qiboml.interfaces.pytorch import QuantumModel + from qiboml.models.decoding import Expectation + except ImportError as exc: + raise ImportError( + "engine='qiboml' requires qiboml. Install optional dependencies, " + "for example with `poetry install --with qiboml`." + ) from exc + + # Reuse qiboopt's own QAOA-object construction path for the Hamiltonian. + hamiltonian = qubo.qubo_to_qaoa_object().hamiltonian + if not hasattr(hamiltonian, "expectation_from_circuit"): + # qiboml 0.1.0 calls this method on SymbolicHamiltonian. + hamiltonian.expectation_from_circuit = hamiltonian.expectation + circuit_builder = qubo.make_qaoa_circuit_callable( + p=p, + custom_mixer=custom_mixer, + has_alphas=has_alphas, + include_measurements=False, + ) + decoder = Expectation( + nqubits=qubo.n, + observable=hamiltonian, + nshots=nshots, + noise_model=noise_model, + backend=backend, + ) + energy_shift = _energy_shift(qubo) + + diff_class = _get_differentiation_class(differentiation) + model = QuantumModel( + circuit_structure=[circuit_builder], + decoding=decoder, + parameters_initialization=np.asarray(parameters, dtype=np.float64), + differentiation=diff_class, + ) + model = model.to(dtype=torch.float64) + + optimizer_name = optimizer.lower() + if optimizer_name == "adam": + torch_optimizer = torch.optim.Adam(model.parameters(), lr=lr) + elif optimizer_name == "sgd": + torch_optimizer = torch.optim.SGD(model.parameters(), lr=lr) + else: + raise ValueError("Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'.") + + losses = [] + best = float("inf") + best_params = np.asarray(parameters, dtype=np.float64) + for _ in range(epochs): + torch_optimizer.zero_grad() + loss = model() + if loss.ndim > 0: + loss = loss.reshape(-1)[0] + loss.backward() + torch_optimizer.step() + loss_value = float(loss.detach().cpu().item()) + energy_shift + losses.append(loss_value) + if loss_value < best: + best = loss_value + best_params = model.circuit_parameters.detach().cpu().numpy().copy() + + extra = { + "engine": "qiboml", + "optimizer": optimizer_name, + "learning_rate": lr, + "epochs": epochs, + "loss_history": losses, + } + return best, best_params, extra From 590ca8688a4b84ff63e784d164ed053054afd7f3 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:44:03 +0800 Subject: [PATCH 03/28] modified QUBO to allow for qiboml optimisation --- src/qiboopt/opt_class/opt_class.py | 210 ++++++++++++++++++++++------- 1 file changed, 164 insertions(+), 46 deletions(-) diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index afb77fb..910a3ee 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -3,6 +3,7 @@ """ import itertools +import inspect from collections import defaultdict import numpy as np @@ -13,6 +14,7 @@ from qibo.models import QAOA from qibo.optimizers import optimize from qibo.symbols import Z +from qiboopt.integrations.qiboml_adapter import optimize_qaoa_with_qiboml class QUBO: @@ -192,7 +194,9 @@ def _default_mixer(self, circuit, beta, alpha=None): if alpha: circuit.add(gates.RY(i, 2 * alpha)) - def _build(self, gammas, betas, alphas=None, custom_mixer=None): + def _build( + self, gammas, betas, alphas=None, custom_mixer=None, include_measurements=True + ): """ Constructs the full QAOA circuit for the Ising model with p layers. custom_mixer (List[:class:`qibo.models.Circuit`]): An optional function that takes as input custom mixers. @@ -236,7 +240,8 @@ def _build(self, gammas, betas, alphas=None, custom_mixer=None): else: self._default_mixer(circuit, betas[layer]) - circuit.add(gates.M(i) for i in range(self.n)) + if include_measurements: + circuit.add(gates.M(i) for i in range(self.n)) return circuit @@ -471,7 +476,14 @@ def canonical_q(self): self.Qdict = Qdict return self.Qdict - def qubo_to_qaoa_circuit(self, gammas, betas, alphas=None, custom_mixer=None): + def qubo_to_qaoa_circuit( + self, + gammas, + betas, + alphas=None, + custom_mixer=None, + include_measurements=True, + ): """ Constructs a QAOA or XQAOA circuit for the given QUBO problem. @@ -483,21 +495,87 @@ def qubo_to_qaoa_circuit(self, gammas, betas, alphas=None, custom_mixer=None): If len(custom_mixer) == 1, then use this one circuit as mixer for all layers. If len(custom_mixer) == len(gammas), then use each circuit as mixer for each layer. If len(custom_mixer) != 1 and != len(gammas), raise an error. + include_measurements (bool, optional): If ``True``, append measurement gates to all qubits. + Defaults to ``True``. Returns: :class:`qibo.models.Circuit`: The QAOA or XQAOA circuit corresponding to the QUBO problem. """ if alphas is not None: # Use XQAOA, ignore mixer_function - circuit = self._build(gammas, betas, alphas) + circuit = self._build( + gammas, betas, alphas, include_measurements=include_measurements + ) else: if custom_mixer: circuit = self._build( - gammas, betas, alphas=None, custom_mixer=custom_mixer + gammas, + betas, + alphas=None, + custom_mixer=custom_mixer, + include_measurements=include_measurements, ) else: - circuit = self._build(gammas, betas) + circuit = self._build( + gammas, betas, include_measurements=include_measurements + ) return circuit + def _split_qaoa_parameters(self, parameters, p, has_alphas=False): + """Unpack a flat QAOA parameter vector in block format.""" + gammas = parameters[:p] + betas = parameters[p : 2 * p] + unpacked_alphas = ( + parameters[2 * p : 3 * p] + if has_alphas + else None + ) + return gammas, betas, unpacked_alphas + + def qaoa_circuit_from_parameters( + self, + parameters, + p, + custom_mixer=None, + include_measurements=True, + has_alphas=False, + ): + """Build a QAOA circuit directly from flat block-ordered parameters.""" + gammas, betas, unpacked_alphas = self._split_qaoa_parameters( + parameters, p, has_alphas=has_alphas + ) + return self.qubo_to_qaoa_circuit( + gammas=gammas, + betas=betas, + alphas=unpacked_alphas, + custom_mixer=custom_mixer, + include_measurements=include_measurements, + ) + + def make_qaoa_circuit_callable( + self, p, custom_mixer=None, has_alphas=False, include_measurements=False + ): + """Create a fixed-arity callable for qiboml circuit tracing.""" + nparams = 3 * p if has_alphas else 2 * p + param_names = [f"theta_{i}" for i in range(nparams)] + signature = inspect.Signature( + [ + inspect.Parameter(name, inspect.Parameter.POSITIONAL_OR_KEYWORD) + for name in param_names + ] + ) + + def qaoa_circuit(*angles): + return self.qaoa_circuit_from_parameters( + parameters=angles, + p=p, + custom_mixer=custom_mixer, + include_measurements=include_measurements, + has_alphas=has_alphas, + ) + + qaoa_circuit.__signature__ = signature + return qaoa_circuit + def train_QAOA( self, gammas=None, @@ -512,6 +590,11 @@ def train_QAOA( custom_mixer=None, backend=None, noise_model=None, + engine="legacy", + optimizer="adam", + lr=0.05, + epochs=100, + differentiation=None, ): """ Constructs the QAOA or XQAOA circuit with optional parameters for the mixers or phases before using a classical @@ -538,6 +621,17 @@ def train_QAOA( If ``None``, it uses the current backend. Defaults to ``None``. noise_model (:class:`qibo.noise.NoiseModel`, optional): noise model applied to simulate noisy computations. Defaults to None. + engine (str, optional): Training engine. ``"legacy"`` uses ``qibo.optimizers.optimize``. + ``"qiboml"`` uses qiboml's pytorch ``QuantumModel`` training loop. Defaults to ``"legacy"``. + optimizer (str, optional): Optimizer name used when ``engine="qiboml"``. + Supported values are ``"adam"`` and ``"sgd"``. Defaults to ``"adam"``. + lr (float, optional): Learning rate used when ``engine="qiboml"``. + Defaults to ``0.05``. + epochs (int, optional): Number of optimization steps used when ``engine="qiboml"``. + Defaults to ``100``. + differentiation (str, optional): Differentiation backend used when ``engine="qiboml"``. + Supported values are ``None``, ``"PSR"``, ``"Jax"``, and ``"Adjoint"``. + Defaults to ``None``. Returns: Tuple[float, List[float], dict, :class:`qibo.models.Circuit`, dict]: A tuple containing: @@ -586,10 +680,51 @@ def train_QAOA( self.n_layers = p self.num_betas = len(betas) + has_alphas = alphas is not None parameters = list(gammas) + list(betas) - if alphas is not None: + if has_alphas: parameters += list(alphas) + + if engine not in ("legacy", "qiboml"): + raise_error( + ValueError, + f"Unsupported engine '{engine}'. Use 'legacy' or 'qiboml'.", + ) + + if engine == "qiboml" and regular_loss: + best, params, extra = optimize_qaoa_with_qiboml( + qubo=self, + parameters=parameters, + p=p, + nshots=nshots, + noise_model=noise_model, + custom_mixer=custom_mixer, + has_alphas=has_alphas, + optimizer=optimizer, + lr=lr, + epochs=epochs, + differentiation=differentiation, + backend=backend, + ) + else: + if engine == "qiboml" and not regular_loss: + # qiboml path currently supports expectation-value optimization only. + engine = "legacy" + if not regular_loss and not (0 < cvar_delta <= 1): + raise_error( + ValueError, + f"cvar_delta must satisfy 0 < cvar_delta <= 1, but got {cvar_delta}.", + ) + + def _probability_dict_from_state(result): + probabilities = np.asarray(result.probabilities()).ravel() + return { + format(index, f"0{self.n}b"): float(probability) + for index, probability in enumerate(probabilities) + if probability > 0 + } + if regular_loss: def myloss(parameters): @@ -603,17 +738,12 @@ def myloss(parameters): loss (float): The computed expectation value. """ - p = len(gammas) - if alphas is not None: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = parameters[2 * p : 3 * p] - else: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = None - circuit = self.qubo_to_qaoa_circuit( - gammas_, betas_, alphas=unpacked_alphas, custom_mixer=custom_mixer + circuit = self.qaoa_circuit_from_parameters( + parameters=parameters, + p=p, + custom_mixer=custom_mixer, + include_measurements=not use_exact, + has_alphas=has_alphas, ) if noise_model is not None: circuit = noise_model.apply(circuit) @@ -644,17 +774,12 @@ def myloss(parameters, delta=cvar_delta): Returns: cvar (float): The computed CVaR value. """ - # m = len(parameters) - if alphas is not None: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = parameters[2 * p : 3 * p] - else: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = None - circuit = self.qubo_to_qaoa_circuit( - gammas_, betas_, alphas=unpacked_alphas, custom_mixer=custom_mixer + circuit = self.qaoa_circuit_from_parameters( + parameters=parameters, + p=p, + custom_mixer=custom_mixer, + include_measurements=not use_exact, + has_alphas=has_alphas, ) if noise_model is not None: circuit = noise_model.apply(circuit) @@ -702,23 +827,16 @@ def myloss(parameters, delta=cvar_delta): cvar = sum(energy * prob for energy, prob in selected_energies) / delta return cvar - best, params, extra = optimize( - myloss, parameters, method=method, options={"maxiter": maxiter} - ) - # Unpack optimised parameters in the same way as in myloss (block format) - if alphas is not None: - optimised_gammas = params[:p] - optimised_betas = params[p : 2 * p] - optimised_alphas = params[2 * p : 3 * p] - else: - optimised_gammas = params[:p] - optimised_betas = params[p : 2 * p] - optimised_alphas = None - circuit = self.qubo_to_qaoa_circuit( - gammas=optimised_gammas, - betas=optimised_betas, - alphas=optimised_alphas, + if engine == "legacy": + best, params, extra = optimize( + myloss, parameters, method=method, options={"maxiter": maxiter} + ) + circuit = self.qaoa_circuit_from_parameters( + parameters=params, + p=p, custom_mixer=custom_mixer, + include_measurements=not use_exact, + has_alphas=has_alphas, ) original_circuit = Circuit.copy(circuit) if noise_model is not None: @@ -737,7 +855,7 @@ def myloss(parameters, delta=cvar_delta): params, extra, circuit, - result.frequencies(binary=True), + statistics, original_circuit, ) return best, params, extra, circuit, statistics From 810bb3c34cc7c7537f78db0b4e8dd7cef9499736 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:44:34 +0800 Subject: [PATCH 04/28] added maxcut notebook example for optimisation with qiboml --- tutorial/Max-Cut-qiboml.ipynb | 411 ++++++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 tutorial/Max-Cut-qiboml.ipynb diff --git a/tutorial/Max-Cut-qiboml.ipynb b/tutorial/Max-Cut-qiboml.ipynb new file mode 100644 index 0000000..80be3b4 --- /dev/null +++ b/tutorial/Max-Cut-qiboml.ipynb @@ -0,0 +1,411 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5099a13c", + "metadata": {}, + "source": [ + "# Maximum-Cut Problem using Variants of Quantum Approximate Optimization Algorithm (QAOA), a small illustration of QiboOpt" + ] + }, + { + "cell_type": "markdown", + "id": "6e77f23f", + "metadata": {}, + "source": [ + "## Maximum-Cut Problem\n" + ] + }, + { + "cell_type": "markdown", + "id": "8a1a5456", + "metadata": {}, + "source": [ + "The **Maximum-Cut Problem** (MCP) can be stated as follows:\n", + "\n", + "> Given a graph G = (V, E) with vertices V and edges E (possibly weighted), the Maximum-Cut Problem seeks to divide the set of verticies into two complementary subsets such that the total weight (or number) of edges between these subsets is maximised.\n", + "\n", + "### Key Points:\n", + "\n", + "1. **Combinatorial Nature:** For a graph with $n$ nodes, there are $2^{n-1} - 1$ possible unique cuts (since swapping the two sets produces the same cut). This exonential growth makes MCP computationally hard for large $n$.\n", + "\n", + "2. **Applications:**\n", + " - **Machine Learning:** \n", + " The Maximum-Cut Problem (MCP) can be reformulated as an optimisation over binary variables, making it useful for tasks such as binary classification and clustering.\n", + "\n", + " - **Statistical / Theoretical Physics:** \n", + " MCP is closely related to minimising the **Hamiltonian** of spin glass systems. \n", + " *By negating the edge weights, a minimisation problem can be transformed into a maximisation one — and vice versa.*\n", + "\n", + " - **Network Design & Circuit Layout:** \n", + " It also arises in applications such as community detection, VLSI (Very Large-Scale Integration) circuit layout optimisation, and social network analysis.\n", + "\n", + "\n", + "3. **Optimisation Goal:** The objective is to maximise the total weight of the edges crossing the cut." + ] + }, + { + "cell_type": "markdown", + "id": "b472b5ef", + "metadata": {}, + "source": [ + "Here is a $5$-person example where we must split people into two groups to minimise within-group conflict. Equivalently, to maximise the total “conflict” cut. We model pairwise incompatibility with a symmetric weight matrix $W$ (higher = more tension), i.e. an undirected weighted graph:\n", + "\n", + "$$\n", + "\\mathbf{W} =\n", + "\\begin{bmatrix}\n", + "0 & 0.2 & 0.8 & 0.8 & 0.8 \\\\\n", + "0.2 & 0 & 0.8 & 0.8 & 0.8 \\\\\n", + "0.8 & 0.8 & 0 & 0.2 & 0.2 \\\\\n", + "0.8 & 0.8 & 0.2 & 0 & 0.2 \\\\\n", + "0.8 & 0.8 & 0.2 & 0.2 & 0\n", + "\\end{bmatrix}\n", + "$$\n", + "\n", + "The $(i,j)$ entry gives the incompatibility between people $i$ and $j$. It is important to note that the matrix has zero diagonal and is symmetric $(W_{ij} = W_{ji})$.\n", + "\n", + "Let's visualise this:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8d266bf9", + "metadata": {}, + "outputs": [], + "source": [ + "# Setting up the challenge as so\n", + "# The end result should be [0, 0, 1, 1, 1]\n", + "# or [1, 1, 0, 0, 0]\n", + "\n", + "import numpy as np\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "\n", + "dist_matrix = np.array([\n", + " [0, 0.2, 0.8, 0.8, 0.8],\n", + " [0.2, 0, 0.8, 0.8, 0.8],\n", + " [0.8, 0.8, 0, 0.2, 0.2],\n", + " [0.8, 0.8, 0.2, 0, 0.2],\n", + " [0.8, 0.8, 0.2, 0.2, 0]\n", + "])\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0e0bc5ef", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create a weighted undirected graph from the matrix\n", + "G = nx.from_numpy_array(dist_matrix)\n", + "\n", + "# Relabel nodes (0-5)\n", + "mapping = {0:'0', 1:'1', 2:'2', 3:'3', 4:'4'}\n", + "mapping = {0:'0', 1:'1', 2:'2', 3:'3', 4:'4'}\n", + "G = nx.relabel_nodes(G, mapping)\n", + "\n", + "# Draw nodes and edges\n", + "pos = nx.spring_layout(G, seed=42, weight='weight', k=0.6)\n", + "plt.figure(figsize=(6, 5))\n", + "nx.draw_networkx_nodes(G, pos, node_color='#ffd166', node_size=800, edgecolors='black')\n", + "nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold')\n", + "\n", + "# Edge widths and colors proportional to weights\n", + "weights = [G[u][v]['weight'] for u,v in G.edges()]\n", + "nx.draw_networkx_edges(\n", + " G, \n", + " pos, \n", + " width=[3*w for w in weights], \n", + " alpha=0.7, \n", + " edge_color='#118ab2'\n", + " )\n", + "\n", + "# Label edges with weights\n", + "edge_labels = {(u,v): f\"{G[u][v]['weight']:.1f}\" for u,v in G.edges()}\n", + "nx.draw_networkx_edge_labels(\n", + " G, \n", + " pos, \n", + " edge_labels=edge_labels, \n", + " font_size=9, \n", + " font_color='gray',\n", + " label_pos=0.64 \n", + " )\n", + "\n", + "# Output Graph\n", + "plt.title(\"Weighted Graph for Max-Cut Example\", fontsize=13)\n", + "plt.axis('off')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "0471e0f4", + "metadata": {}, + "source": [ + "By splitting the verticies into the sets $S = \\{0, 1\\}$ and $S' = \\{2, 3, 4\\}$, the edges with the higher values (0.8) are cut. This configuration therefore yields a maximum cut, as it maximises the total weight of edges crossing between the two subsets.\n", + "\n", + "In this tutorial, we will explore a similar max-cut instance and demostrate that the optimal partition can be identified in a small, illustrative example using the Quantum Approximate Optimisation Algorithm. This serves as a proof-of-concept showing how MCP can be formulated as a QUBO using Qibo." + ] + }, + { + "cell_type": "markdown", + "id": "2d8aefa3", + "metadata": {}, + "source": [ + "## Encoding of the Maximum Cut Objective as a Quadratic Unconstrained Binary Optimisation (QUBO) Problem" + ] + }, + { + "cell_type": "markdown", + "id": "196d1082", + "metadata": {}, + "source": [ + "The Maximum-Cut problem can be encoded as a Quadratic Unconstrained Binary Optimisation (QUBO) problem. \n", + "\n", + "Consider a graph $G = (V, E)$ with vertex set $V$ and weighted edges $w_{uv}$. We assign to each vertex $u \\in V$ a binary variable $x_u \\in \\set {0, 1}$, which encodes the subset to which the vertex belongs. For example, $x_u = 0$ if the vertex is in set $S$, and $x_u = 1$ if it is in the complementary set $S'$.\n", + "\n", + "An edge $(u, v)$ is said to be cut if its endpoints lie in different subsets: if $x_u \\neq x_v$. The total weight of the cut can then be written as\n", + "\n", + "$$C(x) = \\frac{1}{2} \\sum_{(u,v)\\in E} w_{uv}(x_u-x_v)^2 $$\n", + "\n", + "This quadratic expression naturally defines the QUBO objective for Max-Cut, as it is a function solely of binary variables. The goal is to find the binary assignment {$x_u$} that maximises $C(x)$ thereby maximising the total weight of the edges that are cut.\n", + "\n", + "Using the same approach introduced by Hadfield [4], the Quantum Approximate Optimization Algorithm (QAOA) can be employed to solve this formulation on quantum hardware. In Qibo, this encoding is implemented through a Max-Cut class, which allows us to specify the graph weights directly and construct the corresponding QUBO Hamiltonian automatically." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fc0d16e4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.23|INFO|2026-02-24 17:40:10]: Using numpy backend on /CPU:0\n", + "INFO:qibo.config:Using numpy backend on /CPU:0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best bitstring: 11000 (count=4345)\n", + "Cut weight (true units): 4.800\n", + "Partition: S=[2, 3, 4], S'=[0, 1]\n" + ] + } + ], + "source": [ + "import itertools, numpy as np\n", + "from qiboopt.combinatorial.combinatorial import MaxCut\n", + "from matplotlib.lines import Line2D\n", + "from collections import defaultdict\n", + "\n", + "\n", + "# Build Max-Cut instance (you can also pass networkx Graph 'G' directly)\n", + "mc = MaxCut(dist_matrix, backend=None, normalize='maxdeg', mixer='xy')\n", + "\n", + "# qiboml training is exposed via qiboopt's QUBO wrapper train_QAOA API.\n", + "qubo = mc.to_qubo()\n", + "p = 2 # QAOA depth\n", + "nshots = 10000\n", + "\n", + "# qiboml optimizer settings\n", + "engine = \"qiboml\"\n", + "optimizer = \"adam\"\n", + "lr = 0.05\n", + "epochs = 100\n", + "differentiation = 'PSR'\n", + "\n", + "best_E, params, extra, circuit, freqs = qubo.train_QAOA(\n", + " p=p,\n", + " nshots=nshots,\n", + " engine=engine,\n", + " optimizer=optimizer,\n", + " lr=lr,\n", + " epochs=epochs,\n", + " differentiation=differentiation,\n", + ")\n", + "\n", + "# Pick most frequent bitstring and evaluate cut\n", + "best_bits = max(freqs, key=freqs.get)\n", + "best_cut_true = mc.cut_value(best_bits, use_scaled=False)\n", + "S, Sp = mc.partition_from_bits(best_bits)\n", + "\n", + "\n", + "# Print results\n", + "print(f\"Best bitstring: {best_bits} (count={freqs[best_bits]})\")\n", + "print(f\"Cut weight (true units): {best_cut_true:.3f}\")\n", + "print(f\"Partition: S={sorted(S)}, S'={sorted(Sp)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0715c1ef", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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UXn8L46IKE7YtYCIiIiIDIkrskREBmT9zc/IH0RE6YwrskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIGYm+ACKidKMNdGP0q3+IZL9GFNkSfRlERCExyBIRxdEHPvSb+MXPfpr8r3mRTV4rEVEyU3Rd15GkNGhwwQNFXKj8fyIi49B0HT6vGRlQoSp8DyMiY9F1QIREixlQk/QtjDWyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgZMAB9yv8TERnF1HctnW9hRGQwOpKfout8eyUiIiIi42FpAREREREZEoMsERERERkSgywRERERGRKDLBEREREZEoMsERERERkSgywRERERGRKDLBEREREZEoMsERERERkSgywRERERwYj+f5+jhaLiSImiAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualize partition on the same layout 'pos' and graph 'G'\n", + "\n", + "\n", + "# Ensure S and Sp exist (from earlier QAOA result), and G/pos are defined.\n", + "# If MaxCut was built from a matrix (S, Sp as ints) but G was relabeled (A..E),\n", + "# use the same 'mapping' to relabel the graph.\n", + "try:\n", + " if len(S) and isinstance(next(iter(S)), int):\n", + " sample_node = next(iter(G.nodes()))\n", + " if not isinstance(sample_node, int) and 'mapping' in globals():\n", + " S = {mapping[i] for i in S}\n", + " Sp = {mapping[i] for i in Sp}\n", + "except Exception:\n", + " pass\n", + "\n", + "# Base layout: reuse existing 'pos' if it exists; else compute one\n", + "if 'pos' not in globals():\n", + " pos = nx.spring_layout(G, seed=42, weight='weight')\n", + "\n", + "# Create horizontally separated layout\n", + "sep = 0.9 # horizontal separation amount\n", + "pos2 = {}\n", + "for n in G.nodes():\n", + " x, y = pos[n]\n", + " if n in S:\n", + " pos2[n] = (x - sep, y)\n", + " elif n in Sp:\n", + " pos2[n] = (x + sep, y)\n", + " else:\n", + " pos2[n] = (x, y) # fallback for any stray nodes\n", + "\n", + "# Partition edges into cut vs intra-group\n", + "def is_cut(u, v): \n", + " return (u in S and v in Sp) or (u in Sp and v in S)\n", + "\n", + "edges = list(G.edges())\n", + "weights = np.array([G[u][v].get('weight', 1.0) for u, v in edges], dtype=float)\n", + "wmin, wmax = (weights.min() if len(weights) else 1.0), (weights.max() if len(weights) else 1.0)\n", + "widths = 1.0 + 4.0 * (weights - wmin) / (wmax - wmin + 1e-9)\n", + "\n", + "mask_cut = np.array([is_cut(u, v) for u, v in edges], dtype=bool)\n", + "edges_cut = [e for e, m in zip(edges, mask_cut) if m]\n", + "edges_in = [e for e, m in zip(edges, mask_cut) if not m]\n", + "widths_cut = widths[mask_cut] if len(edges) else []\n", + "widths_in = widths[~mask_cut] if len(edges) else []\n", + "\n", + "# Background shading for groups\n", + "xs = [xy[0] for xy in pos2.values()]\n", + "xmin, xmax = min(xs) - 0.2, max(xs) + 0.2\n", + "xmid = 0.5 * (xmin + xmax)\n", + "\n", + "fig, ax = plt.subplots(figsize=(7, 6))\n", + "ax.axvspan(xmin, xmid, color='#f0fff4', alpha=0.55, zorder=0) # left group background\n", + "ax.axvspan(xmid, xmax, color='#f0f7ff', alpha=0.55, zorder=0) # right group background\n", + "\n", + "# Draw nodes with distinct shapes/colors\n", + "nodes_S = list(S)\n", + "nodes_Sp = list(Sp)\n", + "\n", + "nx.draw_networkx_nodes(G, pos2, nodelist=nodes_S, node_color='#06d6a0', node_shape='o',\n", + " node_size=900, edgecolors='black', linewidths=1.2, ax=ax)\n", + "nx.draw_networkx_nodes(G, pos2, nodelist=nodes_Sp, node_color='#118ab2', node_shape='s',\n", + " node_size=900, edgecolors='black', linewidths=1.2, ax=ax)\n", + "\n", + "nx.draw_networkx_labels(G, pos2, font_size=12, font_weight='bold', ax=ax)\n", + "\n", + "# Draw intra-group edges (light) and cut edges (highlighted)\n", + "nx.draw_networkx_edges(G, pos2, edgelist=edges_in, width=widths_in,\n", + " alpha=0.25, edge_color='#e76f51', ax=ax)\n", + "nx.draw_networkx_edges(G, pos2, edgelist=edges_cut, width=widths_cut,\n", + " alpha=0.95, edge_color='#9e9e9e', ax=ax)\n", + "\n", + "# Label edges\n", + "edge_labels = {(u, v): f\"{G[u][v].get('weight', 1.0):.1f}\" for (u, v) in edges}\n", + "nx.draw_networkx_edge_labels(G, pos2, edge_labels=edge_labels, font_size=9,\n", + " font_color='#444', bbox=dict(facecolor='white', edgecolor='none', alpha=0.6),\n", + " label_pos=0.6, ax=ax)\n", + "\n", + "# Legends\n", + "legend_nodes = [\n", + " Line2D([0], [0], marker='o', color='w', label=\"S\", markerfacecolor='#06d6a0',\n", + " markeredgecolor='black', markersize=12),\n", + " Line2D([0], [0], marker='s', color='w', label=\"S'\", markerfacecolor='#118ab2',\n", + " markeredgecolor='black', markersize=12),\n", + "]\n", + "legend_edges = [\n", + " Line2D([0], [0], color='#9e9e9e', lw=2.5, label='Cut edges'),\n", + " Line2D([0], [0], color='#e76f51', lw=2.5, alpha=0.5, label='Intra-group edges'),\n", + "]\n", + "ax.legend(handles=legend_nodes + legend_edges, loc='upper center', ncol=2, frameon=False)\n", + "\n", + "# Optional group labels\n", + "if nodes_S:\n", + " sx = np.mean([pos2[n][0] for n in nodes_S]); sy = max([pos2[n][1] for n in nodes_S]) + 0.15\n", + " ax.text(sx, sy, \"S\", fontsize=13, color='#048a63', ha='center', va='bottom')\n", + "if nodes_Sp:\n", + " tx = np.mean([pos2[n][0] for n in nodes_Sp]); ty = max([pos2[n][1] for n in nodes_Sp]) + 0.15\n", + " ax.text(tx, ty, \"S'\", fontsize=13, color='#0b6691', ha='center', va='bottom')\n", + "\n", + "ax.set_title(\"Max-Cut Partition: S vs S' (cut edges highlighted)\", fontsize=14)\n", + "ax.axis('off')\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "118a7007", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "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.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 4f718d5011fdac4669f0bc635d66353d2b894a80 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:50:12 +0800 Subject: [PATCH 05/28] updated qiboml dependencies --- pyproject.toml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e80414b..7607d22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,12 @@ packages = [{include="qiboopt", from="src"}] [tool.poetry.dependencies] python = ">=3.10,<3.14" -qibo = "^0.2" +qibo = ">=0.2,<0.3" +qiboml = {version = ">=0.1", optional = true} +torch = {version = "^2.7.0", optional = true} + +[tool.poetry.extras] +qiboml = ["qiboml", "torch"] [build-system] requires = ["poetry-core"] @@ -38,6 +43,13 @@ pytest-env = ">=0.8.1" pylint = "^3.3.5" matplotlib = "^3.9.2" +[tool.poetry.group.qiboml] +optional = true + +[tool.poetry.group.qiboml.dependencies] +qiboml = ">=0.1" +torch = "^2.7.0" + [tool.pytest.ini_options] testpaths = ['tests/'] filterwarnings = ['ignore::RuntimeWarning'] From 66d12c1cb136a11c75cb7d678a86d0613783f0dd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:15:58 +0000 Subject: [PATCH 06/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qiboopt/integrations/qiboml_adapter.py | 18 ++++++++++-------- src/qiboopt/opt_class/opt_class.py | 9 +++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 9a881e8..5d52987 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Dict, Optional +from typing import Any import numpy as np @@ -13,10 +13,10 @@ def _energy_shift(qubo) -> float: return float(constant) -def _get_differentiation_class(name: Optional[str]): - if name is None or name == 'torch': +def _get_differentiation_class(name: str | None): + if name is None or name == "torch": return None - from qiboml.operations.differentiation import Adjoint, Jax, PSR + from qiboml.operations.differentiation import PSR, Adjoint, Jax mapping = { "psr": PSR, @@ -38,16 +38,16 @@ def optimize_qaoa_with_qiboml( qubo, parameters, p: int, - nshots: Optional[int], + nshots: int | None, noise_model, custom_mixer, has_alphas: bool, optimizer: str, lr: float, epochs: int, - differentiation: Optional[str], + differentiation: str | None, backend, -) -> tuple[float, np.ndarray, Dict[str, Any]]: +) -> tuple[float, np.ndarray, dict[str, Any]]: """Optimize QAOA parameters using qiboml's pytorch interface.""" try: import torch @@ -101,7 +101,9 @@ def optimize_qaoa_with_qiboml( elif optimizer_name == "sgd": torch_optimizer = torch.optim.SGD(model.parameters(), lr=lr) else: - raise ValueError("Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'.") + raise ValueError( + "Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'." + ) losses = [] best = float("inf") diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index 910a3ee..badbd59 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -2,8 +2,8 @@ Optimisation classes """ -import itertools import inspect +import itertools from collections import defaultdict import numpy as np @@ -14,6 +14,7 @@ from qibo.models import QAOA from qibo.optimizers import optimize from qibo.symbols import Z + from qiboopt.integrations.qiboml_adapter import optimize_qaoa_with_qiboml @@ -524,11 +525,7 @@ def _split_qaoa_parameters(self, parameters, p, has_alphas=False): """Unpack a flat QAOA parameter vector in block format.""" gammas = parameters[:p] betas = parameters[p : 2 * p] - unpacked_alphas = ( - parameters[2 * p : 3 * p] - if has_alphas - else None - ) + unpacked_alphas = parameters[2 * p : 3 * p] if has_alphas else None return gammas, betas, unpacked_alphas def qaoa_circuit_from_parameters( From e71b9de127c01b10a62ac5faac08c7e4dc25190f Mon Sep 17 00:00:00 2001 From: J F Kong Date: Fri, 13 Mar 2026 23:18:01 +0800 Subject: [PATCH 07/28] issue warnings for unsupported optimisers --- doc/source/getting-started/quickstart.rst | 28 + poetry.lock | 673 +++++++++++++++++++-- src/qiboopt/__init__.py | 10 +- src/qiboopt/integrations/qiboml_adapter.py | 27 +- src/qiboopt/opt_class/opt_class.py | 14 +- tests/test_opt_class.py | 69 ++- tests/test_tutorial_max_cut_notebook.py | 31 +- 7 files changed, 795 insertions(+), 57 deletions(-) diff --git a/doc/source/getting-started/quickstart.rst b/doc/source/getting-started/quickstart.rst index aef588a..d91c690 100644 --- a/doc/source/getting-started/quickstart.rst +++ b/doc/source/getting-started/quickstart.rst @@ -59,3 +59,31 @@ The Conditional Variance at Risk (CVaR) can also be used as an alternative loss gammas = [0.1, 0.2] betas = [0.3, 0.4] output = qp.train_QAOA(gammas=gammas, betas=betas, regular_loss=False, cvar_delta=0.1) + +To use qiboml's pytorch training loop instead of the legacy optimizer, set ``engine="qiboml"``: + +.. code-block:: python + + from qiboopt.opt_class.opt_class import QUBO + gammas = [0.1, 0.2] + betas = [0.3, 0.4] + output = qp.train_QAOA( + gammas=gammas, + betas=betas, + engine="qiboml", + optimizer="adam", + lr=0.05, + epochs=100, + ) + +You can also run in exact (no-shot) mode by setting ``nshots=None`` (or ``nshots=0``): + +.. code-block:: python + + from qiboopt.opt_class.opt_class import QUBO + gammas = [0.1, 0.2] + betas = [0.3, 0.4] + output = qp.train_QAOA(gammas=gammas, betas=betas, nshots=None) + +In sampled mode (``nshots > 0``), the returned dictionary contains bitstring counts. +In exact mode (``nshots is None`` or ``nshots == 0``), it contains exact bitstring probabilities. diff --git a/poetry.lock b/poetry.lock index 666d872..b9029c5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -18,7 +18,7 @@ version = "1.16.2" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "alembic-1.16.2-py3-none-any.whl", hash = "sha256:5f42e9bd0afdbd1d5e3ad856c01754530367debdebf21ed6894e34af52b3bb03"}, {file = "alembic-1.16.2.tar.gz", hash = "sha256:e53c38ff88dadb92eb22f8b150708367db731d58ad7e9d417c9168ab516cbed8"}, @@ -39,7 +39,7 @@ version = "4.13.2" description = "ANTLR 4.13.2 runtime for Python 3" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "qiboml"] 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"}, @@ -338,7 +338,7 @@ 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"] +groups = ["main", "qiboml"] files = [ {file = "cma-3.4.0-py3-none-any.whl", hash = "sha256:4140e490cc4e68cf8c7b1114e079c0561c9b78b1bf9ec69362c20865636ae5ca"}, {file = "cma-3.4.0.tar.gz", hash = "sha256:a1ebd969b99871be3715d5a24b7bf54cf04ea94e80d6b8536d7147620dd10f6c"}, @@ -357,12 +357,12 @@ 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", "docs", "tests"] +groups = ["main", "docs", "qiboml", "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\"", docs = "sys_platform == \"win32\"", tests = "sys_platform == \"win32\""} +markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", docs = "sys_platform == \"win32\"", qiboml = "sys_platform == \"win32\" or platform_system == \"Windows\"", tests = "sys_platform == \"win32\""} [[package]] name = "colorlog" @@ -370,7 +370,7 @@ version = "6.9.0" description = "Add colours to the output of Python's logging module." optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff"}, {file = "colorlog-6.9.0.tar.gz", hash = "sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2"}, @@ -651,6 +651,60 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +[[package]] +name = "cuda-bindings" +version = "12.9.4" +description = "Python bindings for CUDA" +optional = false +python-versions = "*" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a"}, + {file = "cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5"}, + {file = "cuda_bindings-12.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:f69107389e6b9948969bfd0a20c4f571fd1aefcfb1d2e1b72cc8ba5ecb7918ab"}, + {file = "cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6a429dc6c13148ff1e27c44f40a3dd23203823e637b87fd0854205195988306"}, + {file = "cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9"}, + {file = "cuda_bindings-12.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:443b0875916879c2e4c3722941e25e42d5ab9bcbf34c9e83404fb100fa1f6913"}, + {file = "cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:694ba35023846625ef471257e6b5a4bc8af690f961d197d77d34b1d1db393f56"}, + {file = "cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8"}, + {file = "cuda_bindings-12.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:696ca75d249ddf287d01b9a698b8e2d8a05046495a9c051ca15659dc52d17615"}, + {file = "cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf8bfaedc238f3b115d957d1fd6562b7e8435ba57f6d0e2f87d0e7149ccb2da5"}, + {file = "cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f"}, + {file = "cuda_bindings-12.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:a2e82c8985948f953c2be51df45c3fe11c812a928fca525154fb9503190b3e64"}, + {file = "cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3adf4958dcf68ae7801a59b73fb00a8b37f8d0595060d66ceae111b1002de38d"}, + {file = "cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb"}, + {file = "cuda_bindings-12.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b32d8b685f0e66f5658bcf4601ef034e89fc2843582886f0a58784a4302da06c"}, + {file = "cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f53a7f453d4b2643d8663d036bafe29b5ba89eb904c133180f295df6dc151e5"}, + {file = "cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686"}, + {file = "cuda_bindings-12.9.4-cp314-cp314-win_amd64.whl", hash = "sha256:53a10c71fdbdb743e0268d07964e5a996dd00b4e43831cbfce9804515d97d575"}, + {file = "cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20f2699d61d724de3eb3f3369d57e2b245f93085cab44fd37c3bea036cea1a6f"}, + {file = "cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee"}, + {file = "cuda_bindings-12.9.4-cp314-cp314t-win_amd64.whl", hash = "sha256:53e11991a92ff6f26a0c8a98554cd5d6721c308a6b7bfb08bebac9201e039e43"}, + {file = "cuda_bindings-12.9.4-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:893ca68114b5b769c1d4c02583b91ed22691887c3ed513b59467d23540104db4"}, + {file = "cuda_bindings-12.9.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9866ceec83e39337d1a1d64837864c964ad902992478caa288a0bc1be95f21aa"}, + {file = "cuda_bindings-12.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:37744e721a18a514423e81863f52a4f7f46f5a6f9cccd569f2735f8067f4d8c2"}, +] + +[package.dependencies] +cuda-pathfinder = ">=1.1,<2.0" + +[package.extras] +all = ["nvidia-cuda-nvcc-cu12", "nvidia-cuda-nvrtc-cu12", "nvidia-cufile-cu12 ; sys_platform == \"linux\"", "nvidia-nvjitlink-cu12 (>=12.3)"] +test = ["cython (>=3.1,<3.2)", "numpy (>=1.21.1)", "pyglet (>=2.1.9)", "pytest (>=6.2.4)", "pytest-benchmark (>=3.4.1)", "setuptools (>=77.0.0)"] + +[[package]] +name = "cuda-pathfinder" +version = "1.3.3" +description = "Pathfinder for CUDA components" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1"}, +] + [[package]] name = "cycler" version = "0.12.1" @@ -741,6 +795,18 @@ files = [ [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +[[package]] +name = "filelock" +version = "3.20.3" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, + {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, +] + [[package]] name = "fonttools" version = "4.60.1" @@ -822,6 +888,46 @@ type1 = ["xattr ; sys_platform == \"darwin\""] unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] +[[package]] +name = "fsspec" +version = "2026.2.0" +description = "File-system specification" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437"}, + {file = "fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff (>=0.5)"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs (>2024.2.0)", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs (>2024.2.0)", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs (>2024.2.0)"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs (>2024.2.0)"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "backports-zstd ; python_version < \"3.14\"", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas (<3.0.0)", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard ; python_version < \"3.14\""] +tqdm = ["tqdm"] + [[package]] name = "furo" version = "2024.8.6" @@ -846,7 +952,7 @@ version = "3.2.3" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] 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.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be"}, @@ -964,13 +1070,86 @@ files = [ colors = ["colorama"] plugins = ["setuptools"] +[[package]] +name = "jax" +version = "0.4.38" +description = "Differentiate, compile, and transform Numpy code." +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "jax-0.4.38-py3-none-any.whl", hash = "sha256:78987306f7041ea8500d99df1a17c33ed92620c2268c4c3677fb24e06712be64"}, + {file = "jax-0.4.38.tar.gz", hash = "sha256:43bae65881628319e0a2148e8f81a202fbc2b8d048e35c7cb1df2416672fa4a8"}, +] + +[package.dependencies] +jaxlib = "0.4.38" +ml_dtypes = ">=0.4.0" +numpy = [ + {version = ">=1.24"}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +opt_einsum = "*" +scipy = [ + {version = ">=1.10"}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, +] + +[package.extras] +ci = ["jaxlib (==0.4.36)"] +cuda = ["jax-cuda12-plugin[with-cuda] (==0.4.38)", "jaxlib (==0.4.38)"] +cuda12 = ["jax-cuda12-plugin[with-cuda] (==0.4.38)", "jaxlib (==0.4.38)"] +cuda12-local = ["jax-cuda12-plugin (==0.4.38)", "jaxlib (==0.4.38)"] +cuda12-pip = ["jax-cuda12-plugin[with-cuda] (==0.4.38)", "jaxlib (==0.4.38)"] +k8s = ["kubernetes"] +minimum-jaxlib = ["jaxlib (==0.4.38)"] +tpu = ["jaxlib (==0.4.38)", "libtpu (==0.0.7)", "libtpu-nightly (==0.1.dev20241010+nightly.cleanup)", "requests"] + +[[package]] +name = "jaxlib" +version = "0.4.38" +description = "XLA library for JAX" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "jaxlib-0.4.38-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:55c19b9d3f33a6fc59f644aa5a21fba02639ccdd776cb4a9b5526625f57839ff"}, + {file = "jaxlib-0.4.38-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30b2f52cb50d74734af2f477c2533a7a583e3bb7b2c8acdeb361ee77d940577a"}, + {file = "jaxlib-0.4.38-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ee19c163a8fdf0839d4c18b88a5fbfb4e731ba7c437416d3e5483e570bb764e4"}, + {file = "jaxlib-0.4.38-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:61aeccb9a27c67fdb8450f6357240019cd4511cb9d62a44e4764756d384853ad"}, + {file = "jaxlib-0.4.38-cp310-cp310-win_amd64.whl", hash = "sha256:d6ab745a89d0fb737a36fe1d8b86659e3fffe6ee8303b20651b26193d5edc0ef"}, + {file = "jaxlib-0.4.38-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:b67fdeabd6dfed08b7768f3bdffb521160085f8305669bd197beef61d08de08b"}, + {file = "jaxlib-0.4.38-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb0eaae7369157afecbead50aaf29e73ffddfa77a2335d721bd9794f3c510e4"}, + {file = "jaxlib-0.4.38-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:43db58c4c427627296366a56c10318e1f00f503690e17f94bb4344293e1995e0"}, + {file = "jaxlib-0.4.38-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:2751ff7037d6a997d0be0e77cc4be381c5a9f9bb8b314edb755c13a6fd969f45"}, + {file = "jaxlib-0.4.38-cp311-cp311-win_amd64.whl", hash = "sha256:35226968fc9de6873d1571670eac4117f5ed80e955f7a1775204d1044abe16c6"}, + {file = "jaxlib-0.4.38-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:3fefea985f0415816f3bbafd3f03a437050275ef9bac9a72c1314e1644ac57c1"}, + {file = "jaxlib-0.4.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f33bcafe32c97a562ecf6894d7c41674c80c0acdedfa5423d49af51147149874"}, + {file = "jaxlib-0.4.38-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:496f45b0e001a2341309cd0c74af0b670537dced79c168cb230cfcc773f0aa86"}, + {file = "jaxlib-0.4.38-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:dad6c0a96567c06d083c0469fec40f201210b099365bd698be31a6d2ec88fd59"}, + {file = "jaxlib-0.4.38-cp312-cp312-win_amd64.whl", hash = "sha256:966cdec36cfa978f5b4582bcb4147fe511725b94c1a752dac3a5f52ce46b6fa3"}, + {file = "jaxlib-0.4.38-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:41e55ae5818a882e5789e848f6f16687ac132bcfbb5a5fa114a5d18b78d05f2d"}, + {file = "jaxlib-0.4.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6fe326b8af366387dd47ccf312583b2b17fed12712c9b74a648b18a13cbdbabf"}, + {file = "jaxlib-0.4.38-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:248cca3771ebf24b070f49701364ceada33e6139445b06c782cca5ac5ad92bf4"}, + {file = "jaxlib-0.4.38-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:2ce77ba8cda9259a4bca97afc1c722e4291a6c463a63f8d372c6edc85117d625"}, + {file = "jaxlib-0.4.38-cp313-cp313-win_amd64.whl", hash = "sha256:4103db0b3a38a5dc132741237453c24d8547290a22079ba1b577d6c88c95300a"}, +] + +[package.dependencies] +ml-dtypes = ">=0.2.0" +numpy = ">=1.24" +scipy = [ + {version = ">=1.10"}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, +] + [[package]] name = "jinja2" version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["docs"] +groups = ["main", "docs", "qiboml"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -988,7 +1167,7 @@ version = "1.5.1" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a"}, {file = "joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444"}, @@ -1216,7 +1395,7 @@ version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, @@ -1236,7 +1415,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main", "docs"] +groups = ["main", "docs", "qiboml"] 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"}, @@ -1407,13 +1586,73 @@ files = [ [package.dependencies] typing-extensions = {version = "*", markers = "python_version < \"3.11\""} +[[package]] +name = "ml-dtypes" +version = "0.5.4" +description = "ml_dtypes is a stand-alone implementation of several NumPy dtype extensions used in machine learning." +optional = false +python-versions = ">=3.9" +groups = ["main", "qiboml"] +files = [ + {file = "ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d81fdb088defa30eb37bf390bb7dde35d3a83ec112ac8e33d75ab28cc29dd8b0"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88c982aac7cb1cbe8cbb4e7f253072b1df872701fcaf48d84ffbb433b6568f24"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9b61c19040397970d18d7737375cffd83b1f36a11dd4ad19f83a016f736c3ef"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:3d277bf3637f2a62176f4575512e9ff9ef51d00e39626d9fe4a161992f355af2"}, + {file = "ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=2.1.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.26.0", markers = "python_version == \"3.12\""}, + {version = ">=1.23.3", markers = "python_version >= \"3.11\""}, +] + +[package.extras] +dev = ["absl-py", "pyink", "pylint (>=2.6.0)", "pytest", "pytest-xdist"] + [[package]] name = "mpmath" version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -1533,7 +1772,7 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version == \"3.10\"" files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, @@ -1554,7 +1793,7 @@ version = "3.5" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.11" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version >= \"3.11\"" files = [ {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, @@ -1576,7 +1815,7 @@ version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main", "tests"] +groups = ["main", "qiboml", "tests"] markers = "python_version == \"3.10\"" files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, @@ -1642,7 +1881,7 @@ version = "2.3.1" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.11" -groups = ["main", "tests"] +groups = ["main", "qiboml", "tests"] markers = "python_version >= \"3.11\"" files = [ {file = "numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070"}, @@ -1698,13 +1937,234 @@ files = [ {file = "numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b"}, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +description = "CUBLAS native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0"}, + {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142"}, + {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +description = "CUDA profiling tools runtime libs." +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed"}, + {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182"}, + {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +description = "NVRTC native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994"}, + {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8"}, + {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +description = "CUDA Runtime native Libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d"}, + {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90"}, + {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +description = "cuDNN runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8"}, + {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8"}, + {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +description = "CUFFT native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a"}, + {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74"}, + {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +description = "cuFile GPUDirect libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc"}, + {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a"}, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +description = "CURAND native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd"}, + {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9"}, + {file = "nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +description = "CUDA solver native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0"}, + {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450"}, + {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" +nvidia-cusparse-cu12 = "*" +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +description = "CUSPARSE native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc"}, + {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b"}, + {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +description = "NVIDIA cuSPARSELt" +optional = false +python-versions = "*" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5"}, + {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623"}, + {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075"}, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +description = "NVIDIA Collective Communication Library (NCCL) Runtime" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a"}, + {file = "nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +description = "Nvidia JIT LTO Library" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88"}, + {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7"}, + {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f"}, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +description = "NVSHMEM creates a global address space that provides efficient and scalable communication for NVIDIA GPU clusters." +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15"}, + {file = "nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +description = "NVIDIA Tools Extension" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615"}, + {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f"}, + {file = "nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e"}, +] + [[package]] name = "openqasm3" version = "1.0.1" description = "Reference OpenQASM AST in Python" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "openqasm3-1.0.1-py3-none-any.whl", hash = "sha256:0d3a1ebe3465e3ea619bcaa369858bba8944cbb0c49604b24f94662d3ec41d41"}, {file = "openqasm3-1.0.1.tar.gz", hash = "sha256:c589dc05d4ced50ca24167d14e0f2c916e717499ba0442e0ff2a3030ef312d0a"}, @@ -1718,13 +2178,25 @@ all = ["antlr4_python3_runtime (>=4.7,<4.14)", "importlib_metadata ; python_vers parser = ["antlr4_python3_runtime (>=4.7,<4.14)", "importlib_metadata ; python_version < \"3.10\""] tests = ["pytest (>=6.0)", "pyyaml"] +[[package]] +name = "opt-einsum" +version = "3.4.0" +description = "Path optimization of einsum functions." +optional = false +python-versions = ">=3.8" +groups = ["main", "qiboml"] +files = [ + {file = "opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd"}, + {file = "opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac"}, +] + [[package]] name = "optuna" version = "4.4.0" description = "A hyperparameter optimization framework" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "optuna-4.4.0-py3-none-any.whl", hash = "sha256:fad8d9c5d5af993ae1280d6ce140aecc031c514a44c3b639d8c8658a8b7920ea"}, {file = "optuna-4.4.0.tar.gz", hash = "sha256:a9029f6a92a1d6c8494a94e45abd8057823b535c2570819072dbcdc06f1c1da4"}, @@ -1752,7 +2224,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "docs", "tests"] +groups = ["main", "docs", "qiboml", "tests"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -2131,7 +2603,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "docs"] +groups = ["main", "docs", "qiboml"] 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"}, @@ -2282,14 +2754,14 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qibo" -version = "0.2.19" +version = "0.2.23" description = "A framework for quantum computing with hardware acceleration." optional = false python-versions = "<3.14,>=3.10" -groups = ["main"] +groups = ["main", "qiboml"] files = [ - {file = "qibo-0.2.19-py3-none-any.whl", hash = "sha256:7d17c77e89cd92be710c8a2e43c9daf24b9131669fef9b56560ec71b2b6036ec"}, - {file = "qibo-0.2.19.tar.gz", hash = "sha256:3f35275024d4877ad19785f479d5f04e859ee02c9eef22aa94205c0d1e8f8fe5"}, + {file = "qibo-0.2.23-py3-none-any.whl", hash = "sha256:1bd93a91ad4f3c23e8cb1db9231665e24921a044bd2f752574af0e36b6c7d860"}, + {file = "qibo-0.2.23.tar.gz", hash = "sha256:7727e583d56589cfd48ef2cfbbd029e5356cf32c4645e6b7e0f1d70f07ccc758"}, ] [package.dependencies] @@ -2304,8 +2776,32 @@ sympy = ">=1.13.1,<2.0.0" tabulate = ">=0.9.0,<0.10.0" [package.extras] +cudaq = ["qbraid[cudaq,qir] (>=0.10.0,<0.11.0)", "qbraid[cudaq] (>=0.10.0,<0.11.0)"] +qir = ["qbraid[cudaq,qir] (>=0.10.0,<0.11.0)", "qbraid[qir] (>=0.10.0,<0.11.0)"] qulacs = ["qulacs (>=0.6.4,<0.7.0) ; python_version < \"3.13\""] +[[package]] +name = "qiboml" +version = "0.1.0" +description = "Quantum Machine Learning using Qibo" +optional = false +python-versions = "<3.14,>=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "qiboml-0.1.0-py3-none-any.whl", hash = "sha256:22f86b6b72b9013c5f6dd0e08328b880cfdc866dc1fe930542a3a76d46159392"}, + {file = "qiboml-0.1.0.tar.gz", hash = "sha256:c2984e64ff0a06f0e268066b5bff826eb4ca5398143abc8669f1804bb0961be5"}, +] + +[package.dependencies] +jax = ">=0.4.25,<0.5.0" +jaxlib = ">=0.4.25,<0.5.0" +numpy = ">=2.0.0,<3.0.0" +qibo = ">=0.2.21,<0.3.0" + +[package.extras] +keras = ["keras (>=3.11.0,<4.0.0)", "tensorflow (>=2.16.1,<3.0.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\""] +torch = ["torch (>=2.7.0,<3.0.0)"] + [[package]] name = "recommonmark" version = "0.7.1" @@ -2495,7 +2991,7 @@ version = "1.15.3" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version == \"3.10\"" files = [ {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, @@ -2560,7 +3056,7 @@ version = "1.16.0" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.11" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version >= \"3.11\"" files = [ {file = "scipy-1.16.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:deec06d831b8f6b5fb0b652433be6a09db29e996368ce5911faf673e78d20085"}, @@ -2616,11 +3112,12 @@ version = "80.9.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" -groups = ["docs"] +groups = ["main", "docs", "qiboml"] files = [ {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] +markers = {main = "python_version >= \"3.12\"", qiboml = "python_version >= \"3.12\""} [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] @@ -2883,7 +3380,7 @@ version = "2.0.41" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, @@ -2979,7 +3476,7 @@ version = "1.14.0" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"}, {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"}, @@ -2997,7 +3494,7 @@ version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -3031,7 +3528,7 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "docs", "tests"] +groups = ["main", "docs", "qiboml", "tests"] markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, @@ -3080,6 +3577,79 @@ files = [ {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, ] +[[package]] +name = "torch" +version = "2.10.0" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313"}, + {file = "torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f"}, + {file = "torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574"}, + {file = "torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e"}, + {file = "torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d"}, + {file = "torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444"}, + {file = "torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb"}, + {file = "torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d"}, + {file = "torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4"}, + {file = "torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763"}, + {file = "torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd"}, + {file = "torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b"}, + {file = "torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf"}, + {file = "torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb"}, + {file = "torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547"}, + {file = "torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294"}, + {file = "torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b"}, + {file = "torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738"}, + {file = "torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57"}, + {file = "torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382"}, + {file = "torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8"}, + {file = "torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f"}, + {file = "torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8"}, + {file = "torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f"}, + {file = "torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a"}, + {file = "torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60"}, + {file = "torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5"}, + {file = "torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c"}, + {file = "torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28"}, + {file = "torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63"}, + {file = "torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6"}, + {file = "torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185"}, +] + +[package.dependencies] +cuda-bindings = {version = "12.9.4", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +filelock = "*" +fsspec = ">=0.8.5" +jinja2 = "*" +networkx = ">=2.5.1" +nvidia-cublas-cu12 = {version = "12.8.4.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cudnn-cu12 = {version = "9.10.2.21", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufft-cu12 = {version = "11.3.3.83", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufile-cu12 = {version = "1.13.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.9.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.7.3.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.5.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparselt-cu12 = {version = "0.7.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.27.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvjitlink-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvshmem-cu12 = {version = "3.4.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +setuptools = {version = "*", markers = "python_version >= \"3.12\""} +sympy = ">=1.13.3" +triton = {version = "3.6.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +typing-extensions = ">=4.10.0" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] +optree = ["optree (>=0.13.0)"] +pyyaml = ["pyyaml"] + [[package]] name = "tornado" version = "6.5.1" @@ -3108,7 +3678,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -3140,13 +3710,43 @@ 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 = "triton" +version = "3.6.0" +description = "A language and compiler for custom Deep Learning operations" +optional = false +python-versions = "<3.15,>=3.10" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781"}, + {file = "triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea"}, + {file = "triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651"}, + {file = "triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3"}, + {file = "triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4"}, + {file = "triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca"}, + {file = "triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd"}, + {file = "triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9"}, + {file = "triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6"}, + {file = "triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f"}, + {file = "triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43"}, + {file = "triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803"}, + {file = "triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d"}, + {file = "triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7"}, +] + +[package.extras] +build = ["cmake (>=3.20,<4.0)", "lit"] +tests = ["autopep8", "isort", "llnl-hatchet", "numpy", "pytest", "pytest-forked", "pytest-xdist", "scipy (>=1.7.1)"] +tutorials = ["matplotlib", "pandas", "tabulate"] + [[package]] name = "typing-extensions" version = "4.14.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "docs", "tests"] +groups = ["main", "docs", "qiboml", "tests"] files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, @@ -3183,7 +3783,10 @@ files = [ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] +[extras] +qiboml = ["qiboml", "torch"] + [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "a1a706611bdeec2b916d01171ce9a17d608571d1d7ace7606a77f24df764eed8" +content-hash = "60ecd11b44061d105affe3ba56670e3486214e5ef7fe8eb575b92ef38c3b8eb7" diff --git a/src/qiboopt/__init__.py b/src/qiboopt/__init__.py index c2cdb64..1d4a119 100644 --- a/src/qiboopt/__init__.py +++ b/src/qiboopt/__init__.py @@ -1,5 +1,7 @@ -import importlib.metadata as im +try: + import importlib.metadata as im + __version__ = im.version(__package__) +except Exception: + __version__ = "0.0.1" -__version__ = im.version(__package__) - -from qiboopt import combinatorial, opt_class +from qiboopt import combinatorial, opt_class, continuous_bandits, integrations diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 5d52987..6b2ad18 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -69,7 +69,8 @@ def optimize_qaoa_with_qiboml( # Reuse qiboopt's own QAOA-object construction path for the Hamiltonian. hamiltonian = qubo.qubo_to_qaoa_object().hamiltonian if not hasattr(hamiltonian, "expectation_from_circuit"): - # qiboml 0.1.0 calls this method on SymbolicHamiltonian. + # TODO: remove once minimum required qiboml version is > 0.1.0. + # qiboml 0.1.0 uses `expectation_from_circuit`; older builds expose only `expectation`. hamiltonian.expectation_from_circuit = hamiltonian.expectation circuit_builder = qubo.make_qaoa_circuit_callable( p=p, @@ -95,15 +96,21 @@ def optimize_qaoa_with_qiboml( ) model = model.to(dtype=torch.float64) - optimizer_name = optimizer.lower() - if optimizer_name == "adam": - torch_optimizer = torch.optim.Adam(model.parameters(), lr=lr) - elif optimizer_name == "sgd": - torch_optimizer = torch.optim.SGD(model.parameters(), lr=lr) + if isinstance(optimizer, str): + _OPT_MAP = { + "adam": torch.optim.Adam, + "sgd": torch.optim.SGD, + } + opt_cls = _OPT_MAP.get(optimizer.lower()) + if opt_cls is None: + raise ValueError( + f"Unknown optimizer string '{optimizer}'. " + f"Pass a torch.optim.Optimizer subclass directly, or use one of: {list(_OPT_MAP)}." + ) else: - raise ValueError( - "Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'." - ) + opt_cls = optimizer # user supplied a class directly + + torch_optimizer = opt_cls(model.parameters(), lr=lr) losses = [] best = float("inf") @@ -123,7 +130,7 @@ def optimize_qaoa_with_qiboml( extra = { "engine": "qiboml", - "optimizer": optimizer_name, + "optimizer": getattr(opt_cls, "__name__", str(opt_cls)), "learning_rate": lr, "epochs": epochs, "loss_history": losses, diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index badbd59..6ed7aff 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -704,10 +704,16 @@ def train_QAOA( differentiation=differentiation, backend=backend, ) - else: - if engine == "qiboml" and not regular_loss: - # qiboml path currently supports expectation-value optimization only. - engine = "legacy" + if engine == "qiboml" and not regular_loss: + import warnings + + warnings.warn( + "engine='qiboml' does not yet support CVaR loss (regular_loss=False). " + "Falling back to engine='legacy'.", + UserWarning, + stacklevel=2, + ) + engine = "legacy" if not regular_loss and not (0 < cvar_delta <= 1): raise_error( ValueError, diff --git a/tests/test_opt_class.py b/tests/test_opt_class.py index 4c8bac9..a301722 100644 --- a/tests/test_opt_class.py +++ b/tests/test_opt_class.py @@ -1,4 +1,5 @@ import itertools +import importlib.util import numpy as np import pytest @@ -16,6 +17,17 @@ ) +def _qiboml_available(): + if importlib.util.find_spec("qiboml") is None: + return False + if importlib.util.find_spec("torch") is None: + return False + return True + + +ENGINES = ["legacy"] + (["qiboml"] if _qiboml_available() else []) + + def test_initialization(): """Test initialization of the QUBO class""" Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} @@ -235,6 +247,17 @@ def test_qubo_to_qaoa_circuit(gammas, betas, alphas): assert circuit.nqubits == qubo.n +def test_qubo_to_qaoa_circuit_without_measurements(): + qubo = QUBO(0, {0: 1, 1: -1}, {(0, 1): 0.5}) + circuit = qubo.qubo_to_qaoa_circuit( + gammas=[0.1, 0.2], + betas=[0.3, 0.4], + include_measurements=False, + ) + assert isinstance(circuit, Circuit) + assert len(circuit.measurements) == 0 + + @pytest.mark.parametrize( "gammas, betas", [ @@ -295,7 +318,8 @@ def test_qubo_to_qaoa_svp_mixer(gammas, betas): ], ) @pytest.mark.parametrize("noise_model", [(True, False)]) -def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model): +@pytest.mark.parametrize("engine", ENGINES) +def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model, engine): h = {0: 1, 1: -1} J = {(0, 1): 0.5} qubo = QUBO(0, h, J) @@ -312,6 +336,8 @@ def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model): regular_loss=reg_loss, cvar_delta=cvar_delta, noise_model=noise_model, + engine=engine, + epochs=5, ) assert isinstance(result[0], float) assert isinstance(result[1], np.ndarray) @@ -319,7 +345,8 @@ def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model): assert isinstance(result[4], dict) -def test_train_QAOA_convex_qubo(): +@pytest.mark.parametrize("engine", ENGINES) +def test_train_QAOA_convex_qubo(engine): Qdict = {(0, 0): 2.0, (1, 1): 2.0} @@ -331,7 +358,7 @@ def test_train_QAOA_convex_qubo(): # Train QAOA with 100 iterations. Should be enough to find the minimum. best, params, extra, circuit, freqs = qp.train_QAOA( - gammas, betas, nshots=1000, maxiter=100 + gammas, betas, nshots=1000, maxiter=100, engine=engine, epochs=50 ) # Convert result keys to bitstrings most_freq = max(freqs, key=freqs.get) @@ -360,6 +387,42 @@ def test_train_QAOA_edge_cases(): assert isinstance(result[4], dict) +@pytest.mark.parametrize("nshots", [None, 0]) +@pytest.mark.parametrize("regular_loss", [True, False]) +def test_train_qaoa_exact_mode_returns_probabilities(nshots, regular_loss): + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + kwargs = {} + if not regular_loss: + kwargs["cvar_delta"] = 0.5 + best, params, extra, circuit, stats = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=nshots, + regular_loss=regular_loss, + maxiter=5, + engine="legacy", + **kwargs, + ) + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(circuit, Circuit) + assert isinstance(stats, dict) + assert all(isinstance(value, float) for value in stats.values()) + assert np.isclose(sum(stats.values()), 1.0) + + +def test_train_qaoa_cvar_delta_validation(): + qp = QUBO(0, {(0, 0): 1.0}) + with pytest.raises(ValueError, match="cvar_delta must satisfy 0 < cvar_delta <= 1"): + qp.train_QAOA( + gammas=[0.1], + betas=[0.2], + regular_loss=False, + cvar_delta=0.0, + ) + + def create_svp_mixer(name_to_index, beta): """ Helper function to create a mixer circuit diff --git a/tests/test_tutorial_max_cut_notebook.py b/tests/test_tutorial_max_cut_notebook.py index e34d90b..e8fa74f 100644 --- a/tests/test_tutorial_max_cut_notebook.py +++ b/tests/test_tutorial_max_cut_notebook.py @@ -1,6 +1,7 @@ import json import os import sys +import importlib.util from pathlib import Path import pytest @@ -43,10 +44,29 @@ def _should_skip(code: str) -> bool: return False +def _requires_qiboml(cells: list[str]) -> bool: + markers = ( + 'engine="qiboml"', + "engine='qiboml'", + 'engine = "qiboml"', + "engine = 'qiboml'", + ) + return any(any(marker in code for marker in markers) for code in cells) + + +def _has_working_qiboml_runtime() -> bool: + if importlib.util.find_spec("torch") is None: + return False + if importlib.util.find_spec("qiboml") is None: + return False + return True + + @pytest.mark.parametrize( "notebook_rel", [ Path("tutorial") / "Max-Cut.ipynb", + Path("tutorial") / "Max-Cut-qiboml.ipynb", ], ) def test_execute_notebook_for_coverage(notebook_rel: Path): @@ -63,11 +83,20 @@ def test_execute_notebook_for_coverage(notebook_rel: Path): # Use non-interactive matplotlib backend for headless test envs os.environ.setdefault("MPLBACKEND", "Agg") + cells = _load_notebook_cells(nb_path) + + # qiboml tutorial variants should skip cleanly when optional deps are unavailable. + if _requires_qiboml(cells): + if not _has_working_qiboml_runtime(): + pytest.skip( + "qiboml notebook requires a working qiboml+torch runtime compatible with this qibo install." + ) + # Minimal execution namespace; mimic a fresh notebook kernel ns: dict = {"__name__": "__main__"} # Execute cells, skipping heavy visualization/magic cells - for code in _load_notebook_cells(nb_path): + for code in cells: if _should_skip(code): continue # Some notebooks assume cwd at repo root; enforce it From 21bc184f0f969ff5b506d7c22f8f1b1cd1e5914d Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 14:39:44 +0800 Subject: [PATCH 08/28] tweak train_QAOA to also allow for non-shot based calculations --- src/qiboopt/opt_class/opt_class.py | 55 +++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index 35e3645..afb77fb 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -523,7 +523,8 @@ def train_QAOA( betas (List[float], optional): parameters for X mixers. alphas (List[float], optional): parameters for Y mixers for XQAOA. Defaults to None. p (int, optional): number of layers. - nshots (int, optional): number of shots + nshots (int, optional): Number of shots for sampled execution. + If ``None`` or ``0``, uses exact (no-shot) execution. regular_loss (Bool, optional): If False, Conditional Variance at Risk (CVaR) is used as cost function. Defaults to True, where expected value is used as cost function. maxiter (int, optional): Maximum number of iterations used in the minimiser. Defaults to 10. @@ -544,7 +545,9 @@ def train_QAOA( - params (List[float]): Optimised QAOA parameters. - extra (dict): Additional metadata (e.g., convergence info). - circuit (:class:`qibo.models.Circuit`): Final circuit used for evaluation. - - frequencies (dict): Bitstring outcome frequencies from measurement. + - frequencies (dict): Bitstring outcome statistics. + In sampled mode (``nshots`` > 0), values are counts. + In exact mode (``nshots`` is ``None`` or ``0``), values are probabilities. Example: .. testcode:: @@ -560,6 +563,7 @@ def train_QAOA( """ backend = _check_backend(backend) + use_exact = (nshots is None) or (nshots == 0) if p is None and gammas is None: raise_error( @@ -614,7 +618,11 @@ def myloss(parameters): if noise_model is not None: circuit = noise_model.apply(circuit) - result = circuit(None, nshots) + if use_exact: + hamiltonian = self.qubo_to_qaoa_object().hamiltonian + return hamiltonian.expectation(circuit, nshots=None) + + result = backend.execute_circuit(circuit, nshots=nshots) result_counter = result.frequencies(binary=True) energy_dict = defaultdict(int) for key in result_counter: @@ -650,20 +658,28 @@ def myloss(parameters, delta=cvar_delta): ) if noise_model is not None: circuit = noise_model.apply(circuit) - result = backend.execute_circuit(circuit, nshots=nshots) - result_counter = result.frequencies(binary=True) + if use_exact: + result = backend.execute_circuit(circuit) + result_probs = _probability_dict_from_state(result) + energy_probs = defaultdict(float) + for key, probability in result_probs.items(): + x = [int(sub_key) for sub_key in key] + energy_probs[self.evaluate_f(x)] += probability + else: + result = backend.execute_circuit(circuit, nshots=nshots) + result_counter = result.frequencies(binary=True) - energy_dict = defaultdict(int) - for key in result_counter: - # key is the binary string, value is the frequency - x = [int(sub_key) for sub_key in key] - energy_dict[self.evaluate_f(x)] += result_counter[key] + energy_dict = defaultdict(int) + for key in result_counter: + # key is the binary string, value is the frequency + x = [int(sub_key) for sub_key in key] + energy_dict[self.evaluate_f(x)] += result_counter[key] - # Normalize frequencies to probabilities - total_counts = sum(energy_dict.values()) - energy_probs = { - key: value / total_counts for key, value in energy_dict.items() - } + # Normalize frequencies to probabilities + total_counts = sum(energy_dict.values()) + energy_probs = { + key: value / total_counts for key, value in energy_dict.items() + } # Sort energies and compute cumulative probability sorted_energies = sorted( @@ -708,7 +724,12 @@ def myloss(parameters, delta=cvar_delta): if noise_model is not None: circuit = noise_model.apply(circuit) - result = backend.execute_circuit(circuit, nshots=nshots) + if use_exact: + result = backend.execute_circuit(circuit) + statistics = _probability_dict_from_state(result) + else: + result = backend.execute_circuit(circuit, nshots=nshots) + statistics = result.frequencies(binary=True) if noise_model is not None: return ( @@ -719,7 +740,7 @@ def myloss(parameters, delta=cvar_delta): result.frequencies(binary=True), original_circuit, ) - return best, params, extra, circuit, result.frequencies(binary=True) + return best, params, extra, circuit, statistics def qubo_to_qaoa_object(self, params: list = None): """ From 02a8d435b0d664969ac9aeaeef19e3d10195aad2 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:31:15 +0800 Subject: [PATCH 09/28] added adapter to use qiboml in QAOA optimisation --- src/qiboopt/integrations/qiboml_adapter.py | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/qiboopt/integrations/qiboml_adapter.py diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py new file mode 100644 index 0000000..9a881e8 --- /dev/null +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -0,0 +1,129 @@ +"""qiboml integration helpers for QAOA training.""" + +from __future__ import annotations + +from typing import Any, Dict, Optional + +import numpy as np + + +def _energy_shift(qubo) -> float: + """Constant shift between Ising expectation and QUBO objective value.""" + _h, _J, constant = qubo.qubo_to_ising() + return float(constant) + + +def _get_differentiation_class(name: Optional[str]): + if name is None or name == 'torch': + return None + from qiboml.operations.differentiation import Adjoint, Jax, PSR + + mapping = { + "psr": PSR, + "jax": Jax, + "adjoint": Adjoint, + "torch": None, + } + diff = mapping.get(name.lower()) + if diff is None: + raise ValueError( + "Unknown qiboml differentiation method. " + "Supported values are: None, 'psr', 'jax', 'adjoint', 'torch'." + ) + return diff + + +def optimize_qaoa_with_qiboml( + *, + qubo, + parameters, + p: int, + nshots: Optional[int], + noise_model, + custom_mixer, + has_alphas: bool, + optimizer: str, + lr: float, + epochs: int, + differentiation: Optional[str], + backend, +) -> tuple[float, np.ndarray, Dict[str, Any]]: + """Optimize QAOA parameters using qiboml's pytorch interface.""" + try: + import torch + except ImportError as exc: + raise ImportError( + "engine='qiboml' requires torch. Install optional dependencies, " + "for example with `poetry install --with qiboml`." + ) from exc + + try: + from qiboml.interfaces.pytorch import QuantumModel + from qiboml.models.decoding import Expectation + except ImportError as exc: + raise ImportError( + "engine='qiboml' requires qiboml. Install optional dependencies, " + "for example with `poetry install --with qiboml`." + ) from exc + + # Reuse qiboopt's own QAOA-object construction path for the Hamiltonian. + hamiltonian = qubo.qubo_to_qaoa_object().hamiltonian + if not hasattr(hamiltonian, "expectation_from_circuit"): + # qiboml 0.1.0 calls this method on SymbolicHamiltonian. + hamiltonian.expectation_from_circuit = hamiltonian.expectation + circuit_builder = qubo.make_qaoa_circuit_callable( + p=p, + custom_mixer=custom_mixer, + has_alphas=has_alphas, + include_measurements=False, + ) + decoder = Expectation( + nqubits=qubo.n, + observable=hamiltonian, + nshots=nshots, + noise_model=noise_model, + backend=backend, + ) + energy_shift = _energy_shift(qubo) + + diff_class = _get_differentiation_class(differentiation) + model = QuantumModel( + circuit_structure=[circuit_builder], + decoding=decoder, + parameters_initialization=np.asarray(parameters, dtype=np.float64), + differentiation=diff_class, + ) + model = model.to(dtype=torch.float64) + + optimizer_name = optimizer.lower() + if optimizer_name == "adam": + torch_optimizer = torch.optim.Adam(model.parameters(), lr=lr) + elif optimizer_name == "sgd": + torch_optimizer = torch.optim.SGD(model.parameters(), lr=lr) + else: + raise ValueError("Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'.") + + losses = [] + best = float("inf") + best_params = np.asarray(parameters, dtype=np.float64) + for _ in range(epochs): + torch_optimizer.zero_grad() + loss = model() + if loss.ndim > 0: + loss = loss.reshape(-1)[0] + loss.backward() + torch_optimizer.step() + loss_value = float(loss.detach().cpu().item()) + energy_shift + losses.append(loss_value) + if loss_value < best: + best = loss_value + best_params = model.circuit_parameters.detach().cpu().numpy().copy() + + extra = { + "engine": "qiboml", + "optimizer": optimizer_name, + "learning_rate": lr, + "epochs": epochs, + "loss_history": losses, + } + return best, best_params, extra From 640d30b62ed436e372a42e3591c117932f9a8fbe Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:44:03 +0800 Subject: [PATCH 10/28] modified QUBO to allow for qiboml optimisation --- src/qiboopt/opt_class/opt_class.py | 210 ++++++++++++++++++++++------- 1 file changed, 164 insertions(+), 46 deletions(-) diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index afb77fb..910a3ee 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -3,6 +3,7 @@ """ import itertools +import inspect from collections import defaultdict import numpy as np @@ -13,6 +14,7 @@ from qibo.models import QAOA from qibo.optimizers import optimize from qibo.symbols import Z +from qiboopt.integrations.qiboml_adapter import optimize_qaoa_with_qiboml class QUBO: @@ -192,7 +194,9 @@ def _default_mixer(self, circuit, beta, alpha=None): if alpha: circuit.add(gates.RY(i, 2 * alpha)) - def _build(self, gammas, betas, alphas=None, custom_mixer=None): + def _build( + self, gammas, betas, alphas=None, custom_mixer=None, include_measurements=True + ): """ Constructs the full QAOA circuit for the Ising model with p layers. custom_mixer (List[:class:`qibo.models.Circuit`]): An optional function that takes as input custom mixers. @@ -236,7 +240,8 @@ def _build(self, gammas, betas, alphas=None, custom_mixer=None): else: self._default_mixer(circuit, betas[layer]) - circuit.add(gates.M(i) for i in range(self.n)) + if include_measurements: + circuit.add(gates.M(i) for i in range(self.n)) return circuit @@ -471,7 +476,14 @@ def canonical_q(self): self.Qdict = Qdict return self.Qdict - def qubo_to_qaoa_circuit(self, gammas, betas, alphas=None, custom_mixer=None): + def qubo_to_qaoa_circuit( + self, + gammas, + betas, + alphas=None, + custom_mixer=None, + include_measurements=True, + ): """ Constructs a QAOA or XQAOA circuit for the given QUBO problem. @@ -483,21 +495,87 @@ def qubo_to_qaoa_circuit(self, gammas, betas, alphas=None, custom_mixer=None): If len(custom_mixer) == 1, then use this one circuit as mixer for all layers. If len(custom_mixer) == len(gammas), then use each circuit as mixer for each layer. If len(custom_mixer) != 1 and != len(gammas), raise an error. + include_measurements (bool, optional): If ``True``, append measurement gates to all qubits. + Defaults to ``True``. Returns: :class:`qibo.models.Circuit`: The QAOA or XQAOA circuit corresponding to the QUBO problem. """ if alphas is not None: # Use XQAOA, ignore mixer_function - circuit = self._build(gammas, betas, alphas) + circuit = self._build( + gammas, betas, alphas, include_measurements=include_measurements + ) else: if custom_mixer: circuit = self._build( - gammas, betas, alphas=None, custom_mixer=custom_mixer + gammas, + betas, + alphas=None, + custom_mixer=custom_mixer, + include_measurements=include_measurements, ) else: - circuit = self._build(gammas, betas) + circuit = self._build( + gammas, betas, include_measurements=include_measurements + ) return circuit + def _split_qaoa_parameters(self, parameters, p, has_alphas=False): + """Unpack a flat QAOA parameter vector in block format.""" + gammas = parameters[:p] + betas = parameters[p : 2 * p] + unpacked_alphas = ( + parameters[2 * p : 3 * p] + if has_alphas + else None + ) + return gammas, betas, unpacked_alphas + + def qaoa_circuit_from_parameters( + self, + parameters, + p, + custom_mixer=None, + include_measurements=True, + has_alphas=False, + ): + """Build a QAOA circuit directly from flat block-ordered parameters.""" + gammas, betas, unpacked_alphas = self._split_qaoa_parameters( + parameters, p, has_alphas=has_alphas + ) + return self.qubo_to_qaoa_circuit( + gammas=gammas, + betas=betas, + alphas=unpacked_alphas, + custom_mixer=custom_mixer, + include_measurements=include_measurements, + ) + + def make_qaoa_circuit_callable( + self, p, custom_mixer=None, has_alphas=False, include_measurements=False + ): + """Create a fixed-arity callable for qiboml circuit tracing.""" + nparams = 3 * p if has_alphas else 2 * p + param_names = [f"theta_{i}" for i in range(nparams)] + signature = inspect.Signature( + [ + inspect.Parameter(name, inspect.Parameter.POSITIONAL_OR_KEYWORD) + for name in param_names + ] + ) + + def qaoa_circuit(*angles): + return self.qaoa_circuit_from_parameters( + parameters=angles, + p=p, + custom_mixer=custom_mixer, + include_measurements=include_measurements, + has_alphas=has_alphas, + ) + + qaoa_circuit.__signature__ = signature + return qaoa_circuit + def train_QAOA( self, gammas=None, @@ -512,6 +590,11 @@ def train_QAOA( custom_mixer=None, backend=None, noise_model=None, + engine="legacy", + optimizer="adam", + lr=0.05, + epochs=100, + differentiation=None, ): """ Constructs the QAOA or XQAOA circuit with optional parameters for the mixers or phases before using a classical @@ -538,6 +621,17 @@ def train_QAOA( If ``None``, it uses the current backend. Defaults to ``None``. noise_model (:class:`qibo.noise.NoiseModel`, optional): noise model applied to simulate noisy computations. Defaults to None. + engine (str, optional): Training engine. ``"legacy"`` uses ``qibo.optimizers.optimize``. + ``"qiboml"`` uses qiboml's pytorch ``QuantumModel`` training loop. Defaults to ``"legacy"``. + optimizer (str, optional): Optimizer name used when ``engine="qiboml"``. + Supported values are ``"adam"`` and ``"sgd"``. Defaults to ``"adam"``. + lr (float, optional): Learning rate used when ``engine="qiboml"``. + Defaults to ``0.05``. + epochs (int, optional): Number of optimization steps used when ``engine="qiboml"``. + Defaults to ``100``. + differentiation (str, optional): Differentiation backend used when ``engine="qiboml"``. + Supported values are ``None``, ``"PSR"``, ``"Jax"``, and ``"Adjoint"``. + Defaults to ``None``. Returns: Tuple[float, List[float], dict, :class:`qibo.models.Circuit`, dict]: A tuple containing: @@ -586,10 +680,51 @@ def train_QAOA( self.n_layers = p self.num_betas = len(betas) + has_alphas = alphas is not None parameters = list(gammas) + list(betas) - if alphas is not None: + if has_alphas: parameters += list(alphas) + + if engine not in ("legacy", "qiboml"): + raise_error( + ValueError, + f"Unsupported engine '{engine}'. Use 'legacy' or 'qiboml'.", + ) + + if engine == "qiboml" and regular_loss: + best, params, extra = optimize_qaoa_with_qiboml( + qubo=self, + parameters=parameters, + p=p, + nshots=nshots, + noise_model=noise_model, + custom_mixer=custom_mixer, + has_alphas=has_alphas, + optimizer=optimizer, + lr=lr, + epochs=epochs, + differentiation=differentiation, + backend=backend, + ) + else: + if engine == "qiboml" and not regular_loss: + # qiboml path currently supports expectation-value optimization only. + engine = "legacy" + if not regular_loss and not (0 < cvar_delta <= 1): + raise_error( + ValueError, + f"cvar_delta must satisfy 0 < cvar_delta <= 1, but got {cvar_delta}.", + ) + + def _probability_dict_from_state(result): + probabilities = np.asarray(result.probabilities()).ravel() + return { + format(index, f"0{self.n}b"): float(probability) + for index, probability in enumerate(probabilities) + if probability > 0 + } + if regular_loss: def myloss(parameters): @@ -603,17 +738,12 @@ def myloss(parameters): loss (float): The computed expectation value. """ - p = len(gammas) - if alphas is not None: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = parameters[2 * p : 3 * p] - else: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = None - circuit = self.qubo_to_qaoa_circuit( - gammas_, betas_, alphas=unpacked_alphas, custom_mixer=custom_mixer + circuit = self.qaoa_circuit_from_parameters( + parameters=parameters, + p=p, + custom_mixer=custom_mixer, + include_measurements=not use_exact, + has_alphas=has_alphas, ) if noise_model is not None: circuit = noise_model.apply(circuit) @@ -644,17 +774,12 @@ def myloss(parameters, delta=cvar_delta): Returns: cvar (float): The computed CVaR value. """ - # m = len(parameters) - if alphas is not None: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = parameters[2 * p : 3 * p] - else: - gammas_ = parameters[:p] - betas_ = parameters[p : 2 * p] - unpacked_alphas = None - circuit = self.qubo_to_qaoa_circuit( - gammas_, betas_, alphas=unpacked_alphas, custom_mixer=custom_mixer + circuit = self.qaoa_circuit_from_parameters( + parameters=parameters, + p=p, + custom_mixer=custom_mixer, + include_measurements=not use_exact, + has_alphas=has_alphas, ) if noise_model is not None: circuit = noise_model.apply(circuit) @@ -702,23 +827,16 @@ def myloss(parameters, delta=cvar_delta): cvar = sum(energy * prob for energy, prob in selected_energies) / delta return cvar - best, params, extra = optimize( - myloss, parameters, method=method, options={"maxiter": maxiter} - ) - # Unpack optimised parameters in the same way as in myloss (block format) - if alphas is not None: - optimised_gammas = params[:p] - optimised_betas = params[p : 2 * p] - optimised_alphas = params[2 * p : 3 * p] - else: - optimised_gammas = params[:p] - optimised_betas = params[p : 2 * p] - optimised_alphas = None - circuit = self.qubo_to_qaoa_circuit( - gammas=optimised_gammas, - betas=optimised_betas, - alphas=optimised_alphas, + if engine == "legacy": + best, params, extra = optimize( + myloss, parameters, method=method, options={"maxiter": maxiter} + ) + circuit = self.qaoa_circuit_from_parameters( + parameters=params, + p=p, custom_mixer=custom_mixer, + include_measurements=not use_exact, + has_alphas=has_alphas, ) original_circuit = Circuit.copy(circuit) if noise_model is not None: @@ -737,7 +855,7 @@ def myloss(parameters, delta=cvar_delta): params, extra, circuit, - result.frequencies(binary=True), + statistics, original_circuit, ) return best, params, extra, circuit, statistics From a386efd1d5c66311d026e034aff97b5912072155 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:44:34 +0800 Subject: [PATCH 11/28] added maxcut notebook example for optimisation with qiboml --- tutorial/Max-Cut-qiboml.ipynb | 411 ++++++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 tutorial/Max-Cut-qiboml.ipynb diff --git a/tutorial/Max-Cut-qiboml.ipynb b/tutorial/Max-Cut-qiboml.ipynb new file mode 100644 index 0000000..80be3b4 --- /dev/null +++ b/tutorial/Max-Cut-qiboml.ipynb @@ -0,0 +1,411 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5099a13c", + "metadata": {}, + "source": [ + "# Maximum-Cut Problem using Variants of Quantum Approximate Optimization Algorithm (QAOA), a small illustration of QiboOpt" + ] + }, + { + "cell_type": "markdown", + "id": "6e77f23f", + "metadata": {}, + "source": [ + "## Maximum-Cut Problem\n" + ] + }, + { + "cell_type": "markdown", + "id": "8a1a5456", + "metadata": {}, + "source": [ + "The **Maximum-Cut Problem** (MCP) can be stated as follows:\n", + "\n", + "> Given a graph G = (V, E) with vertices V and edges E (possibly weighted), the Maximum-Cut Problem seeks to divide the set of verticies into two complementary subsets such that the total weight (or number) of edges between these subsets is maximised.\n", + "\n", + "### Key Points:\n", + "\n", + "1. **Combinatorial Nature:** For a graph with $n$ nodes, there are $2^{n-1} - 1$ possible unique cuts (since swapping the two sets produces the same cut). This exonential growth makes MCP computationally hard for large $n$.\n", + "\n", + "2. **Applications:**\n", + " - **Machine Learning:** \n", + " The Maximum-Cut Problem (MCP) can be reformulated as an optimisation over binary variables, making it useful for tasks such as binary classification and clustering.\n", + "\n", + " - **Statistical / Theoretical Physics:** \n", + " MCP is closely related to minimising the **Hamiltonian** of spin glass systems. \n", + " *By negating the edge weights, a minimisation problem can be transformed into a maximisation one — and vice versa.*\n", + "\n", + " - **Network Design & Circuit Layout:** \n", + " It also arises in applications such as community detection, VLSI (Very Large-Scale Integration) circuit layout optimisation, and social network analysis.\n", + "\n", + "\n", + "3. **Optimisation Goal:** The objective is to maximise the total weight of the edges crossing the cut." + ] + }, + { + "cell_type": "markdown", + "id": "b472b5ef", + "metadata": {}, + "source": [ + "Here is a $5$-person example where we must split people into two groups to minimise within-group conflict. Equivalently, to maximise the total “conflict” cut. We model pairwise incompatibility with a symmetric weight matrix $W$ (higher = more tension), i.e. an undirected weighted graph:\n", + "\n", + "$$\n", + "\\mathbf{W} =\n", + "\\begin{bmatrix}\n", + "0 & 0.2 & 0.8 & 0.8 & 0.8 \\\\\n", + "0.2 & 0 & 0.8 & 0.8 & 0.8 \\\\\n", + "0.8 & 0.8 & 0 & 0.2 & 0.2 \\\\\n", + "0.8 & 0.8 & 0.2 & 0 & 0.2 \\\\\n", + "0.8 & 0.8 & 0.2 & 0.2 & 0\n", + "\\end{bmatrix}\n", + "$$\n", + "\n", + "The $(i,j)$ entry gives the incompatibility between people $i$ and $j$. It is important to note that the matrix has zero diagonal and is symmetric $(W_{ij} = W_{ji})$.\n", + "\n", + "Let's visualise this:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8d266bf9", + "metadata": {}, + "outputs": [], + "source": [ + "# Setting up the challenge as so\n", + "# The end result should be [0, 0, 1, 1, 1]\n", + "# or [1, 1, 0, 0, 0]\n", + "\n", + "import numpy as np\n", + "import networkx as nx\n", + "import matplotlib.pyplot as plt\n", + "\n", + "dist_matrix = np.array([\n", + " [0, 0.2, 0.8, 0.8, 0.8],\n", + " [0.2, 0, 0.8, 0.8, 0.8],\n", + " [0.8, 0.8, 0, 0.2, 0.2],\n", + " [0.8, 0.8, 0.2, 0, 0.2],\n", + " [0.8, 0.8, 0.2, 0.2, 0]\n", + "])\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0e0bc5ef", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create a weighted undirected graph from the matrix\n", + "G = nx.from_numpy_array(dist_matrix)\n", + "\n", + "# Relabel nodes (0-5)\n", + "mapping = {0:'0', 1:'1', 2:'2', 3:'3', 4:'4'}\n", + "mapping = {0:'0', 1:'1', 2:'2', 3:'3', 4:'4'}\n", + "G = nx.relabel_nodes(G, mapping)\n", + "\n", + "# Draw nodes and edges\n", + "pos = nx.spring_layout(G, seed=42, weight='weight', k=0.6)\n", + "plt.figure(figsize=(6, 5))\n", + "nx.draw_networkx_nodes(G, pos, node_color='#ffd166', node_size=800, edgecolors='black')\n", + "nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold')\n", + "\n", + "# Edge widths and colors proportional to weights\n", + "weights = [G[u][v]['weight'] for u,v in G.edges()]\n", + "nx.draw_networkx_edges(\n", + " G, \n", + " pos, \n", + " width=[3*w for w in weights], \n", + " alpha=0.7, \n", + " edge_color='#118ab2'\n", + " )\n", + "\n", + "# Label edges with weights\n", + "edge_labels = {(u,v): f\"{G[u][v]['weight']:.1f}\" for u,v in G.edges()}\n", + "nx.draw_networkx_edge_labels(\n", + " G, \n", + " pos, \n", + " edge_labels=edge_labels, \n", + " font_size=9, \n", + " font_color='gray',\n", + " label_pos=0.64 \n", + " )\n", + "\n", + "# Output Graph\n", + "plt.title(\"Weighted Graph for Max-Cut Example\", fontsize=13)\n", + "plt.axis('off')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "0471e0f4", + "metadata": {}, + "source": [ + "By splitting the verticies into the sets $S = \\{0, 1\\}$ and $S' = \\{2, 3, 4\\}$, the edges with the higher values (0.8) are cut. This configuration therefore yields a maximum cut, as it maximises the total weight of edges crossing between the two subsets.\n", + "\n", + "In this tutorial, we will explore a similar max-cut instance and demostrate that the optimal partition can be identified in a small, illustrative example using the Quantum Approximate Optimisation Algorithm. This serves as a proof-of-concept showing how MCP can be formulated as a QUBO using Qibo." + ] + }, + { + "cell_type": "markdown", + "id": "2d8aefa3", + "metadata": {}, + "source": [ + "## Encoding of the Maximum Cut Objective as a Quadratic Unconstrained Binary Optimisation (QUBO) Problem" + ] + }, + { + "cell_type": "markdown", + "id": "196d1082", + "metadata": {}, + "source": [ + "The Maximum-Cut problem can be encoded as a Quadratic Unconstrained Binary Optimisation (QUBO) problem. \n", + "\n", + "Consider a graph $G = (V, E)$ with vertex set $V$ and weighted edges $w_{uv}$. We assign to each vertex $u \\in V$ a binary variable $x_u \\in \\set {0, 1}$, which encodes the subset to which the vertex belongs. For example, $x_u = 0$ if the vertex is in set $S$, and $x_u = 1$ if it is in the complementary set $S'$.\n", + "\n", + "An edge $(u, v)$ is said to be cut if its endpoints lie in different subsets: if $x_u \\neq x_v$. The total weight of the cut can then be written as\n", + "\n", + "$$C(x) = \\frac{1}{2} \\sum_{(u,v)\\in E} w_{uv}(x_u-x_v)^2 $$\n", + "\n", + "This quadratic expression naturally defines the QUBO objective for Max-Cut, as it is a function solely of binary variables. The goal is to find the binary assignment {$x_u$} that maximises $C(x)$ thereby maximising the total weight of the edges that are cut.\n", + "\n", + "Using the same approach introduced by Hadfield [4], the Quantum Approximate Optimization Algorithm (QAOA) can be employed to solve this formulation on quantum hardware. In Qibo, this encoding is implemented through a Max-Cut class, which allows us to specify the graph weights directly and construct the corresponding QUBO Hamiltonian automatically." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fc0d16e4", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[Qibo 0.2.23|INFO|2026-02-24 17:40:10]: Using numpy backend on /CPU:0\n", + "INFO:qibo.config:Using numpy backend on /CPU:0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best bitstring: 11000 (count=4345)\n", + "Cut weight (true units): 4.800\n", + "Partition: S=[2, 3, 4], S'=[0, 1]\n" + ] + } + ], + "source": [ + "import itertools, numpy as np\n", + "from qiboopt.combinatorial.combinatorial import MaxCut\n", + "from matplotlib.lines import Line2D\n", + "from collections import defaultdict\n", + "\n", + "\n", + "# Build Max-Cut instance (you can also pass networkx Graph 'G' directly)\n", + "mc = MaxCut(dist_matrix, backend=None, normalize='maxdeg', mixer='xy')\n", + "\n", + "# qiboml training is exposed via qiboopt's QUBO wrapper train_QAOA API.\n", + "qubo = mc.to_qubo()\n", + "p = 2 # QAOA depth\n", + "nshots = 10000\n", + "\n", + "# qiboml optimizer settings\n", + "engine = \"qiboml\"\n", + "optimizer = \"adam\"\n", + "lr = 0.05\n", + "epochs = 100\n", + "differentiation = 'PSR'\n", + "\n", + "best_E, params, extra, circuit, freqs = qubo.train_QAOA(\n", + " p=p,\n", + " nshots=nshots,\n", + " engine=engine,\n", + " optimizer=optimizer,\n", + " lr=lr,\n", + " epochs=epochs,\n", + " differentiation=differentiation,\n", + ")\n", + "\n", + "# Pick most frequent bitstring and evaluate cut\n", + "best_bits = max(freqs, key=freqs.get)\n", + "best_cut_true = mc.cut_value(best_bits, use_scaled=False)\n", + "S, Sp = mc.partition_from_bits(best_bits)\n", + "\n", + "\n", + "# Print results\n", + "print(f\"Best bitstring: {best_bits} (count={freqs[best_bits]})\")\n", + "print(f\"Cut weight (true units): {best_cut_true:.3f}\")\n", + "print(f\"Partition: S={sorted(S)}, S'={sorted(Sp)}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0715c1ef", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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UXn8L46IKE7YtYCIiIiIDIkrskREBmT9zc/IH0RE6YwrskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIGYm+ACKidKMNdGP0q3+IZL9GFNkSfRlERCExyBIRxdEHPvSb+MXPfpr8r3mRTV4rEVEyU3Rd15GkNGhwwQNFXKj8fyIi49B0HT6vGRlQoSp8DyMiY9F1QIREixlQk/QtjDWyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgMskRERERkSAyyRERERGRIDLJEREREZEgZMAB9yv8TERnF1HctnW9hRGQwOpKfout8eyUiIiIi42FpAREREREZEoMsERERERkSgywRERERGRKDLBEREREZEoMsERERERkSgywRERERGRKDLBEREREZEoMsERERERkSgywRERERwYj+f5+jhaLiSImiAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualize partition on the same layout 'pos' and graph 'G'\n", + "\n", + "\n", + "# Ensure S and Sp exist (from earlier QAOA result), and G/pos are defined.\n", + "# If MaxCut was built from a matrix (S, Sp as ints) but G was relabeled (A..E),\n", + "# use the same 'mapping' to relabel the graph.\n", + "try:\n", + " if len(S) and isinstance(next(iter(S)), int):\n", + " sample_node = next(iter(G.nodes()))\n", + " if not isinstance(sample_node, int) and 'mapping' in globals():\n", + " S = {mapping[i] for i in S}\n", + " Sp = {mapping[i] for i in Sp}\n", + "except Exception:\n", + " pass\n", + "\n", + "# Base layout: reuse existing 'pos' if it exists; else compute one\n", + "if 'pos' not in globals():\n", + " pos = nx.spring_layout(G, seed=42, weight='weight')\n", + "\n", + "# Create horizontally separated layout\n", + "sep = 0.9 # horizontal separation amount\n", + "pos2 = {}\n", + "for n in G.nodes():\n", + " x, y = pos[n]\n", + " if n in S:\n", + " pos2[n] = (x - sep, y)\n", + " elif n in Sp:\n", + " pos2[n] = (x + sep, y)\n", + " else:\n", + " pos2[n] = (x, y) # fallback for any stray nodes\n", + "\n", + "# Partition edges into cut vs intra-group\n", + "def is_cut(u, v): \n", + " return (u in S and v in Sp) or (u in Sp and v in S)\n", + "\n", + "edges = list(G.edges())\n", + "weights = np.array([G[u][v].get('weight', 1.0) for u, v in edges], dtype=float)\n", + "wmin, wmax = (weights.min() if len(weights) else 1.0), (weights.max() if len(weights) else 1.0)\n", + "widths = 1.0 + 4.0 * (weights - wmin) / (wmax - wmin + 1e-9)\n", + "\n", + "mask_cut = np.array([is_cut(u, v) for u, v in edges], dtype=bool)\n", + "edges_cut = [e for e, m in zip(edges, mask_cut) if m]\n", + "edges_in = [e for e, m in zip(edges, mask_cut) if not m]\n", + "widths_cut = widths[mask_cut] if len(edges) else []\n", + "widths_in = widths[~mask_cut] if len(edges) else []\n", + "\n", + "# Background shading for groups\n", + "xs = [xy[0] for xy in pos2.values()]\n", + "xmin, xmax = min(xs) - 0.2, max(xs) + 0.2\n", + "xmid = 0.5 * (xmin + xmax)\n", + "\n", + "fig, ax = plt.subplots(figsize=(7, 6))\n", + "ax.axvspan(xmin, xmid, color='#f0fff4', alpha=0.55, zorder=0) # left group background\n", + "ax.axvspan(xmid, xmax, color='#f0f7ff', alpha=0.55, zorder=0) # right group background\n", + "\n", + "# Draw nodes with distinct shapes/colors\n", + "nodes_S = list(S)\n", + "nodes_Sp = list(Sp)\n", + "\n", + "nx.draw_networkx_nodes(G, pos2, nodelist=nodes_S, node_color='#06d6a0', node_shape='o',\n", + " node_size=900, edgecolors='black', linewidths=1.2, ax=ax)\n", + "nx.draw_networkx_nodes(G, pos2, nodelist=nodes_Sp, node_color='#118ab2', node_shape='s',\n", + " node_size=900, edgecolors='black', linewidths=1.2, ax=ax)\n", + "\n", + "nx.draw_networkx_labels(G, pos2, font_size=12, font_weight='bold', ax=ax)\n", + "\n", + "# Draw intra-group edges (light) and cut edges (highlighted)\n", + "nx.draw_networkx_edges(G, pos2, edgelist=edges_in, width=widths_in,\n", + " alpha=0.25, edge_color='#e76f51', ax=ax)\n", + "nx.draw_networkx_edges(G, pos2, edgelist=edges_cut, width=widths_cut,\n", + " alpha=0.95, edge_color='#9e9e9e', ax=ax)\n", + "\n", + "# Label edges\n", + "edge_labels = {(u, v): f\"{G[u][v].get('weight', 1.0):.1f}\" for (u, v) in edges}\n", + "nx.draw_networkx_edge_labels(G, pos2, edge_labels=edge_labels, font_size=9,\n", + " font_color='#444', bbox=dict(facecolor='white', edgecolor='none', alpha=0.6),\n", + " label_pos=0.6, ax=ax)\n", + "\n", + "# Legends\n", + "legend_nodes = [\n", + " Line2D([0], [0], marker='o', color='w', label=\"S\", markerfacecolor='#06d6a0',\n", + " markeredgecolor='black', markersize=12),\n", + " Line2D([0], [0], marker='s', color='w', label=\"S'\", markerfacecolor='#118ab2',\n", + " markeredgecolor='black', markersize=12),\n", + "]\n", + "legend_edges = [\n", + " Line2D([0], [0], color='#9e9e9e', lw=2.5, label='Cut edges'),\n", + " Line2D([0], [0], color='#e76f51', lw=2.5, alpha=0.5, label='Intra-group edges'),\n", + "]\n", + "ax.legend(handles=legend_nodes + legend_edges, loc='upper center', ncol=2, frameon=False)\n", + "\n", + "# Optional group labels\n", + "if nodes_S:\n", + " sx = np.mean([pos2[n][0] for n in nodes_S]); sy = max([pos2[n][1] for n in nodes_S]) + 0.15\n", + " ax.text(sx, sy, \"S\", fontsize=13, color='#048a63', ha='center', va='bottom')\n", + "if nodes_Sp:\n", + " tx = np.mean([pos2[n][0] for n in nodes_Sp]); ty = max([pos2[n][1] for n in nodes_Sp]) + 0.15\n", + " ax.text(tx, ty, \"S'\", fontsize=13, color='#0b6691', ha='center', va='bottom')\n", + "\n", + "ax.set_title(\"Max-Cut Partition: S vs S' (cut edges highlighted)\", fontsize=14)\n", + "ax.axis('off')\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "118a7007", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "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.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From aedc6e9dfe8d86b52e27400b6805b9efcee73abd Mon Sep 17 00:00:00 2001 From: J F Kong Date: Tue, 24 Feb 2026 17:50:12 +0800 Subject: [PATCH 12/28] updated qiboml dependencies --- pyproject.toml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 080982d..700876d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,12 @@ packages = [{include="qiboopt", from="src"}] [tool.poetry.dependencies] python = ">=3.10,<3.14" -qibo = "^0.2" +qibo = ">=0.2,<0.3" +qiboml = {version = ">=0.1", optional = true} +torch = {version = "^2.7.0", optional = true} + +[tool.poetry.extras] +qiboml = ["qiboml", "torch"] [build-system] requires = ["poetry-core"] @@ -38,6 +43,13 @@ pytest-env = ">=0.8.1" pylint = "^3.3.5" matplotlib = "^3.9.2" +[tool.poetry.group.qiboml] +optional = true + +[tool.poetry.group.qiboml.dependencies] +qiboml = ">=0.1" +torch = "^2.7.0" + [tool.pytest.ini_options] testpaths = ['tests/'] filterwarnings = ['ignore::RuntimeWarning'] From 92f6a21b9be3980599032d63ebeba798fbcd75ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:15:58 +0000 Subject: [PATCH 13/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qiboopt/integrations/qiboml_adapter.py | 18 ++++++++++-------- src/qiboopt/opt_class/opt_class.py | 9 +++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 9a881e8..5d52987 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Any, Dict, Optional +from typing import Any import numpy as np @@ -13,10 +13,10 @@ def _energy_shift(qubo) -> float: return float(constant) -def _get_differentiation_class(name: Optional[str]): - if name is None or name == 'torch': +def _get_differentiation_class(name: str | None): + if name is None or name == "torch": return None - from qiboml.operations.differentiation import Adjoint, Jax, PSR + from qiboml.operations.differentiation import PSR, Adjoint, Jax mapping = { "psr": PSR, @@ -38,16 +38,16 @@ def optimize_qaoa_with_qiboml( qubo, parameters, p: int, - nshots: Optional[int], + nshots: int | None, noise_model, custom_mixer, has_alphas: bool, optimizer: str, lr: float, epochs: int, - differentiation: Optional[str], + differentiation: str | None, backend, -) -> tuple[float, np.ndarray, Dict[str, Any]]: +) -> tuple[float, np.ndarray, dict[str, Any]]: """Optimize QAOA parameters using qiboml's pytorch interface.""" try: import torch @@ -101,7 +101,9 @@ def optimize_qaoa_with_qiboml( elif optimizer_name == "sgd": torch_optimizer = torch.optim.SGD(model.parameters(), lr=lr) else: - raise ValueError("Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'.") + raise ValueError( + "Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'." + ) losses = [] best = float("inf") diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index 910a3ee..badbd59 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -2,8 +2,8 @@ Optimisation classes """ -import itertools import inspect +import itertools from collections import defaultdict import numpy as np @@ -14,6 +14,7 @@ from qibo.models import QAOA from qibo.optimizers import optimize from qibo.symbols import Z + from qiboopt.integrations.qiboml_adapter import optimize_qaoa_with_qiboml @@ -524,11 +525,7 @@ def _split_qaoa_parameters(self, parameters, p, has_alphas=False): """Unpack a flat QAOA parameter vector in block format.""" gammas = parameters[:p] betas = parameters[p : 2 * p] - unpacked_alphas = ( - parameters[2 * p : 3 * p] - if has_alphas - else None - ) + unpacked_alphas = parameters[2 * p : 3 * p] if has_alphas else None return gammas, betas, unpacked_alphas def qaoa_circuit_from_parameters( From 8cfaba53f6f99f2c83ec1d3fd84956a2c9f200b5 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Fri, 13 Mar 2026 23:18:01 +0800 Subject: [PATCH 14/28] issue warnings for unsupported optimisers --- doc/source/getting-started/quickstart.rst | 28 + poetry.lock | 673 +++++++++++++++++++-- src/qiboopt/__init__.py | 10 +- src/qiboopt/integrations/qiboml_adapter.py | 27 +- src/qiboopt/opt_class/opt_class.py | 14 +- tests/test_opt_class.py | 69 ++- tests/test_tutorial_max_cut_notebook.py | 31 +- 7 files changed, 795 insertions(+), 57 deletions(-) diff --git a/doc/source/getting-started/quickstart.rst b/doc/source/getting-started/quickstart.rst index aef588a..d91c690 100644 --- a/doc/source/getting-started/quickstart.rst +++ b/doc/source/getting-started/quickstart.rst @@ -59,3 +59,31 @@ The Conditional Variance at Risk (CVaR) can also be used as an alternative loss gammas = [0.1, 0.2] betas = [0.3, 0.4] output = qp.train_QAOA(gammas=gammas, betas=betas, regular_loss=False, cvar_delta=0.1) + +To use qiboml's pytorch training loop instead of the legacy optimizer, set ``engine="qiboml"``: + +.. code-block:: python + + from qiboopt.opt_class.opt_class import QUBO + gammas = [0.1, 0.2] + betas = [0.3, 0.4] + output = qp.train_QAOA( + gammas=gammas, + betas=betas, + engine="qiboml", + optimizer="adam", + lr=0.05, + epochs=100, + ) + +You can also run in exact (no-shot) mode by setting ``nshots=None`` (or ``nshots=0``): + +.. code-block:: python + + from qiboopt.opt_class.opt_class import QUBO + gammas = [0.1, 0.2] + betas = [0.3, 0.4] + output = qp.train_QAOA(gammas=gammas, betas=betas, nshots=None) + +In sampled mode (``nshots > 0``), the returned dictionary contains bitstring counts. +In exact mode (``nshots is None`` or ``nshots == 0``), it contains exact bitstring probabilities. diff --git a/poetry.lock b/poetry.lock index 666d872..b9029c5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -18,7 +18,7 @@ version = "1.16.2" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "alembic-1.16.2-py3-none-any.whl", hash = "sha256:5f42e9bd0afdbd1d5e3ad856c01754530367debdebf21ed6894e34af52b3bb03"}, {file = "alembic-1.16.2.tar.gz", hash = "sha256:e53c38ff88dadb92eb22f8b150708367db731d58ad7e9d417c9168ab516cbed8"}, @@ -39,7 +39,7 @@ version = "4.13.2" description = "ANTLR 4.13.2 runtime for Python 3" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "qiboml"] 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"}, @@ -338,7 +338,7 @@ 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"] +groups = ["main", "qiboml"] files = [ {file = "cma-3.4.0-py3-none-any.whl", hash = "sha256:4140e490cc4e68cf8c7b1114e079c0561c9b78b1bf9ec69362c20865636ae5ca"}, {file = "cma-3.4.0.tar.gz", hash = "sha256:a1ebd969b99871be3715d5a24b7bf54cf04ea94e80d6b8536d7147620dd10f6c"}, @@ -357,12 +357,12 @@ 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", "docs", "tests"] +groups = ["main", "docs", "qiboml", "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\"", docs = "sys_platform == \"win32\"", tests = "sys_platform == \"win32\""} +markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", docs = "sys_platform == \"win32\"", qiboml = "sys_platform == \"win32\" or platform_system == \"Windows\"", tests = "sys_platform == \"win32\""} [[package]] name = "colorlog" @@ -370,7 +370,7 @@ version = "6.9.0" description = "Add colours to the output of Python's logging module." optional = false python-versions = ">=3.6" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff"}, {file = "colorlog-6.9.0.tar.gz", hash = "sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2"}, @@ -651,6 +651,60 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +[[package]] +name = "cuda-bindings" +version = "12.9.4" +description = "Python bindings for CUDA" +optional = false +python-versions = "*" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a"}, + {file = "cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5"}, + {file = "cuda_bindings-12.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:f69107389e6b9948969bfd0a20c4f571fd1aefcfb1d2e1b72cc8ba5ecb7918ab"}, + {file = "cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6a429dc6c13148ff1e27c44f40a3dd23203823e637b87fd0854205195988306"}, + {file = "cuda_bindings-12.9.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c912a3d9e6b6651853eed8eed96d6800d69c08e94052c292fec3f282c5a817c9"}, + {file = "cuda_bindings-12.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:443b0875916879c2e4c3722941e25e42d5ab9bcbf34c9e83404fb100fa1f6913"}, + {file = "cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:694ba35023846625ef471257e6b5a4bc8af690f961d197d77d34b1d1db393f56"}, + {file = "cuda_bindings-12.9.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fda147a344e8eaeca0c6ff113d2851ffca8f7dfc0a6c932374ee5c47caa649c8"}, + {file = "cuda_bindings-12.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:696ca75d249ddf287d01b9a698b8e2d8a05046495a9c051ca15659dc52d17615"}, + {file = "cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cf8bfaedc238f3b115d957d1fd6562b7e8435ba57f6d0e2f87d0e7149ccb2da5"}, + {file = "cuda_bindings-12.9.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:32bdc5a76906be4c61eb98f546a6786c5773a881f3b166486449b5d141e4a39f"}, + {file = "cuda_bindings-12.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:a2e82c8985948f953c2be51df45c3fe11c812a928fca525154fb9503190b3e64"}, + {file = "cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3adf4958dcf68ae7801a59b73fb00a8b37f8d0595060d66ceae111b1002de38d"}, + {file = "cuda_bindings-12.9.4-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56e0043c457a99ac473ddc926fe0dc4046694d99caef633e92601ab52cbe17eb"}, + {file = "cuda_bindings-12.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b32d8b685f0e66f5658bcf4601ef034e89fc2843582886f0a58784a4302da06c"}, + {file = "cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f53a7f453d4b2643d8663d036bafe29b5ba89eb904c133180f295df6dc151e5"}, + {file = "cuda_bindings-12.9.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b72ee72a9cc1b531db31eebaaee5c69a8ec3500e32c6933f2d3b15297b53686"}, + {file = "cuda_bindings-12.9.4-cp314-cp314-win_amd64.whl", hash = "sha256:53a10c71fdbdb743e0268d07964e5a996dd00b4e43831cbfce9804515d97d575"}, + {file = "cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:20f2699d61d724de3eb3f3369d57e2b245f93085cab44fd37c3bea036cea1a6f"}, + {file = "cuda_bindings-12.9.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d80bffc357df9988dca279734bc9674c3934a654cab10cadeed27ce17d8635ee"}, + {file = "cuda_bindings-12.9.4-cp314-cp314t-win_amd64.whl", hash = "sha256:53e11991a92ff6f26a0c8a98554cd5d6721c308a6b7bfb08bebac9201e039e43"}, + {file = "cuda_bindings-12.9.4-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:893ca68114b5b769c1d4c02583b91ed22691887c3ed513b59467d23540104db4"}, + {file = "cuda_bindings-12.9.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9866ceec83e39337d1a1d64837864c964ad902992478caa288a0bc1be95f21aa"}, + {file = "cuda_bindings-12.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:37744e721a18a514423e81863f52a4f7f46f5a6f9cccd569f2735f8067f4d8c2"}, +] + +[package.dependencies] +cuda-pathfinder = ">=1.1,<2.0" + +[package.extras] +all = ["nvidia-cuda-nvcc-cu12", "nvidia-cuda-nvrtc-cu12", "nvidia-cufile-cu12 ; sys_platform == \"linux\"", "nvidia-nvjitlink-cu12 (>=12.3)"] +test = ["cython (>=3.1,<3.2)", "numpy (>=1.21.1)", "pyglet (>=2.1.9)", "pytest (>=6.2.4)", "pytest-benchmark (>=3.4.1)", "setuptools (>=77.0.0)"] + +[[package]] +name = "cuda-pathfinder" +version = "1.3.3" +description = "Pathfinder for CUDA components" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1"}, +] + [[package]] name = "cycler" version = "0.12.1" @@ -741,6 +795,18 @@ files = [ [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +[[package]] +name = "filelock" +version = "3.20.3" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, + {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, +] + [[package]] name = "fonttools" version = "4.60.1" @@ -822,6 +888,46 @@ type1 = ["xattr ; sys_platform == \"darwin\""] unicode = ["unicodedata2 (>=15.1.0) ; python_version <= \"3.12\""] woff = ["brotli (>=1.0.1) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\"", "zopfli (>=0.1.4)"] +[[package]] +name = "fsspec" +version = "2026.2.0" +description = "File-system specification" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437"}, + {file = "fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff (>=0.5)"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs (>2024.2.0)", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs (>2024.2.0)", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs (>2024.2.0)"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs (>2024.2.0)"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "backports-zstd ; python_version < \"3.14\"", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas (<3.0.0)", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard ; python_version < \"3.14\""] +tqdm = ["tqdm"] + [[package]] name = "furo" version = "2024.8.6" @@ -846,7 +952,7 @@ version = "3.2.3" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] 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.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be"}, @@ -964,13 +1070,86 @@ files = [ colors = ["colorama"] plugins = ["setuptools"] +[[package]] +name = "jax" +version = "0.4.38" +description = "Differentiate, compile, and transform Numpy code." +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "jax-0.4.38-py3-none-any.whl", hash = "sha256:78987306f7041ea8500d99df1a17c33ed92620c2268c4c3677fb24e06712be64"}, + {file = "jax-0.4.38.tar.gz", hash = "sha256:43bae65881628319e0a2148e8f81a202fbc2b8d048e35c7cb1df2416672fa4a8"}, +] + +[package.dependencies] +jaxlib = "0.4.38" +ml_dtypes = ">=0.4.0" +numpy = [ + {version = ">=1.24"}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +opt_einsum = "*" +scipy = [ + {version = ">=1.10"}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, +] + +[package.extras] +ci = ["jaxlib (==0.4.36)"] +cuda = ["jax-cuda12-plugin[with-cuda] (==0.4.38)", "jaxlib (==0.4.38)"] +cuda12 = ["jax-cuda12-plugin[with-cuda] (==0.4.38)", "jaxlib (==0.4.38)"] +cuda12-local = ["jax-cuda12-plugin (==0.4.38)", "jaxlib (==0.4.38)"] +cuda12-pip = ["jax-cuda12-plugin[with-cuda] (==0.4.38)", "jaxlib (==0.4.38)"] +k8s = ["kubernetes"] +minimum-jaxlib = ["jaxlib (==0.4.38)"] +tpu = ["jaxlib (==0.4.38)", "libtpu (==0.0.7)", "libtpu-nightly (==0.1.dev20241010+nightly.cleanup)", "requests"] + +[[package]] +name = "jaxlib" +version = "0.4.38" +description = "XLA library for JAX" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "jaxlib-0.4.38-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:55c19b9d3f33a6fc59f644aa5a21fba02639ccdd776cb4a9b5526625f57839ff"}, + {file = "jaxlib-0.4.38-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30b2f52cb50d74734af2f477c2533a7a583e3bb7b2c8acdeb361ee77d940577a"}, + {file = "jaxlib-0.4.38-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ee19c163a8fdf0839d4c18b88a5fbfb4e731ba7c437416d3e5483e570bb764e4"}, + {file = "jaxlib-0.4.38-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:61aeccb9a27c67fdb8450f6357240019cd4511cb9d62a44e4764756d384853ad"}, + {file = "jaxlib-0.4.38-cp310-cp310-win_amd64.whl", hash = "sha256:d6ab745a89d0fb737a36fe1d8b86659e3fffe6ee8303b20651b26193d5edc0ef"}, + {file = "jaxlib-0.4.38-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:b67fdeabd6dfed08b7768f3bdffb521160085f8305669bd197beef61d08de08b"}, + {file = "jaxlib-0.4.38-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fb0eaae7369157afecbead50aaf29e73ffddfa77a2335d721bd9794f3c510e4"}, + {file = "jaxlib-0.4.38-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:43db58c4c427627296366a56c10318e1f00f503690e17f94bb4344293e1995e0"}, + {file = "jaxlib-0.4.38-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:2751ff7037d6a997d0be0e77cc4be381c5a9f9bb8b314edb755c13a6fd969f45"}, + {file = "jaxlib-0.4.38-cp311-cp311-win_amd64.whl", hash = "sha256:35226968fc9de6873d1571670eac4117f5ed80e955f7a1775204d1044abe16c6"}, + {file = "jaxlib-0.4.38-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:3fefea985f0415816f3bbafd3f03a437050275ef9bac9a72c1314e1644ac57c1"}, + {file = "jaxlib-0.4.38-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f33bcafe32c97a562ecf6894d7c41674c80c0acdedfa5423d49af51147149874"}, + {file = "jaxlib-0.4.38-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:496f45b0e001a2341309cd0c74af0b670537dced79c168cb230cfcc773f0aa86"}, + {file = "jaxlib-0.4.38-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:dad6c0a96567c06d083c0469fec40f201210b099365bd698be31a6d2ec88fd59"}, + {file = "jaxlib-0.4.38-cp312-cp312-win_amd64.whl", hash = "sha256:966cdec36cfa978f5b4582bcb4147fe511725b94c1a752dac3a5f52ce46b6fa3"}, + {file = "jaxlib-0.4.38-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:41e55ae5818a882e5789e848f6f16687ac132bcfbb5a5fa114a5d18b78d05f2d"}, + {file = "jaxlib-0.4.38-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6fe326b8af366387dd47ccf312583b2b17fed12712c9b74a648b18a13cbdbabf"}, + {file = "jaxlib-0.4.38-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:248cca3771ebf24b070f49701364ceada33e6139445b06c782cca5ac5ad92bf4"}, + {file = "jaxlib-0.4.38-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:2ce77ba8cda9259a4bca97afc1c722e4291a6c463a63f8d372c6edc85117d625"}, + {file = "jaxlib-0.4.38-cp313-cp313-win_amd64.whl", hash = "sha256:4103db0b3a38a5dc132741237453c24d8547290a22079ba1b577d6c88c95300a"}, +] + +[package.dependencies] +ml-dtypes = ">=0.2.0" +numpy = ">=1.24" +scipy = [ + {version = ">=1.10"}, + {version = ">=1.11.1", markers = "python_version >= \"3.12\""}, +] + [[package]] name = "jinja2" version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["docs"] +groups = ["main", "docs", "qiboml"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -988,7 +1167,7 @@ version = "1.5.1" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a"}, {file = "joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444"}, @@ -1216,7 +1395,7 @@ version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, @@ -1236,7 +1415,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["main", "docs"] +groups = ["main", "docs", "qiboml"] 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"}, @@ -1407,13 +1586,73 @@ files = [ [package.dependencies] typing-extensions = {version = "*", markers = "python_version < \"3.11\""} +[[package]] +name = "ml-dtypes" +version = "0.5.4" +description = "ml_dtypes is a stand-alone implementation of several NumPy dtype extensions used in machine learning." +optional = false +python-versions = ">=3.9" +groups = ["main", "qiboml"] +files = [ + {file = "ml_dtypes-0.5.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b95e97e470fe60ed493fd9ae3911d8da4ebac16bd21f87ffa2b7c588bf22ea2c"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4b801ebe0b477be666696bda493a9be8356f1f0057a57f1e35cd26928823e5a"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:388d399a2152dd79a3f0456a952284a99ee5c93d3e2f8dfe25977511e0515270"}, + {file = "ml_dtypes-0.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:4ff7f3e7ca2972e7de850e7b8fcbb355304271e2933dd90814c1cb847414d6e2"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6c7ecb74c4bd71db68a6bea1edf8da8c34f3d9fe218f038814fd1d310ac76c90"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc11d7e8c44a65115d05e2ab9989d1e045125d7be8e05a071a48bc76eb6d6040"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19b9a53598f21e453ea2fbda8aa783c20faff8e1eeb0d7ab899309a0053f1483"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:7c23c54a00ae43edf48d44066a7ec31e05fdc2eee0be2b8b50dd1903a1db94bb"}, + {file = "ml_dtypes-0.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:557a31a390b7e9439056644cb80ed0735a6e3e3bb09d67fd5687e4b04238d1de"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a174837a64f5b16cab6f368171a1a03a27936b31699d167684073ff1c4237dac"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a7f7c643e8b1320fd958bf098aa7ecf70623a42ec5154e3be3be673f4c34d900"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ad459e99793fa6e13bd5b7e6792c8f9190b4e5a1b45c63aba14a4d0a7f1d5ff"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:c1a953995cccb9e25a4ae19e34316671e4e2edaebe4cf538229b1fc7109087b7"}, + {file = "ml_dtypes-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:9bad06436568442575beb2d03389aa7456c690a5b05892c471215bfd8cf39460"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328"}, + {file = "ml_dtypes-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:35f29491a3e478407f7047b8a4834e4640a77d2737e0b294d049746507af5175"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6"}, + {file = "ml_dtypes-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:3bbbe120b915090d9dd1375e4684dd17a20a2491ef25d640a908281da85e73f1"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56"}, + {file = "ml_dtypes-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:5a0f68ca8fd8d16583dfa7793973feb86f2fbb56ce3966daf9c9f748f52a2049"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1"}, + {file = "ml_dtypes-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:11942cbf2cf92157db91e5022633c0d9474d4dfd813a909383bd23ce828a4b7d"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d81fdb088defa30eb37bf390bb7dde35d3a83ec112ac8e33d75ab28cc29dd8b0"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88c982aac7cb1cbe8cbb4e7f253072b1df872701fcaf48d84ffbb433b6568f24"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9b61c19040397970d18d7737375cffd83b1f36a11dd4ad19f83a016f736c3ef"}, + {file = "ml_dtypes-0.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:3d277bf3637f2a62176f4575512e9ff9ef51d00e39626d9fe4a161992f355af2"}, + {file = "ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.21.2", markers = "python_version >= \"3.10\""}, + {version = ">=2.1.0", markers = "python_version >= \"3.13\""}, + {version = ">=1.26.0", markers = "python_version == \"3.12\""}, + {version = ">=1.23.3", markers = "python_version >= \"3.11\""}, +] + +[package.extras] +dev = ["absl-py", "pyink", "pylint (>=2.6.0)", "pytest", "pytest-xdist"] + [[package]] name = "mpmath" version = "1.3.0" description = "Python library for arbitrary-precision floating-point arithmetic" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -1533,7 +1772,7 @@ version = "3.4.2" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version == \"3.10\"" files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, @@ -1554,7 +1793,7 @@ version = "3.5" description = "Python package for creating and manipulating graphs and networks" optional = false python-versions = ">=3.11" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version >= \"3.11\"" files = [ {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"}, @@ -1576,7 +1815,7 @@ version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" -groups = ["main", "tests"] +groups = ["main", "qiboml", "tests"] markers = "python_version == \"3.10\"" files = [ {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, @@ -1642,7 +1881,7 @@ version = "2.3.1" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.11" -groups = ["main", "tests"] +groups = ["main", "qiboml", "tests"] markers = "python_version >= \"3.11\"" files = [ {file = "numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070"}, @@ -1698,13 +1937,234 @@ files = [ {file = "numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b"}, ] +[[package]] +name = "nvidia-cublas-cu12" +version = "12.8.4.1" +description = "CUBLAS native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0"}, + {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142"}, + {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af"}, +] + +[[package]] +name = "nvidia-cuda-cupti-cu12" +version = "12.8.90" +description = "CUDA profiling tools runtime libs." +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed"}, + {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182"}, + {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e"}, +] + +[[package]] +name = "nvidia-cuda-nvrtc-cu12" +version = "12.8.93" +description = "NVRTC native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994"}, + {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8"}, + {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909"}, +] + +[[package]] +name = "nvidia-cuda-runtime-cu12" +version = "12.8.90" +description = "CUDA Runtime native Libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d"}, + {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90"}, + {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8"}, +] + +[[package]] +name = "nvidia-cudnn-cu12" +version = "9.10.2.21" +description = "cuDNN runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8"}, + {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8"}, + {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" + +[[package]] +name = "nvidia-cufft-cu12" +version = "11.3.3.83" +description = "CUFFT native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a"}, + {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74"}, + {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cufile-cu12" +version = "1.13.1.3" +description = "cuFile GPUDirect libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc"}, + {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a"}, +] + +[[package]] +name = "nvidia-curand-cu12" +version = "10.3.9.90" +description = "CURAND native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd"}, + {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9"}, + {file = "nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec"}, +] + +[[package]] +name = "nvidia-cusolver-cu12" +version = "11.7.3.90" +description = "CUDA solver native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0"}, + {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450"}, + {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34"}, +] + +[package.dependencies] +nvidia-cublas-cu12 = "*" +nvidia-cusparse-cu12 = "*" +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparse-cu12" +version = "12.5.8.93" +description = "CUSPARSE native runtime libraries" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc"}, + {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b"}, + {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd"}, +] + +[package.dependencies] +nvidia-nvjitlink-cu12 = "*" + +[[package]] +name = "nvidia-cusparselt-cu12" +version = "0.7.1" +description = "NVIDIA cuSPARSELt" +optional = false +python-versions = "*" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5"}, + {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623"}, + {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075"}, +] + +[[package]] +name = "nvidia-nccl-cu12" +version = "2.27.5" +description = "NVIDIA Collective Communication Library (NCCL) Runtime" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a"}, + {file = "nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457"}, +] + +[[package]] +name = "nvidia-nvjitlink-cu12" +version = "12.8.93" +description = "Nvidia JIT LTO Library" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88"}, + {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7"}, + {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f"}, +] + +[[package]] +name = "nvidia-nvshmem-cu12" +version = "3.4.5" +description = "NVSHMEM creates a global address space that provides efficient and scalable communication for NVIDIA GPU clusters." +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15"}, + {file = "nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd"}, +] + +[[package]] +name = "nvidia-nvtx-cu12" +version = "12.8.90" +description = "NVIDIA Tools Extension" +optional = false +python-versions = ">=3" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615"}, + {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f"}, + {file = "nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e"}, +] + [[package]] name = "openqasm3" version = "1.0.1" description = "Reference OpenQASM AST in Python" optional = false python-versions = "*" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "openqasm3-1.0.1-py3-none-any.whl", hash = "sha256:0d3a1ebe3465e3ea619bcaa369858bba8944cbb0c49604b24f94662d3ec41d41"}, {file = "openqasm3-1.0.1.tar.gz", hash = "sha256:c589dc05d4ced50ca24167d14e0f2c916e717499ba0442e0ff2a3030ef312d0a"}, @@ -1718,13 +2178,25 @@ all = ["antlr4_python3_runtime (>=4.7,<4.14)", "importlib_metadata ; python_vers parser = ["antlr4_python3_runtime (>=4.7,<4.14)", "importlib_metadata ; python_version < \"3.10\""] tests = ["pytest (>=6.0)", "pyyaml"] +[[package]] +name = "opt-einsum" +version = "3.4.0" +description = "Path optimization of einsum functions." +optional = false +python-versions = ">=3.8" +groups = ["main", "qiboml"] +files = [ + {file = "opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd"}, + {file = "opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac"}, +] + [[package]] name = "optuna" version = "4.4.0" description = "A hyperparameter optimization framework" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "optuna-4.4.0-py3-none-any.whl", hash = "sha256:fad8d9c5d5af993ae1280d6ce140aecc031c514a44c3b639d8c8658a8b7920ea"}, {file = "optuna-4.4.0.tar.gz", hash = "sha256:a9029f6a92a1d6c8494a94e45abd8057823b535c2570819072dbcdc06f1c1da4"}, @@ -1752,7 +2224,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "docs", "tests"] +groups = ["main", "docs", "qiboml", "tests"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -2131,7 +2603,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "docs"] +groups = ["main", "docs", "qiboml"] 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"}, @@ -2282,14 +2754,14 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qibo" -version = "0.2.19" +version = "0.2.23" description = "A framework for quantum computing with hardware acceleration." optional = false python-versions = "<3.14,>=3.10" -groups = ["main"] +groups = ["main", "qiboml"] files = [ - {file = "qibo-0.2.19-py3-none-any.whl", hash = "sha256:7d17c77e89cd92be710c8a2e43c9daf24b9131669fef9b56560ec71b2b6036ec"}, - {file = "qibo-0.2.19.tar.gz", hash = "sha256:3f35275024d4877ad19785f479d5f04e859ee02c9eef22aa94205c0d1e8f8fe5"}, + {file = "qibo-0.2.23-py3-none-any.whl", hash = "sha256:1bd93a91ad4f3c23e8cb1db9231665e24921a044bd2f752574af0e36b6c7d860"}, + {file = "qibo-0.2.23.tar.gz", hash = "sha256:7727e583d56589cfd48ef2cfbbd029e5356cf32c4645e6b7e0f1d70f07ccc758"}, ] [package.dependencies] @@ -2304,8 +2776,32 @@ sympy = ">=1.13.1,<2.0.0" tabulate = ">=0.9.0,<0.10.0" [package.extras] +cudaq = ["qbraid[cudaq,qir] (>=0.10.0,<0.11.0)", "qbraid[cudaq] (>=0.10.0,<0.11.0)"] +qir = ["qbraid[cudaq,qir] (>=0.10.0,<0.11.0)", "qbraid[qir] (>=0.10.0,<0.11.0)"] qulacs = ["qulacs (>=0.6.4,<0.7.0) ; python_version < \"3.13\""] +[[package]] +name = "qiboml" +version = "0.1.0" +description = "Quantum Machine Learning using Qibo" +optional = false +python-versions = "<3.14,>=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "qiboml-0.1.0-py3-none-any.whl", hash = "sha256:22f86b6b72b9013c5f6dd0e08328b880cfdc866dc1fe930542a3a76d46159392"}, + {file = "qiboml-0.1.0.tar.gz", hash = "sha256:c2984e64ff0a06f0e268066b5bff826eb4ca5398143abc8669f1804bb0961be5"}, +] + +[package.dependencies] +jax = ">=0.4.25,<0.5.0" +jaxlib = ">=0.4.25,<0.5.0" +numpy = ">=2.0.0,<3.0.0" +qibo = ">=0.2.21,<0.3.0" + +[package.extras] +keras = ["keras (>=3.11.0,<4.0.0)", "tensorflow (>=2.16.1,<3.0.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\""] +torch = ["torch (>=2.7.0,<3.0.0)"] + [[package]] name = "recommonmark" version = "0.7.1" @@ -2495,7 +2991,7 @@ version = "1.15.3" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version == \"3.10\"" files = [ {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, @@ -2560,7 +3056,7 @@ version = "1.16.0" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.11" -groups = ["main"] +groups = ["main", "qiboml"] markers = "python_version >= \"3.11\"" files = [ {file = "scipy-1.16.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:deec06d831b8f6b5fb0b652433be6a09db29e996368ce5911faf673e78d20085"}, @@ -2616,11 +3112,12 @@ version = "80.9.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" -groups = ["docs"] +groups = ["main", "docs", "qiboml"] files = [ {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] +markers = {main = "python_version >= \"3.12\"", qiboml = "python_version >= \"3.12\""} [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] @@ -2883,7 +3380,7 @@ version = "2.0.41" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "SQLAlchemy-2.0.41-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6854175807af57bdb6425e47adbce7d20a4d79bbfd6f6d6519cd10bb7109a7f8"}, {file = "SQLAlchemy-2.0.41-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05132c906066142103b83d9c250b60508af556982a385d96c4eaa9fb9720ac2b"}, @@ -2979,7 +3476,7 @@ version = "1.14.0" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"}, {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"}, @@ -2997,7 +3494,7 @@ version = "0.9.0" description = "Pretty-print tabular data" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, @@ -3031,7 +3528,7 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" -groups = ["main", "docs", "tests"] +groups = ["main", "docs", "qiboml", "tests"] markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, @@ -3080,6 +3577,79 @@ files = [ {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, ] +[[package]] +name = "torch" +version = "2.10.0" +description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" +optional = false +python-versions = ">=3.10" +groups = ["main", "qiboml"] +files = [ + {file = "torch-2.10.0-2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:2b980edd8d7c0a68c4e951ee1856334a43193f98730d97408fbd148c1a933313"}, + {file = "torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f"}, + {file = "torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574"}, + {file = "torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e"}, + {file = "torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d"}, + {file = "torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444"}, + {file = "torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb"}, + {file = "torch-2.10.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:35e407430795c8d3edb07a1d711c41cc1f9eaddc8b2f1cc0a165a6767a8fb73d"}, + {file = "torch-2.10.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:3282d9febd1e4e476630a099692b44fdc214ee9bf8ee5377732d9d9dfe5712e4"}, + {file = "torch-2.10.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a2f9edd8dbc99f62bc4dfb78af7bf89499bca3d753423ac1b4e06592e467b763"}, + {file = "torch-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:29b7009dba4b7a1c960260fc8ac85022c784250af43af9fb0ebafc9883782ebd"}, + {file = "torch-2.10.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:b7bd80f3477b830dd166c707c5b0b82a898e7b16f59a7d9d42778dd058272e8b"}, + {file = "torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf"}, + {file = "torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb"}, + {file = "torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547"}, + {file = "torch-2.10.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:6d3707a61863d1c4d6ebba7be4ca320f42b869ee657e9b2c21c736bf17000294"}, + {file = "torch-2.10.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:5c4d217b14741e40776dd7074d9006fd28b8a97ef5654db959d8635b2fe5f29b"}, + {file = "torch-2.10.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6b71486353fce0f9714ca0c9ef1c850a2ae766b409808acd58e9678a3edb7738"}, + {file = "torch-2.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:c2ee399c644dc92ef7bc0d4f7e74b5360c37cdbe7c5ba11318dda49ffac2bc57"}, + {file = "torch-2.10.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:3202429f58309b9fa96a614885eace4b7995729f44beb54d3e4a47773649d382"}, + {file = "torch-2.10.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:aae1b29cd68e50a9397f5ee897b9c24742e9e306f88a807a27d617f07adb3bd8"}, + {file = "torch-2.10.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6021db85958db2f07ec94e1bc77212721ba4920c12a18dc552d2ae36a3eb163f"}, + {file = "torch-2.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff43db38af76fda183156153983c9a096fc4c78d0cd1e07b14a2314c7f01c2c8"}, + {file = "torch-2.10.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:cdf2a523d699b70d613243211ecaac14fe9c5df8a0b0a9c02add60fb2a413e0f"}, + {file = "torch-2.10.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:bf0d9ff448b0218e0433aeb198805192346c4fd659c852370d5cc245f602a06a"}, + {file = "torch-2.10.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:233aed0659a2503b831d8a67e9da66a62c996204c0bba4f4c442ccc0c68a3f60"}, + {file = "torch-2.10.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:682497e16bdfa6efeec8cde66531bc8d1fbbbb4d8788ec6173c089ed3cc2bfe5"}, + {file = "torch-2.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:6528f13d2a8593a1a412ea07a99812495bec07e9224c28b2a25c0a30c7da025c"}, + {file = "torch-2.10.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f5ab4ba32383061be0fb74bda772d470140a12c1c3b58a0cfbf3dae94d164c28"}, + {file = "torch-2.10.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:716b01a176c2a5659c98f6b01bf868244abdd896526f1c692712ab36dbaf9b63"}, + {file = "torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6"}, + {file = "torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185"}, +] + +[package.dependencies] +cuda-bindings = {version = "12.9.4", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +filelock = "*" +fsspec = ">=0.8.5" +jinja2 = "*" +networkx = ">=2.5.1" +nvidia-cublas-cu12 = {version = "12.8.4.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-cupti-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-nvrtc-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cuda-runtime-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cudnn-cu12 = {version = "9.10.2.21", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufft-cu12 = {version = "11.3.3.83", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cufile-cu12 = {version = "1.13.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-curand-cu12 = {version = "10.3.9.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusolver-cu12 = {version = "11.7.3.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparse-cu12 = {version = "12.5.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-cusparselt-cu12 = {version = "0.7.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nccl-cu12 = {version = "2.27.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvjitlink-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvshmem-cu12 = {version = "3.4.5", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +nvidia-nvtx-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +setuptools = {version = "*", markers = "python_version >= \"3.12\""} +sympy = ">=1.13.3" +triton = {version = "3.6.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} +typing-extensions = ">=4.10.0" + +[package.extras] +opt-einsum = ["opt-einsum (>=3.3)"] +optree = ["optree (>=0.13.0)"] +pyyaml = ["pyyaml"] + [[package]] name = "tornado" version = "6.5.1" @@ -3108,7 +3678,7 @@ version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" -groups = ["main"] +groups = ["main", "qiboml"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -3140,13 +3710,43 @@ 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 = "triton" +version = "3.6.0" +description = "A language and compiler for custom Deep Learning operations" +optional = false +python-versions = "<3.15,>=3.10" +groups = ["main", "qiboml"] +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" +files = [ + {file = "triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781"}, + {file = "triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea"}, + {file = "triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651"}, + {file = "triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3"}, + {file = "triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4"}, + {file = "triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca"}, + {file = "triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd"}, + {file = "triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9"}, + {file = "triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6"}, + {file = "triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f"}, + {file = "triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43"}, + {file = "triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803"}, + {file = "triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d"}, + {file = "triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7"}, +] + +[package.extras] +build = ["cmake (>=3.20,<4.0)", "lit"] +tests = ["autopep8", "isort", "llnl-hatchet", "numpy", "pytest", "pytest-forked", "pytest-xdist", "scipy (>=1.7.1)"] +tutorials = ["matplotlib", "pandas", "tabulate"] + [[package]] name = "typing-extensions" version = "4.14.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main", "docs", "tests"] +groups = ["main", "docs", "qiboml", "tests"] files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, @@ -3183,7 +3783,10 @@ files = [ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] +[extras] +qiboml = ["qiboml", "torch"] + [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "a1a706611bdeec2b916d01171ce9a17d608571d1d7ace7606a77f24df764eed8" +content-hash = "60ecd11b44061d105affe3ba56670e3486214e5ef7fe8eb575b92ef38c3b8eb7" diff --git a/src/qiboopt/__init__.py b/src/qiboopt/__init__.py index c2cdb64..1d4a119 100644 --- a/src/qiboopt/__init__.py +++ b/src/qiboopt/__init__.py @@ -1,5 +1,7 @@ -import importlib.metadata as im +try: + import importlib.metadata as im + __version__ = im.version(__package__) +except Exception: + __version__ = "0.0.1" -__version__ = im.version(__package__) - -from qiboopt import combinatorial, opt_class +from qiboopt import combinatorial, opt_class, continuous_bandits, integrations diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 5d52987..6b2ad18 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -69,7 +69,8 @@ def optimize_qaoa_with_qiboml( # Reuse qiboopt's own QAOA-object construction path for the Hamiltonian. hamiltonian = qubo.qubo_to_qaoa_object().hamiltonian if not hasattr(hamiltonian, "expectation_from_circuit"): - # qiboml 0.1.0 calls this method on SymbolicHamiltonian. + # TODO: remove once minimum required qiboml version is > 0.1.0. + # qiboml 0.1.0 uses `expectation_from_circuit`; older builds expose only `expectation`. hamiltonian.expectation_from_circuit = hamiltonian.expectation circuit_builder = qubo.make_qaoa_circuit_callable( p=p, @@ -95,15 +96,21 @@ def optimize_qaoa_with_qiboml( ) model = model.to(dtype=torch.float64) - optimizer_name = optimizer.lower() - if optimizer_name == "adam": - torch_optimizer = torch.optim.Adam(model.parameters(), lr=lr) - elif optimizer_name == "sgd": - torch_optimizer = torch.optim.SGD(model.parameters(), lr=lr) + if isinstance(optimizer, str): + _OPT_MAP = { + "adam": torch.optim.Adam, + "sgd": torch.optim.SGD, + } + opt_cls = _OPT_MAP.get(optimizer.lower()) + if opt_cls is None: + raise ValueError( + f"Unknown optimizer string '{optimizer}'. " + f"Pass a torch.optim.Optimizer subclass directly, or use one of: {list(_OPT_MAP)}." + ) else: - raise ValueError( - "Unsupported optimizer for qiboml engine. Use 'adam' or 'sgd'." - ) + opt_cls = optimizer # user supplied a class directly + + torch_optimizer = opt_cls(model.parameters(), lr=lr) losses = [] best = float("inf") @@ -123,7 +130,7 @@ def optimize_qaoa_with_qiboml( extra = { "engine": "qiboml", - "optimizer": optimizer_name, + "optimizer": getattr(opt_cls, "__name__", str(opt_cls)), "learning_rate": lr, "epochs": epochs, "loss_history": losses, diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index badbd59..6ed7aff 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -704,10 +704,16 @@ def train_QAOA( differentiation=differentiation, backend=backend, ) - else: - if engine == "qiboml" and not regular_loss: - # qiboml path currently supports expectation-value optimization only. - engine = "legacy" + if engine == "qiboml" and not regular_loss: + import warnings + + warnings.warn( + "engine='qiboml' does not yet support CVaR loss (regular_loss=False). " + "Falling back to engine='legacy'.", + UserWarning, + stacklevel=2, + ) + engine = "legacy" if not regular_loss and not (0 < cvar_delta <= 1): raise_error( ValueError, diff --git a/tests/test_opt_class.py b/tests/test_opt_class.py index 4c8bac9..a301722 100644 --- a/tests/test_opt_class.py +++ b/tests/test_opt_class.py @@ -1,4 +1,5 @@ import itertools +import importlib.util import numpy as np import pytest @@ -16,6 +17,17 @@ ) +def _qiboml_available(): + if importlib.util.find_spec("qiboml") is None: + return False + if importlib.util.find_spec("torch") is None: + return False + return True + + +ENGINES = ["legacy"] + (["qiboml"] if _qiboml_available() else []) + + def test_initialization(): """Test initialization of the QUBO class""" Qdict = {(0, 0): 1.0, (0, 1): 0.5, (1, 1): -1.0} @@ -235,6 +247,17 @@ def test_qubo_to_qaoa_circuit(gammas, betas, alphas): assert circuit.nqubits == qubo.n +def test_qubo_to_qaoa_circuit_without_measurements(): + qubo = QUBO(0, {0: 1, 1: -1}, {(0, 1): 0.5}) + circuit = qubo.qubo_to_qaoa_circuit( + gammas=[0.1, 0.2], + betas=[0.3, 0.4], + include_measurements=False, + ) + assert isinstance(circuit, Circuit) + assert len(circuit.measurements) == 0 + + @pytest.mark.parametrize( "gammas, betas", [ @@ -295,7 +318,8 @@ def test_qubo_to_qaoa_svp_mixer(gammas, betas): ], ) @pytest.mark.parametrize("noise_model", [(True, False)]) -def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model): +@pytest.mark.parametrize("engine", ENGINES) +def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model, engine): h = {0: 1, 1: -1} J = {(0, 1): 0.5} qubo = QUBO(0, h, J) @@ -312,6 +336,8 @@ def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model): regular_loss=reg_loss, cvar_delta=cvar_delta, noise_model=noise_model, + engine=engine, + epochs=5, ) assert isinstance(result[0], float) assert isinstance(result[1], np.ndarray) @@ -319,7 +345,8 @@ def test_train_QAOA(gammas, betas, alphas, reg_loss, cvar_delta, noise_model): assert isinstance(result[4], dict) -def test_train_QAOA_convex_qubo(): +@pytest.mark.parametrize("engine", ENGINES) +def test_train_QAOA_convex_qubo(engine): Qdict = {(0, 0): 2.0, (1, 1): 2.0} @@ -331,7 +358,7 @@ def test_train_QAOA_convex_qubo(): # Train QAOA with 100 iterations. Should be enough to find the minimum. best, params, extra, circuit, freqs = qp.train_QAOA( - gammas, betas, nshots=1000, maxiter=100 + gammas, betas, nshots=1000, maxiter=100, engine=engine, epochs=50 ) # Convert result keys to bitstrings most_freq = max(freqs, key=freqs.get) @@ -360,6 +387,42 @@ def test_train_QAOA_edge_cases(): assert isinstance(result[4], dict) +@pytest.mark.parametrize("nshots", [None, 0]) +@pytest.mark.parametrize("regular_loss", [True, False]) +def test_train_qaoa_exact_mode_returns_probabilities(nshots, regular_loss): + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + kwargs = {} + if not regular_loss: + kwargs["cvar_delta"] = 0.5 + best, params, extra, circuit, stats = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=nshots, + regular_loss=regular_loss, + maxiter=5, + engine="legacy", + **kwargs, + ) + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(circuit, Circuit) + assert isinstance(stats, dict) + assert all(isinstance(value, float) for value in stats.values()) + assert np.isclose(sum(stats.values()), 1.0) + + +def test_train_qaoa_cvar_delta_validation(): + qp = QUBO(0, {(0, 0): 1.0}) + with pytest.raises(ValueError, match="cvar_delta must satisfy 0 < cvar_delta <= 1"): + qp.train_QAOA( + gammas=[0.1], + betas=[0.2], + regular_loss=False, + cvar_delta=0.0, + ) + + def create_svp_mixer(name_to_index, beta): """ Helper function to create a mixer circuit diff --git a/tests/test_tutorial_max_cut_notebook.py b/tests/test_tutorial_max_cut_notebook.py index e34d90b..e8fa74f 100644 --- a/tests/test_tutorial_max_cut_notebook.py +++ b/tests/test_tutorial_max_cut_notebook.py @@ -1,6 +1,7 @@ import json import os import sys +import importlib.util from pathlib import Path import pytest @@ -43,10 +44,29 @@ def _should_skip(code: str) -> bool: return False +def _requires_qiboml(cells: list[str]) -> bool: + markers = ( + 'engine="qiboml"', + "engine='qiboml'", + 'engine = "qiboml"', + "engine = 'qiboml'", + ) + return any(any(marker in code for marker in markers) for code in cells) + + +def _has_working_qiboml_runtime() -> bool: + if importlib.util.find_spec("torch") is None: + return False + if importlib.util.find_spec("qiboml") is None: + return False + return True + + @pytest.mark.parametrize( "notebook_rel", [ Path("tutorial") / "Max-Cut.ipynb", + Path("tutorial") / "Max-Cut-qiboml.ipynb", ], ) def test_execute_notebook_for_coverage(notebook_rel: Path): @@ -63,11 +83,20 @@ def test_execute_notebook_for_coverage(notebook_rel: Path): # Use non-interactive matplotlib backend for headless test envs os.environ.setdefault("MPLBACKEND", "Agg") + cells = _load_notebook_cells(nb_path) + + # qiboml tutorial variants should skip cleanly when optional deps are unavailable. + if _requires_qiboml(cells): + if not _has_working_qiboml_runtime(): + pytest.skip( + "qiboml notebook requires a working qiboml+torch runtime compatible with this qibo install." + ) + # Minimal execution namespace; mimic a fresh notebook kernel ns: dict = {"__name__": "__main__"} # Execute cells, skipping heavy visualization/magic cells - for code in _load_notebook_cells(nb_path): + for code in cells: if _should_skip(code): continue # Some notebooks assume cwd at repo root; enforce it From fa626bb42fe1c51099fc7c46469dddb953029b1d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:18:29 +0000 Subject: [PATCH 15/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qiboopt/__init__.py | 3 ++- tests/test_opt_class.py | 2 +- tests/test_tutorial_max_cut_notebook.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/qiboopt/__init__.py b/src/qiboopt/__init__.py index 1d4a119..e28e789 100644 --- a/src/qiboopt/__init__.py +++ b/src/qiboopt/__init__.py @@ -1,7 +1,8 @@ try: import importlib.metadata as im + __version__ = im.version(__package__) except Exception: __version__ = "0.0.1" -from qiboopt import combinatorial, opt_class, continuous_bandits, integrations +from qiboopt import combinatorial, continuous_bandits, integrations, opt_class diff --git a/tests/test_opt_class.py b/tests/test_opt_class.py index a301722..6c651e9 100644 --- a/tests/test_opt_class.py +++ b/tests/test_opt_class.py @@ -1,5 +1,5 @@ -import itertools import importlib.util +import itertools import numpy as np import pytest diff --git a/tests/test_tutorial_max_cut_notebook.py b/tests/test_tutorial_max_cut_notebook.py index e8fa74f..f37be11 100644 --- a/tests/test_tutorial_max_cut_notebook.py +++ b/tests/test_tutorial_max_cut_notebook.py @@ -1,7 +1,7 @@ +import importlib.util import json import os import sys -import importlib.util from pathlib import Path import pytest From 4be04bd74988a41303381e1564423112d2a26afe Mon Sep 17 00:00:00 2001 From: shangtai Date: Sun, 15 Mar 2026 03:11:59 +0800 Subject: [PATCH 16/28] Update poetry.lock --- poetry.lock | 64 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/poetry.lock b/poetry.lock index b9029c5..b1f6acf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -658,7 +658,6 @@ description = "Python bindings for CUDA" optional = false python-versions = "*" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a022c96b8bd847e8dc0675523431149a4c3e872f440e3002213dbb9e08f0331a"}, {file = "cuda_bindings-12.9.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d3c842c2a4303b2a580fe955018e31aea30278be19795ae05226235268032e5"}, @@ -685,6 +684,7 @@ files = [ {file = "cuda_bindings-12.9.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9866ceec83e39337d1a1d64837864c964ad902992478caa288a0bc1be95f21aa"}, {file = "cuda_bindings-12.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:37744e721a18a514423e81863f52a4f7f46f5a6f9cccd569f2735f8067f4d8c2"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [package.dependencies] cuda-pathfinder = ">=1.1,<2.0" @@ -700,10 +700,10 @@ description = "Pathfinder for CUDA components" optional = false python-versions = ">=3.10" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "cuda_pathfinder-1.3.3-py3-none-any.whl", hash = "sha256:9984b664e404f7c134954a771be8775dfd6180ea1e1aef4a5a37d4be05d9bbb1"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "cycler" @@ -806,6 +806,7 @@ files = [ {file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"}, {file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"}, ] +markers = {main = "extra == \"qiboml\""} [[package]] name = "fonttools" @@ -899,6 +900,7 @@ files = [ {file = "fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437"}, {file = "fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff"}, ] +markers = {main = "extra == \"qiboml\""} [package.extras] abfs = ["adlfs"] @@ -944,7 +946,7 @@ files = [ beautifulsoup4 = "*" pygments = ">=2.7" sphinx = ">=6.0,<9.0" -sphinx-basic-ng = ">=1.0.0.beta2" +sphinx-basic-ng = ">=1.0.0b2" [[package]] name = "greenlet" @@ -1081,6 +1083,7 @@ files = [ {file = "jax-0.4.38-py3-none-any.whl", hash = "sha256:78987306f7041ea8500d99df1a17c33ed92620c2268c4c3677fb24e06712be64"}, {file = "jax-0.4.38.tar.gz", hash = "sha256:43bae65881628319e0a2148e8f81a202fbc2b8d048e35c7cb1df2416672fa4a8"}, ] +markers = {main = "extra == \"qiboml\""} [package.dependencies] jaxlib = "0.4.38" @@ -1134,6 +1137,7 @@ files = [ {file = "jaxlib-0.4.38-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:2ce77ba8cda9259a4bca97afc1c722e4291a6c463a63f8d372c6edc85117d625"}, {file = "jaxlib-0.4.38-cp313-cp313-win_amd64.whl", hash = "sha256:4103db0b3a38a5dc132741237453c24d8547290a22079ba1b577d6c88c95300a"}, ] +markers = {main = "extra == \"qiboml\""} [package.dependencies] ml-dtypes = ">=0.2.0" @@ -1154,6 +1158,7 @@ files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] +markers = {main = "extra == \"qiboml\""} [package.dependencies] MarkupSafe = ">=2.0" @@ -1187,7 +1192,7 @@ files = [ [package.dependencies] attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" +jsonschema-specifications = ">=2023.3.6" referencing = ">=0.28.4" rpds-py = ">=0.7.1" @@ -1634,6 +1639,7 @@ files = [ {file = "ml_dtypes-0.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:3d277bf3637f2a62176f4575512e9ff9ef51d00e39626d9fe4a161992f355af2"}, {file = "ml_dtypes-0.5.4.tar.gz", hash = "sha256:8ab06a50fb9bf9666dd0fe5dfb4676fa2b0ac0f31ecff72a6c3af8e22c063453"}, ] +markers = {main = "extra == \"qiboml\""} [package.dependencies] numpy = [ @@ -1944,12 +1950,12 @@ description = "CUBLAS native runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0"}, {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142"}, {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-cuda-cupti-cu12" @@ -1958,12 +1964,12 @@ description = "CUDA profiling tools runtime libs." optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed"}, {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182"}, {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-cuda-nvrtc-cu12" @@ -1972,12 +1978,12 @@ description = "NVRTC native runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994"}, {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8"}, {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-cuda-runtime-cu12" @@ -1986,12 +1992,12 @@ description = "CUDA Runtime native Libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d"}, {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90"}, {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-cudnn-cu12" @@ -2000,12 +2006,12 @@ description = "cuDNN runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8"}, {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8"}, {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [package.dependencies] nvidia-cublas-cu12 = "*" @@ -2017,12 +2023,12 @@ description = "CUFFT native runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a"}, {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74"}, {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [package.dependencies] nvidia-nvjitlink-cu12 = "*" @@ -2034,11 +2040,11 @@ description = "cuFile GPUDirect libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc"}, {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-curand-cu12" @@ -2047,12 +2053,12 @@ description = "CURAND native runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd"}, {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9"}, {file = "nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-cusolver-cu12" @@ -2061,12 +2067,12 @@ description = "CUDA solver native runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0"}, {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450"}, {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [package.dependencies] nvidia-cublas-cu12 = "*" @@ -2080,12 +2086,12 @@ description = "CUSPARSE native runtime libraries" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc"}, {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b"}, {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [package.dependencies] nvidia-nvjitlink-cu12 = "*" @@ -2097,12 +2103,12 @@ description = "NVIDIA cuSPARSELt" optional = false python-versions = "*" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5"}, {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623"}, {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-nccl-cu12" @@ -2111,11 +2117,11 @@ description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:31432ad4d1fb1004eb0c56203dc9bc2178a1ba69d1d9e02d64a6938ab5e40e7a"}, {file = "nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-nvjitlink-cu12" @@ -2124,12 +2130,12 @@ description = "Nvidia JIT LTO Library" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88"}, {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7"}, {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-nvshmem-cu12" @@ -2138,11 +2144,11 @@ description = "NVSHMEM creates a global address space that provides efficient an optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0b48363fc6964dede448029434c6abed6c5e37f823cb43c3bcde7ecfc0457e15"}, {file = "nvidia_nvshmem_cu12-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:042f2500f24c021db8a06c5eec2539027d57460e1c1a762055a6554f72c369bd"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "nvidia-nvtx-cu12" @@ -2151,12 +2157,12 @@ description = "NVIDIA Tools Extension" optional = false python-versions = ">=3" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615"}, {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f"}, {file = "nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [[package]] name = "openqasm3" @@ -2189,6 +2195,7 @@ files = [ {file = "opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd"}, {file = "opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac"}, ] +markers = {main = "extra == \"qiboml\""} [[package]] name = "optuna" @@ -2398,7 +2405,7 @@ files = [ [package.dependencies] latexcodec = ">=1.0.4" -PyYAML = ">=3.01" +PyYAML = ">=3.1" [package.extras] doc = ["sphinx"] @@ -2791,6 +2798,7 @@ files = [ {file = "qiboml-0.1.0-py3-none-any.whl", hash = "sha256:22f86b6b72b9013c5f6dd0e08328b880cfdc866dc1fe930542a3a76d46159392"}, {file = "qiboml-0.1.0.tar.gz", hash = "sha256:c2984e64ff0a06f0e268066b5bff826eb4ca5398143abc8669f1804bb0961be5"}, ] +markers = {main = "extra == \"qiboml\""} [package.dependencies] jax = ">=0.4.25,<0.5.0" @@ -3117,7 +3125,7 @@ files = [ {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] -markers = {main = "python_version >= \"3.12\"", qiboml = "python_version >= \"3.12\""} +markers = {main = "extra == \"qiboml\" and python_version >= \"3.12\"", qiboml = "python_version >= \"3.12\""} [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] @@ -3589,6 +3597,13 @@ files = [ {file = "torch-2.10.0-2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:418997cb02d0a0f1497cf6a09f63166f9f5df9f3e16c8a716ab76a72127c714f"}, {file = "torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574"}, {file = "torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e"}, + {file = "torch-2.10.0-3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a1ff626b884f8c4e897c4c33782bdacdff842a165fee79817b1dd549fdda1321"}, + {file = "torch-2.10.0-3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac5bdcbb074384c66fa160c15b1ead77839e3fe7ed117d667249afce0acabfac"}, + {file = "torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6"}, + {file = "torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b"}, + {file = "torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49"}, + {file = "torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328"}, + {file = "torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591"}, {file = "torch-2.10.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5276fa790a666ee8becaffff8acb711922252521b28fbce5db7db5cf9cb2026d"}, {file = "torch-2.10.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aaf663927bcd490ae971469a624c322202a2a1e68936eb952535ca4cd3b90444"}, {file = "torch-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:a4be6a2a190b32ff5c8002a0977a25ea60e64f7ba46b1be37093c141d9c49aeb"}, @@ -3618,6 +3633,7 @@ files = [ {file = "torch-2.10.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d8f5912ba938233f86361e891789595ff35ca4b4e2ac8fe3670895e5976731d6"}, {file = "torch-2.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:71283a373f0ee2c89e0f0d5f446039bdabe8dbc3c9ccf35f0f784908b0acd185"}, ] +markers = {main = "extra == \"qiboml\""} [package.dependencies] cuda-bindings = {version = "12.9.4", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} @@ -3717,7 +3733,6 @@ description = "A language and compiler for custom Deep Learning operations" optional = false python-versions = "<3.15,>=3.10" groups = ["main", "qiboml"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\"" files = [ {file = "triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781"}, {file = "triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea"}, @@ -3734,6 +3749,7 @@ files = [ {file = "triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d"}, {file = "triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7"}, ] +markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"qiboml\"", qiboml = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} [package.extras] build = ["cmake (>=3.20,<4.0)", "lit"] @@ -3789,4 +3805,4 @@ qiboml = ["qiboml", "torch"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "60ecd11b44061d105affe3ba56670e3486214e5ef7fe8eb575b92ef38c3b8eb7" +content-hash = "85752f79ebb0c4ad9bd848c17e3e32a501241616c7fd2c65aa6b00503ff610de" From 63087fc267006bd5e6c0beb5305b60946d195e22 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 15 Mar 2026 03:49:55 +0000 Subject: [PATCH 17/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_opt_class.py | 1 - tests/test_tutorial_max_cut_notebook.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/test_opt_class.py b/tests/test_opt_class.py index ce21ad0..6c651e9 100644 --- a/tests/test_opt_class.py +++ b/tests/test_opt_class.py @@ -1,6 +1,5 @@ import importlib.util import itertools -import importlib.util import numpy as np import pytest diff --git a/tests/test_tutorial_max_cut_notebook.py b/tests/test_tutorial_max_cut_notebook.py index 6666cc0..f37be11 100644 --- a/tests/test_tutorial_max_cut_notebook.py +++ b/tests/test_tutorial_max_cut_notebook.py @@ -2,7 +2,6 @@ import json import os import sys -import importlib.util from pathlib import Path import pytest From f0c3180e3bb7ea1dc49e35e5e65827b259ed4522 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Sun, 15 Mar 2026 12:44:31 +0800 Subject: [PATCH 18/28] fixing some lint errors --- src/qiboopt/integrations/qiboml_adapter.py | 25 ++++++++++++---- src/qiboopt/opt_class/opt_class.py | 33 +++++++++++----------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 6b2ad18..8e5ae91 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -2,6 +2,7 @@ from __future__ import annotations +import importlib from typing import Any import numpy as np @@ -16,12 +17,21 @@ def _energy_shift(qubo) -> float: def _get_differentiation_class(name: str | None): if name is None or name == "torch": return None - from qiboml.operations.differentiation import PSR, Adjoint, Jax + + try: + differentiation_module = importlib.import_module( + "qiboml.operations.differentiation" + ) + except ImportError as exc: + raise ImportError( + "engine='qiboml' differentiation backend requires " + "`qiboml.operations.differentiation`." + ) from exc mapping = { - "psr": PSR, - "jax": Jax, - "adjoint": Adjoint, + "psr": differentiation_module.PSR, + "jax": differentiation_module.Jax, + "adjoint": differentiation_module.Adjoint, "torch": None, } diff = mapping.get(name.lower()) @@ -126,7 +136,12 @@ def optimize_qaoa_with_qiboml( losses.append(loss_value) if loss_value < best: best = loss_value - best_params = model.circuit_parameters.detach().cpu().numpy().copy() + current_parameters = model.circuit_parameters + detach_fn = getattr(current_parameters, "detach", None) + if callable(detach_fn): + current_parameters = detach_fn() + current_parameters = current_parameters.cpu().numpy() + best_params = np.asarray(current_parameters, dtype=np.float64).copy() extra = { "engine": "qiboml", diff --git a/src/qiboopt/opt_class/opt_class.py b/src/qiboopt/opt_class/opt_class.py index 6ed7aff..3fcf9e8 100644 --- a/src/qiboopt/opt_class/opt_class.py +++ b/src/qiboopt/opt_class/opt_class.py @@ -689,21 +689,6 @@ def train_QAOA( f"Unsupported engine '{engine}'. Use 'legacy' or 'qiboml'.", ) - if engine == "qiboml" and regular_loss: - best, params, extra = optimize_qaoa_with_qiboml( - qubo=self, - parameters=parameters, - p=p, - nshots=nshots, - noise_model=noise_model, - custom_mixer=custom_mixer, - has_alphas=has_alphas, - optimizer=optimizer, - lr=lr, - epochs=epochs, - differentiation=differentiation, - backend=backend, - ) if engine == "qiboml" and not regular_loss: import warnings @@ -830,10 +815,26 @@ def myloss(parameters, delta=cvar_delta): cvar = sum(energy * prob for energy, prob in selected_energies) / delta return cvar - if engine == "legacy": + if engine == "qiboml": + best, params, extra = optimize_qaoa_with_qiboml( + qubo=self, + parameters=parameters, + p=p, + nshots=nshots, + noise_model=noise_model, + custom_mixer=custom_mixer, + has_alphas=has_alphas, + optimizer=optimizer, + lr=lr, + epochs=epochs, + differentiation=differentiation, + backend=backend, + ) + else: best, params, extra = optimize( myloss, parameters, method=method, options={"maxiter": maxiter} ) + circuit = self.qaoa_circuit_from_parameters( parameters=params, p=p, From 2e25fbb8957a06da5dafde412a310ab0ac1d4b08 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Sun, 15 Mar 2026 22:40:32 +0800 Subject: [PATCH 19/28] fixed import issues --- src/qiboopt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qiboopt/__init__.py b/src/qiboopt/__init__.py index e28e789..91ee101 100644 --- a/src/qiboopt/__init__.py +++ b/src/qiboopt/__init__.py @@ -5,4 +5,4 @@ except Exception: __version__ = "0.0.1" -from qiboopt import combinatorial, continuous_bandits, integrations, opt_class +from . import combinatorial, integrations, opt_class From 0ac3291f34e4298771190d094eac6ea1eff9e0da Mon Sep 17 00:00:00 2001 From: J F Kong Date: Sun, 15 Mar 2026 23:05:02 +0800 Subject: [PATCH 20/28] added tests for coverage --- tests/test_opt_class.py | 50 +++++++ tests/test_package_init.py | 22 +++ tests/test_qiboml_integration.py | 243 +++++++++++++++++++++++++++++++ 3 files changed, 315 insertions(+) create mode 100644 tests/test_package_init.py create mode 100644 tests/test_qiboml_integration.py diff --git a/tests/test_opt_class.py b/tests/test_opt_class.py index 6c651e9..477a063 100644 --- a/tests/test_opt_class.py +++ b/tests/test_opt_class.py @@ -387,6 +387,56 @@ def test_train_QAOA_edge_cases(): assert isinstance(result[4], dict) +def test_train_qaoa_unsupported_engine_raises(): + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + with pytest.raises(ValueError, match="Unsupported engine"): + qp.train_QAOA(gammas=[0.1], betas=[0.2], engine="invalid") + + +def test_train_qaoa_qiboml_cvar_fallback_warns(): + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + with pytest.warns(UserWarning, match="CVaR loss"): + best, params, extra, circuit, stats = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=20, + regular_loss=False, + cvar_delta=0.5, + engine="qiboml", + maxiter=5, + ) + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(circuit, Circuit) + assert isinstance(stats, dict) + + +def test_train_qaoa_with_noise_model_returns_original_circuit(): + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + noise_model = NoiseModel() + noise_model.add(DepolarizingError(0.05)) + + result = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=20, + noise_model=noise_model, + maxiter=5, + engine="legacy", + ) + + assert len(result) == 6 + best, params, extra, circuit, stats, original_circuit = result + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(circuit, Circuit) + assert isinstance(stats, dict) + assert isinstance(original_circuit, Circuit) + assert circuit is not original_circuit + + @pytest.mark.parametrize("nshots", [None, 0]) @pytest.mark.parametrize("regular_loss", [True, False]) def test_train_qaoa_exact_mode_returns_probabilities(nshots, regular_loss): diff --git a/tests/test_package_init.py b/tests/test_package_init.py new file mode 100644 index 0000000..c97ca78 --- /dev/null +++ b/tests/test_package_init.py @@ -0,0 +1,22 @@ +import importlib +import importlib.metadata as im + + +def test_qiboopt_version_fallback(monkeypatch): + import qiboopt + + original_version = qiboopt.__version__ + original_metadata_version = im.version + + monkeypatch.setattr( + im, + "version", + lambda _package: (_ for _ in ()).throw(RuntimeError("missing metadata")), + ) + importlib.reload(qiboopt) + + assert qiboopt.__version__ == "0.0.1" + + monkeypatch.setattr(im, "version", original_metadata_version) + importlib.reload(qiboopt) + assert qiboopt.__version__ == original_version diff --git a/tests/test_qiboml_integration.py b/tests/test_qiboml_integration.py new file mode 100644 index 0000000..780ce35 --- /dev/null +++ b/tests/test_qiboml_integration.py @@ -0,0 +1,243 @@ +import importlib.util +import builtins +from types import SimpleNamespace + +import numpy as np +import pytest + +from qiboopt.integrations.qiboml_adapter import _energy_shift, _get_differentiation_class +from qiboopt.integrations.qiboml_adapter import optimize_qaoa_with_qiboml +from qiboopt.opt_class.opt_class import QUBO + + +def _qiboml_available(): + return importlib.util.find_spec("qiboml") is not None and importlib.util.find_spec("torch") is not None + + +def test_get_differentiation_class_none_and_torch_return_none(): + assert _get_differentiation_class(None) is None + assert _get_differentiation_class("torch") is None + + +def test_get_differentiation_class_import_error(monkeypatch): + def _raise_import_error(_name): + raise ImportError("mocked import failure") + + monkeypatch.setattr("qiboopt.integrations.qiboml_adapter.importlib.import_module", _raise_import_error) + + with pytest.raises(ImportError, match="differentiation backend requires"): + _get_differentiation_class("psr") + + +def test_get_differentiation_class_invalid_value(monkeypatch): + fake_module = SimpleNamespace(PSR=object, Jax=object, Adjoint=object) + monkeypatch.setattr( + "qiboopt.integrations.qiboml_adapter.importlib.import_module", + lambda _name: fake_module, + ) + + with pytest.raises(ValueError, match="Unknown qiboml differentiation method"): + _get_differentiation_class("invalid") + + +def test_get_differentiation_class_valid_value(monkeypatch): + fake_psr = object() + fake_module = SimpleNamespace(PSR=fake_psr, Jax=object, Adjoint=object) + monkeypatch.setattr( + "qiboopt.integrations.qiboml_adapter.importlib.import_module", + lambda _name: fake_module, + ) + assert _get_differentiation_class("psr") is fake_psr + + +def test_energy_shift_matches_qubo_to_ising_constant(): + qp = QUBO(0, {(0, 0): 2.0, (0, 1): 1.5, (1, 1): -0.5}) + _h, _J, constant = qp.qubo_to_ising() + assert _energy_shift(qp) == float(constant) + + +def test_optimize_qaoa_with_qiboml_raises_when_torch_missing(monkeypatch): + original_import = builtins.__import__ + + def _mocked_import(name, globals=None, locals=None, fromlist=(), level=0): + if name == "torch": + raise ImportError("mocked missing torch") + return original_import(name, globals, locals, fromlist, level) + + monkeypatch.setattr(builtins, "__import__", _mocked_import) + + with pytest.raises(ImportError, match="requires torch"): + optimize_qaoa_with_qiboml( + qubo=None, + parameters=[0.1, 0.2], + p=1, + nshots=10, + noise_model=None, + custom_mixer=None, + has_alphas=False, + optimizer="adam", + lr=0.05, + epochs=1, + differentiation=None, + backend=None, + ) + + +def test_optimize_qaoa_with_qiboml_raises_when_qiboml_missing(monkeypatch): + original_import = builtins.__import__ + + def _mocked_import(name, globals=None, locals=None, fromlist=(), level=0): + if name.startswith("qiboml"): + raise ImportError("mocked missing qiboml") + return original_import(name, globals, locals, fromlist, level) + + monkeypatch.setattr(builtins, "__import__", _mocked_import) + + with pytest.raises(ImportError, match="requires qiboml"): + optimize_qaoa_with_qiboml( + qubo=None, + parameters=[0.1, 0.2], + p=1, + nshots=10, + noise_model=None, + custom_mixer=None, + has_alphas=False, + optimizer="adam", + lr=0.05, + epochs=1, + differentiation=None, + backend=None, + ) + + +def test_legacy_engine_does_not_require_qiboml(monkeypatch): + import qiboopt.opt_class.opt_class as opt_module + + def _should_not_be_called(**kwargs): + raise AssertionError("qiboml adapter should not be called for legacy engine") + + monkeypatch.setattr(opt_module, "optimize_qaoa_with_qiboml", _should_not_be_called) + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + best, params, extra, circuit, freqs = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.3, 0.4], + nshots=100, + maxiter=5, + engine="legacy", + ) + assert isinstance(best, float) + assert isinstance(params, np.ndarray) + assert isinstance(freqs, dict) + + +@pytest.mark.skipif(not _qiboml_available(), reason="qiboml/torch not installed") +def test_qiboml_engine_output_contract(): + qp = QUBO(0, {(0, 0): 2.0, (1, 1): 2.0}) + best, params, extra, circuit, freqs = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=200, + engine="qiboml", + optimizer="adam", + lr=0.05, + epochs=20, + ) + assert isinstance(best, float) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(freqs, dict) + assert extra.get("engine") == "qiboml" + assert "loss_history" in extra + + +@pytest.mark.skipif(not _qiboml_available(), reason="qiboml/torch not installed") +def test_qiboml_engine_updates_parameters(): + qp = QUBO(0, {(0, 0): 2.0, (1, 1): 2.0}) + gammas = [0.1, 0.2] + betas = [0.3, 0.4] + init_params = np.array(gammas + betas, dtype=float) + best, params, extra, circuit, freqs = qp.train_QAOA( + gammas=gammas, + betas=betas, + nshots=200, + engine="qiboml", + optimizer="adam", + lr=0.05, + epochs=30, + ) + assert np.linalg.norm(params - init_params) > 0 + assert np.isfinite(best) + + +@pytest.mark.skipif(not _qiboml_available(), reason="qiboml/torch not installed") +def test_qiboml_engine_supports_exact_mode(): + qp = QUBO(0, {(0, 0): 1.0, (1, 1): 1.0}) + best, params, extra, circuit, stats = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=None, + engine="qiboml", + optimizer="adam", + lr=0.05, + epochs=10, + ) + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(stats, dict) + assert all(isinstance(value, float) for value in stats.values()) + assert np.isclose(sum(stats.values()), 1.0) + + +@pytest.mark.skipif(not _qiboml_available(), reason="qiboml/torch not installed") +def test_qiboml_engine_supports_sgd_optimizer(): + qp = QUBO(0, {(0, 0): 2.0, (1, 1): 2.0}) + best, params, extra, circuit, freqs = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=150, + engine="qiboml", + optimizer="sgd", + lr=0.05, + epochs=15, + ) + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(freqs, dict) + assert extra.get("optimizer") == "SGD" + + +@pytest.mark.skipif(not _qiboml_available(), reason="qiboml/torch not installed") +def test_qiboml_engine_unknown_optimizer_raises(): + qp = QUBO(0, {(0, 0): 2.0, (1, 1): 2.0}) + with pytest.raises(ValueError, match="Unknown optimizer string"): + qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=100, + engine="qiboml", + optimizer="rmsprop", + epochs=5, + ) + + +@pytest.mark.skipif(not _qiboml_available(), reason="qiboml/torch not installed") +def test_qiboml_engine_accepts_optimizer_class(): + import torch + + qp = QUBO(0, {(0, 0): 2.0, (1, 1): 2.0}) + best, params, extra, circuit, freqs = qp.train_QAOA( + gammas=[0.1, 0.2], + betas=[0.2, 0.3], + nshots=100, + engine="qiboml", + optimizer=torch.optim.SGD, + lr=0.05, + epochs=5, + ) + assert np.isfinite(best) + assert isinstance(params, np.ndarray) + assert isinstance(extra, dict) + assert isinstance(freqs, dict) + assert extra.get("optimizer") == "SGD" From 168ebcd5422b12501a85d76c5b9817ce41ad324b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:05:28 +0000 Subject: [PATCH 21/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_qiboml_integration.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/test_qiboml_integration.py b/tests/test_qiboml_integration.py index 780ce35..7b17e73 100644 --- a/tests/test_qiboml_integration.py +++ b/tests/test_qiboml_integration.py @@ -1,17 +1,23 @@ -import importlib.util import builtins +import importlib.util from types import SimpleNamespace import numpy as np import pytest -from qiboopt.integrations.qiboml_adapter import _energy_shift, _get_differentiation_class -from qiboopt.integrations.qiboml_adapter import optimize_qaoa_with_qiboml +from qiboopt.integrations.qiboml_adapter import ( + _energy_shift, + _get_differentiation_class, + optimize_qaoa_with_qiboml, +) from qiboopt.opt_class.opt_class import QUBO def _qiboml_available(): - return importlib.util.find_spec("qiboml") is not None and importlib.util.find_spec("torch") is not None + return ( + importlib.util.find_spec("qiboml") is not None + and importlib.util.find_spec("torch") is not None + ) def test_get_differentiation_class_none_and_torch_return_none(): @@ -23,7 +29,10 @@ def test_get_differentiation_class_import_error(monkeypatch): def _raise_import_error(_name): raise ImportError("mocked import failure") - monkeypatch.setattr("qiboopt.integrations.qiboml_adapter.importlib.import_module", _raise_import_error) + monkeypatch.setattr( + "qiboopt.integrations.qiboml_adapter.importlib.import_module", + _raise_import_error, + ) with pytest.raises(ImportError, match="differentiation backend requires"): _get_differentiation_class("psr") From 3a6358513e6fa086e123c6e84cb1837f2d5fda4b Mon Sep 17 00:00:00 2001 From: J F Kong Date: Sun, 15 Mar 2026 23:42:03 +0800 Subject: [PATCH 22/28] fixed test errors --- tests/test_qiboml_integration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_qiboml_integration.py b/tests/test_qiboml_integration.py index 780ce35..d172dfa 100644 --- a/tests/test_qiboml_integration.py +++ b/tests/test_qiboml_integration.py @@ -85,10 +85,13 @@ def _mocked_import(name, globals=None, locals=None, fromlist=(), level=0): def test_optimize_qaoa_with_qiboml_raises_when_qiboml_missing(monkeypatch): original_import = builtins.__import__ + fake_torch = SimpleNamespace() def _mocked_import(name, globals=None, locals=None, fromlist=(), level=0): if name.startswith("qiboml"): raise ImportError("mocked missing qiboml") + if name == "torch": + return fake_torch return original_import(name, globals, locals, fromlist, level) monkeypatch.setattr(builtins, "__import__", _mocked_import) From c3366f9d2d68a48dce8e767a529461b75d887359 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Mon, 16 Mar 2026 00:10:08 +0800 Subject: [PATCH 23/28] chore: rerun CI From bd715eadec5712d7143a1cdcbaca60d769cc2c8e Mon Sep 17 00:00:00 2001 From: J F Kong Date: Mon, 16 Mar 2026 01:02:53 +0800 Subject: [PATCH 24/28] install qiboml in ubuntu tests --- .github/workflows/rules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rules.yml b/.github/workflows/rules.yml index a255098..dd9b194 100644 --- a/.github/workflows/rules.yml +++ b/.github/workflows/rules.yml @@ -20,5 +20,5 @@ jobs: os: ${{ matrix.os }} python-version: ${{ matrix.python-version }} doctests: ${{ matrix.os == 'ubuntu-latest'}} - poetry-extras: "--with tests,docs" + poetry-extras: ${{ matrix.os == 'ubuntu-latest' && '--with tests,docs,qiboml' || '--with tests,docs' }} secrets: inherit From 3007ed51a5b3c09e7d0e3cce7fcad1dd4b233919 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Mon, 16 Mar 2026 01:18:39 +0800 Subject: [PATCH 25/28] fix pylint errors with detach --- src/qiboopt/integrations/qiboml_adapter.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 8e5ae91..a215650 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -137,10 +137,8 @@ def optimize_qaoa_with_qiboml( if loss_value < best: best = loss_value current_parameters = model.circuit_parameters - detach_fn = getattr(current_parameters, "detach", None) - if callable(detach_fn): - current_parameters = detach_fn() - current_parameters = current_parameters.cpu().numpy() + if isinstance(current_parameters, torch.Tensor): + current_parameters = current_parameters.detach().cpu().numpy() # pylint: disable=not-callable best_params = np.asarray(current_parameters, dtype=np.float64).copy() extra = { From 03181dc90f25ac7d7ea4ca974e2c367173bb5dca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 15 Mar 2026 17:20:23 +0000 Subject: [PATCH 26/28] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/qiboopt/integrations/qiboml_adapter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index a215650..0641858 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -138,7 +138,9 @@ def optimize_qaoa_with_qiboml( best = loss_value current_parameters = model.circuit_parameters if isinstance(current_parameters, torch.Tensor): - current_parameters = current_parameters.detach().cpu().numpy() # pylint: disable=not-callable + current_parameters = ( + current_parameters.detach().cpu().numpy() + ) # pylint: disable=not-callable best_params = np.asarray(current_parameters, dtype=np.float64).copy() extra = { From 42efee7951ac3ec54e7845ec5688c44570cfeef8 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Mon, 16 Mar 2026 08:06:31 +0800 Subject: [PATCH 27/28] trying to fix the lint errors related to detach --- poetry.lock | 164 ++++++++++++++------- pyproject.toml | 1 + src/qiboopt/integrations/qiboml_adapter.py | 3 +- 3 files changed, 111 insertions(+), 57 deletions(-) diff --git a/poetry.lock b/poetry.lock index b1f6acf..0d563d1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2249,6 +2249,18 @@ files = [ {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, ] +[[package]] +name = "pastel" +version = "0.2.1" +description = "Bring colors to your terminal." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["tests"] +files = [ + {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, + {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, +] + [[package]] name = "pillow" version = "12.0.0" @@ -2391,6 +2403,26 @@ files = [ dev = ["pre-commit", "tox"] testing = ["coverage", "pytest", "pytest-benchmark"] +[[package]] +name = "poethepoet" +version = "0.42.1" +description = "A task runner that works well with poetry and uv." +optional = false +python-versions = ">=3.10" +groups = ["tests"] +files = [ + {file = "poethepoet-0.42.1-py3-none-any.whl", hash = "sha256:d8d1345a5ca521be9255e7c13bc2c4c8698ed5e5ac5e9e94890d239fcd423d0a"}, + {file = "poethepoet-0.42.1.tar.gz", hash = "sha256:205747e276062c2aaba8afd8a98838f8a3a0237b7ab94715fab8d82718aac14f"}, +] + +[package.dependencies] +pastel = ">=0.2.1,<0.3.0" +pyyaml = ">=6.0.3,<7.0" +tomli = {version = ">=1.3.0", markers = "python_version < \"3.11\""} + +[package.extras] +poetry-plugin = ["poetry (>=1.2.0,<3.0.0) ; python_version < \"4.0\""] + [[package]] name = "pybtex" version = "0.25.0" @@ -2606,65 +2638,85 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main", "docs", "qiboml"] +groups = ["main", "docs", "qiboml", "tests"] 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"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] @@ -3805,4 +3857,4 @@ qiboml = ["qiboml", "torch"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "85752f79ebb0c4ad9bd848c17e3e32a501241616c7fd2c65aa6b00503ff610de" +content-hash = "3fcf926b1c0cf00d701d6cf30d06c47c1c221c078fa3b59c7717e1ba039427b0" diff --git a/pyproject.toml b/pyproject.toml index 700876d..4925afa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ pytest-cov = "^4.0.0" pytest-env = ">=0.8.1" pylint = "^3.3.5" matplotlib = "^3.9.2" +poethepoet = "^0.42.1" [tool.poetry.group.qiboml] optional = true diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index a215650..f907f43 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -138,7 +138,8 @@ def optimize_qaoa_with_qiboml( best = loss_value current_parameters = model.circuit_parameters if isinstance(current_parameters, torch.Tensor): - current_parameters = current_parameters.detach().cpu().numpy() # pylint: disable=not-callable + # pylint: disable-next=not-callable + current_parameters = current_parameters.detach().cpu().numpy() best_params = np.asarray(current_parameters, dtype=np.float64).copy() extra = { From ebf0682d7766503e6d4c408ce23744d3c14458f3 Mon Sep 17 00:00:00 2001 From: J F Kong Date: Mon, 16 Mar 2026 08:09:24 +0800 Subject: [PATCH 28/28] trying to fix linting errors for detach --- src/qiboopt/integrations/qiboml_adapter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qiboopt/integrations/qiboml_adapter.py b/src/qiboopt/integrations/qiboml_adapter.py index 709165a..f907f43 100644 --- a/src/qiboopt/integrations/qiboml_adapter.py +++ b/src/qiboopt/integrations/qiboml_adapter.py @@ -138,7 +138,6 @@ def optimize_qaoa_with_qiboml( best = loss_value current_parameters = model.circuit_parameters if isinstance(current_parameters, torch.Tensor): -<<<<<<< HEAD # pylint: disable-next=not-callable current_parameters = current_parameters.detach().cpu().numpy() best_params = np.asarray(current_parameters, dtype=np.float64).copy()