diff --git a/qsplit/adapters/ibm/__ibm_pce.py b/qsplit/adapters/ibm/__ibm_pce.py new file mode 100644 index 0000000..e9ed1bb --- /dev/null +++ b/qsplit/adapters/ibm/__ibm_pce.py @@ -0,0 +1,200 @@ +# Copyright (C) 2025 The QSplit Contributors. +# See the 'CONTRIBUTORS' file at the top-level directory of this distribution. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from itertools import combinations +from math import comb + +import numpy as np +import pandas as pd +from qiskit import QuantumCircuit, generate_preset_pass_manager +from qiskit.circuit.library import qaoa_ansatz +from qiskit.passmanager import BasePassManager +from qiskit.quantum_info import SparsePauliOp +from qiskit_ibm_runtime import EstimatorV2 +from scipy.optimize import minimize + +from qsplit.adapters.ibm.util import get_variables_mapping, to_dataframe +from qsplit.qubo import QUBO + + +def ibm_solve(qubo: QUBO, backend) -> pd.DataFrame: + var_to_qubit, all_vars = get_variables_mapping(qubo) + pm = generate_preset_pass_manager(backend=backend, optimization_level=2) + quantum_results = __run_quantum_optimizer(var_to_qubit, all_vars, qubo, backend, pm, k=3) + return to_dataframe(quantum_results, qubo, var_to_qubit, all_vars) + + +def __build_pce(pauli: str, node_list: list, n_qubits: int, k: int) -> list[SparsePauliOp]: + pauli_correlation_encoding = [] + for idx, c in enumerate(combinations(range(n_qubits), k)): + if idx >= len(node_list): + break + paulis = ["I"] * n_qubits + for qubit_idx in c: + paulis[qubit_idx] = pauli + pauli_correlation_encoding.append(("".join(paulis)[::-1], 1.0)) + + hamiltonians = [] + for p_str, weight in pauli_correlation_encoding: + hamiltonians.append(SparsePauliOp.from_list([(p_str, weight)])) + return hamiltonians + + +def __pce_loss( + x: list[float], + ansatz: QuantumCircuit, + hamiltonians: list, + estimator, + J_prime: dict, + num_nodes: int, + num_qubits: int, +) -> dict[str, float | dict]: + job = estimator.run([(ansatz, hamiltonians[0], x), (ansatz, hamiltonians[1], x), (ansatz, hamiltonians[2], x)]) + result = job.result() + + node_exp_map = {} + idx = 0 + for r in result: + for ev in r.data.evs: + node_exp_map[idx] = ev + idx += 1 + + loss_val = 0 + alpha = num_qubits + + for (edge0, edge1), weight in J_prime.items(): + loss_val += weight * np.tanh(alpha * node_exp_map[edge0]) * np.tanh(alpha * node_exp_map[edge1]) + + regulation_term = 0 + for i in range(num_nodes): + regulation_term += np.tanh(alpha * node_exp_map[i]) ** 2 + regulation_term = (regulation_term / num_nodes) ** 2 + + beta = 1 / 2 + v = len(J_prime) / 2 + (num_nodes - 1) / 4 + regulation_term = beta * v * regulation_term + + loss_val += regulation_term + + return {"loss": loss_val, "exp_map": node_exp_map} + + +def __run_quantum_optimizer( + var_to_qubit, all_vars, qubo: QUBO, backend, pm: BasePassManager, k: int = 3 +) -> dict[int, int]: + n = len(all_vars) + Q = np.zeros((n, n)) + row_indices = [var_to_qubit[r] for r in qubo.rows_idx] + col_indices = [var_to_qubit[c] for c in qubo.cols_idx] + Q[np.ix_(row_indices, col_indices)] = qubo.mat + J_prime = {} + + u_idx, v_idx = np.triu_indices(n, k=1) + for u, v in zip(u_idx, v_idx): + if Q[u, v] != 0: + J_prime[(u, v)] = Q[u, v] / 4.0 + + diag_Q = np.diag(Q) + sum_rows_cols = np.sum(Q, axis=1) + np.sum(Q, axis=0) - 2 * diag_Q + h = -diag_Q / 2.0 - sum_rows_cols / 4.0 + + dummy_index = n + for u, val in enumerate(h): + if val != 0: + J_prime[(u, dummy_index)] = val + + num_nodes = n + 1 + q = k + while 3 * comb(q, k) < num_nodes: + q += 1 + num_qubits = q + + list_size = num_nodes // 3 + remainder = num_nodes % 3 + nodes = list(range(num_nodes)) + split_1 = list_size + (1 if remainder > 0 else 0) + split_2 = split_1 + list_size + (1 if remainder > 1 else 0) + + node_x = nodes[:split_1] + node_y = nodes[split_1:split_2] + node_z = nodes[split_2:] + + pce_x = __build_pce("X", node_x, num_qubits, k) + pce_y = __build_pce("Y", node_y, num_qubits, k) + pce_z = __build_pce("Z", node_z, num_qubits, k) + + cost_ops = [] + for i in range(num_qubits - 1): + paulis = ["I"] * num_qubits + paulis[i] = "Z" + paulis[i + 1] = "Z" + cost_ops.append(("".join(paulis)[::-1], 1.0)) + + base_cost_op = SparsePauliOp.from_list(cost_ops) + reps = 3 + qc = qaoa_ansatz(cost_operator=base_cost_op, reps=reps) + qc = pm.run(qc) + + pce_mapped = [ + [op.apply_layout(qc.layout) if getattr(qc, "layout", None) else op for op in pce_x], + [op.apply_layout(qc.layout) if getattr(qc, "layout", None) else op for op in pce_y], + [op.apply_layout(qc.layout) if getattr(qc, "layout", None) else op for op in pce_z], + ] + + estimator = EstimatorV2(mode=backend) + exp_result = [] + + def loss_wrapper(x_params): + exp = __pce_loss(x_params, qc, pce_mapped, estimator, J_prime, num_nodes, num_qubits) + exp_result.append(exp) + return exp["loss"] + + delta_t = 0.25 + gamma_list = [(i / reps) * delta_t for i in range(1, reps + 1)] + beta_list = [(1 - (i / reps)) * delta_t for i in range(1, reps + 1)] + initial_params = beta_list + gamma_list + + minimize( + loss_wrapper, + initial_params, + method="COBYLA", + options={"rhobeg": 1.0, "maxiter": len(initial_params) + 2}, + tol=1e-4, + ) + + best_exp_map = min(exp_result, key=lambda val: val["loss"])["exp_map"] + best_exp_arr = np.array([best_exp_map[idx] for idx in range(num_nodes)]) + x_raw = np.where(best_exp_arr >= 0, 1, -1) + x_dummy = x_raw[dummy_index] + x = ((1 - (x_raw[:n] * x_dummy)) // 2).astype(int) + H = Q @ x + x @ Q - 2 * diag_Q * x + + improved = True + while improved: + improved = False + for u in range(n): + delta_z = 1 - 2 * x[u] + delta_E = (Q[u, u] + H[u]) * delta_z + if delta_E < -1e-6: + x[u] = 1 - x[u] + improved = True + H += (Q[u, :] + Q[:, u]) * delta_z + H[u] -= 2 * Q[u, u] * delta_z + + powers_of_two = 2 ** np.arange(n) + state_int = int(np.dot(x, powers_of_two)) + + return {state_int: 1} diff --git a/qsplit/adapters/ibm/__ibm_qaoa.py b/qsplit/adapters/ibm/__ibm_qaoa.py index 8c6e758..71ced13 100644 --- a/qsplit/adapters/ibm/__ibm_qaoa.py +++ b/qsplit/adapters/ibm/__ibm_qaoa.py @@ -17,7 +17,8 @@ import pandas as pd from qiskit import generate_preset_pass_manager -from qsplit.adapters.ibm.util import get_qaoa_circuit_optimized, run_quantum_optimizer, to_dataframe +from qsplit.adapters.ibm.util import to_dataframe +from qsplit.adapters.ibm.util_qaoa import get_qaoa_circuit_optimized, run_quantum_optimizer from qsplit.qubo import QUBO diff --git a/qsplit/adapters/ibm/ibm_default.py b/qsplit/adapters/ibm/ibm_default.py new file mode 100644 index 0000000..8f4202b --- /dev/null +++ b/qsplit/adapters/ibm/ibm_default.py @@ -0,0 +1,25 @@ +# Copyright (C) 2025 The QSplit Contributors. +# See the 'CONTRIBUTORS' file at the top-level directory of this distribution. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import pandas as pd +from qiskit_aer import AerSimulator + +from qsplit.adapters.ibm.__ibm_pce import ibm_solve +from qsplit.qubo import QUBO + + +def solve(qubo: QUBO) -> pd.DataFrame: + return ibm_solve(qubo, AerSimulator()) diff --git a/qsplit/adapters/ibm/ibm_pce_cpu_noiseless.py b/qsplit/adapters/ibm/ibm_pce_cpu_noiseless.py new file mode 100644 index 0000000..f3e9d84 --- /dev/null +++ b/qsplit/adapters/ibm/ibm_pce_cpu_noiseless.py @@ -0,0 +1,25 @@ +# Copyright (C) 2025 The QSplit Contributors. +# See the 'CONTRIBUTORS' file at the top-level directory of this distribution. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import pandas as pd +from qiskit_aer import AerSimulator + +from qsplit.adapters.ibm.__ibm_pce import ibm_pce +from qsplit.qubo import QUBO + + +def solve(qubo: QUBO) -> pd.DataFrame: + return ibm_pce(qubo, AerSimulator()) diff --git a/qsplit/adapters/ibm/util.py b/qsplit/adapters/ibm/util.py index ad51476..b2d5491 100644 --- a/qsplit/adapters/ibm/util.py +++ b/qsplit/adapters/ibm/util.py @@ -14,228 +14,56 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Acknowledgement: -# Parts of this code are adapted from the official IBM Quantum documentation -# regarding the Quantum Approximate Optimization Algorithm (QAOA). -# Source: https://quantum.cloud.ibm.com/docs/en/tutorials/quantum-approximate-optimization-algorithm -# Modifications have been made to tailor the implementation to local requirements. - import numpy as np import pandas as pd -from qiskit import QuantumCircuit, generate_preset_pass_manager -from qiskit.circuit.library import QAOAAnsatz -from qiskit.passmanager import BasePassManager -from qiskit.primitives import BackendEstimatorV2, BackendSamplerV2 -from qiskit.quantum_info import SparsePauliOp -from qiskit.transpiler.exceptions import TranspilerError -from qiskit_aer import AerSimulator -from qiskit_algorithms.optimizers import SPSA -from qiskit_ibm_runtime import IBMBackend from qsplit.qubo import QUBO -try: - from qiskit_aer import AerSimulator -except Exception: - AerSimulator = None - -try: - from qiskit_ibm_runtime import EstimatorV2 as RuntimeEstimatorV2 - from qiskit_ibm_runtime import IBMBackend - from qiskit_ibm_runtime import SamplerV2 as RuntimeSamplerV2 -except Exception: - RuntimeEstimatorV2 = None - RuntimeSamplerV2 = None - IBMBackend = None - -try: - from qiskit.primitives import StatevectorEstimator -except Exception: - StatevectorEstimator = None - -def __get_variables_mapping(qubo: QUBO) -> tuple[dict[int, int], list[int]]: +def get_variables_mapping(qubo: QUBO) -> tuple[dict[int, int], list[int]]: all_vars = sorted(list(set(qubo.rows_idx) | set(qubo.cols_idx))) var_to_qubit = {var: i for i, var in enumerate(all_vars)} return var_to_qubit, all_vars -def __from_qubo_matrix_to_circuit(qubo: QUBO) -> tuple[QuantumCircuit, SparsePauliOp, dict[int, int], list[int]]: - var_to_qubit, all_vars = __get_variables_mapping(qubo) - num_qubits = len(all_vars) - - pauli_list = [] - - for i, row_var in enumerate(qubo.rows_idx): - for j, col_var in enumerate(qubo.cols_idx): - coeff = qubo.mat[i, j] - if coeff == 0: - continue - - if row_var == col_var: - pauli_list.append(("Z", [var_to_qubit[row_var]], coeff)) - else: - pauli_list.append(("ZZ", [var_to_qubit[row_var], var_to_qubit[col_var]], coeff)) - - if qubo.offset != 0: - pauli_list.append(("I" * num_qubits, list(range(num_qubits)), qubo.offset)) - - cost_hamiltonian = SparsePauliOp.from_sparse_list(pauli_list, num_qubits) - cost_hamiltonian = cost_hamiltonian.simplify() - - circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2) - - return circuit, cost_hamiltonian, var_to_qubit, all_vars - - -__objective_func_vals = [] - - -def _is_ibm_backend(backend) -> bool: - return IBMBackend is not None and isinstance(backend, IBMBackend) - - -def _is_aer_backend(backend) -> bool: - return AerSimulator is not None and isinstance(backend, AerSimulator) - - -def __optimize_circuit( - backend, - candidate_circuit: QuantumCircuit, - cost_hamiltonian: SparsePauliOp, - optimize_on_backend: bool = True, -) -> QuantumCircuit: - initial_gamma = np.pi - initial_beta = np.pi / 2 - init_params = [initial_beta, initial_beta, initial_gamma, initial_gamma] - if not optimize_on_backend: - if StatevectorEstimator is not None: - estimator = StatevectorEstimator() - elif AerSimulator is not None: - estimator = BackendEstimatorV2( - backend=AerSimulator(method="matrix_product_state", matrix_product_state_max_bond_dimension=None) - ) - else: - raise RuntimeError("Local estimator backend is required when optimize_on_backend=False.") - elif _is_ibm_backend(backend) and RuntimeEstimatorV2 is not None: - estimator = RuntimeEstimatorV2(backend) - else: - estimator = BackendEstimatorV2(backend=backend) - if optimize_on_backend and _is_ibm_backend(backend) and hasattr(estimator, "options"): - if hasattr(estimator.options, "default_shots"): - estimator.options.default_shots = 500 - estimator.options.dynamical_decoupling.enable = True - estimator.options.dynamical_decoupling.sequence_type = "XY4" - estimator.options.twirling.enable_gates = True - estimator.options.twirling.num_randomizations = "auto" - - def objective_function(params: list[float]) -> float: - return __cost_func_estimator(params, candidate_circuit, cost_hamiltonian, estimator) - - optimizer = SPSA() - result = optimizer.minimize(fun=objective_function, x0=init_params) - optimized_circuit = candidate_circuit.assign_parameters(result.x) - return optimized_circuit - - -def __cost_func_estimator( - params: list[float], ansatz: QuantumCircuit, hamiltonian: SparsePauliOp, estimator: object -) -> float: - layout = getattr(ansatz, "layout", None) - isa_hamiltonian = hamiltonian.apply_layout(layout) if layout is not None else hamiltonian - pub = (ansatz, isa_hamiltonian, params) - job = estimator.run([pub]) - results = job.result()[0] - cost = results.data.evs - __objective_func_vals.append(cost) - return cost - - -def get_qaoa_circuit_optimized( - backend, - pm: BasePassManager, - qubo: QUBO, - *, - optimize_on_backend: bool = True, -) -> tuple[QuantumCircuit, dict[int, int], list[int]]: - circuit, cost_hamiltonian, var_to_qubit, all_vars = __from_qubo_matrix_to_circuit(qubo) - if optimize_on_backend: - try: - candidate_circuit = pm.run(circuit) - except TranspilerError as exc: - if _is_aer_backend(backend) and "not in Target" in str(exc): - try: - candidate_circuit = generate_preset_pass_manager(optimization_level=1).run(circuit) - except TranspilerError: - candidate_circuit = circuit.decompose(reps=10) - else: - raise - if _is_aer_backend(backend) and any( - str(inst.operation.name).lower() == "qaoa" for inst in candidate_circuit.data - ): - candidate_circuit = candidate_circuit.decompose(reps=10) - optimized_circ = __optimize_circuit(backend, candidate_circuit, cost_hamiltonian, optimize_on_backend=True) - else: - optimized_logical = __optimize_circuit(backend, circuit, cost_hamiltonian, optimize_on_backend=False) - try: - optimized_circ = pm.run(optimized_logical) - except TranspilerError: - optimized_circ = optimized_logical.decompose(reps=10) - measured_circ = optimized_circ.copy() - if measured_circ.num_clbits == 0: - measured_circ.measure_all() - return measured_circ, var_to_qubit, all_vars - - -def run_quantum_optimizer(backend, optimized_circuit: QuantumCircuit) -> dict[int, int]: - if _is_ibm_backend(backend) and RuntimeSamplerV2 is not None: - sampler = RuntimeSamplerV2(mode=backend) - else: - sampler = BackendSamplerV2(backend=backend) - if _is_ibm_backend(backend) and hasattr(sampler, "options"): - sampler.options.dynamical_decoupling.enable = True - sampler.options.dynamical_decoupling.sequence_type = "XY4" - sampler.options.twirling.enable_gates = True - sampler.options.twirling.num_randomizations = "auto" - pub = (optimized_circuit,) - job = sampler.run([pub], shots=500) - data_bin = job.result()[0].data - keys_method = getattr(data_bin, "keys", None) - available_keys = list(keys_method()) if callable(keys_method) else [] - if hasattr(data_bin, "meas") and hasattr(data_bin.meas, "get_int_counts"): - return data_bin.meas.get_int_counts() - if hasattr(data_bin, "c") and hasattr(data_bin.c, "get_int_counts"): - return data_bin.c.get_int_counts() - if available_keys: - for key in available_keys: - reg = getattr(data_bin, key, None) - if reg is not None and hasattr(reg, "get_int_counts"): - return reg.get_int_counts() - raise RuntimeError("No readable classical register found in SamplerV2 result") - - def to_dataframe( counts_int: dict[int, int], qubo: QUBO, var_to_qubit: dict[int, int], all_vars: list[int] ) -> pd.DataFrame: data = [] num_qubits = len(all_vars) - for state_int, count in counts_int.items(): + valid_row_mask = [i for i, r in enumerate(qubo.rows_idx) if r != -1] + valid_col_mask = [i for i, c in enumerate(qubo.cols_idx) if c != -1] + + valid_rows_idx = [qubo.rows_idx[i] for i in valid_row_mask] + valid_cols_idx = [qubo.cols_idx[i] for i in valid_col_mask] + + mat_valid = qubo.mat[np.ix_(valid_row_mask, valid_col_mask)] + + for state_int, _ in counts_int.items(): bin_str = np.binary_repr(state_int, width=num_qubits) full_solution = np.array([int(bit) for bit in bin_str])[::-1] + sol_dict = {var_name: full_solution[q_idx] for var_name, q_idx in var_to_qubit.items()} - vec_row = np.array([sol_dict[r] for r in qubo.rows_idx]) - vec_col = np.array([sol_dict[c] for c in qubo.cols_idx]) - energy = vec_row @ qubo.mat @ vec_col.T + + vec_row = np.array([sol_dict[r] for r in valid_rows_idx]) + vec_col = np.array([sol_dict[c] for c in valid_cols_idx]) + energy = vec_row @ mat_valid @ vec_col.T + + if -1 in sol_dict: + del sol_dict[-1] + row = sol_dict.copy() row["energy"] = energy data.append(row) res = pd.DataFrame(data) res = res.sort_values(by="energy", ascending=True) - cols = [c for c in res.columns if c not in ["energy"]] + + cols = [c for c in res.columns if c != "energy"] cols.sort() res = res[cols + ["energy"]] + best_energy = res["energy"].min() return res[res["energy"] == best_energy] diff --git a/qsplit/adapters/ibm/util_qaoa.py b/qsplit/adapters/ibm/util_qaoa.py new file mode 100644 index 0000000..358b373 --- /dev/null +++ b/qsplit/adapters/ibm/util_qaoa.py @@ -0,0 +1,208 @@ +# Copyright (C) 2025 The QSplit Contributors. +# See the 'CONTRIBUTORS' file at the top-level directory of this distribution. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Acknowledgement: +# Parts of this code are adapted from the official IBM Quantum documentation +# regarding the Quantum Approximate Optimization Algorithm (QAOA). +# Source: https://quantum.cloud.ibm.com/docs/en/tutorials/quantum-approximate-optimization-algorithm +# Modifications have been made to tailor the implementation to local requirements. + +import numpy as np +from qiskit import QuantumCircuit, generate_preset_pass_manager +from qiskit.circuit.library import QAOAAnsatz +from qiskit.passmanager import BasePassManager +from qiskit.primitives import BackendEstimatorV2, BackendSamplerV2 +from qiskit.quantum_info import SparsePauliOp +from qiskit.transpiler.exceptions import TranspilerError +from qiskit_aer import AerSimulator +from qiskit_algorithms.optimizers import SPSA +from qiskit_ibm_runtime import IBMBackend + +from qsplit.adapters.ibm.util import get_variables_mapping +from qsplit.qubo import QUBO + +try: + from qiskit_aer import AerSimulator +except Exception: + AerSimulator = None + +try: + from qiskit_ibm_runtime import EstimatorV2 as RuntimeEstimatorV2 + from qiskit_ibm_runtime import IBMBackend + from qiskit_ibm_runtime import SamplerV2 as RuntimeSamplerV2 +except Exception: + RuntimeEstimatorV2 = None + RuntimeSamplerV2 = None + IBMBackend = None + +try: + from qiskit.primitives import StatevectorEstimator +except Exception: + StatevectorEstimator = None + + +def __from_qubo_matrix_to_circuit(qubo: QUBO) -> tuple[QuantumCircuit, SparsePauliOp, dict[int, int], list[int]]: + var_to_qubit, all_vars = get_variables_mapping(qubo) + num_qubits = len(all_vars) + + pauli_list = [] + + for i, row_var in enumerate(qubo.rows_idx): + for j, col_var in enumerate(qubo.cols_idx): + coeff = qubo.mat[i, j] + if coeff == 0: + continue + + if row_var == col_var: + pauli_list.append(("Z", [var_to_qubit[row_var]], coeff)) + else: + pauli_list.append(("ZZ", [var_to_qubit[row_var], var_to_qubit[col_var]], coeff)) + + if qubo.offset != 0: + pauli_list.append(("I" * num_qubits, list(range(num_qubits)), qubo.offset)) + + cost_hamiltonian = SparsePauliOp.from_sparse_list(pauli_list, num_qubits) + cost_hamiltonian = cost_hamiltonian.simplify() + + circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2) + + return circuit, cost_hamiltonian, var_to_qubit, all_vars + + +__objective_func_vals = [] + + +def _is_ibm_backend(backend) -> bool: + return IBMBackend is not None and isinstance(backend, IBMBackend) + + +def _is_aer_backend(backend) -> bool: + return AerSimulator is not None and isinstance(backend, AerSimulator) + + +def __optimize_circuit( + backend, + candidate_circuit: QuantumCircuit, + cost_hamiltonian: SparsePauliOp, + optimize_on_backend: bool = True, +) -> QuantumCircuit: + initial_gamma = np.pi + initial_beta = np.pi / 2 + init_params = [initial_beta, initial_beta, initial_gamma, initial_gamma] + if not optimize_on_backend: + if StatevectorEstimator is not None: + estimator = StatevectorEstimator() + elif AerSimulator is not None: + estimator = BackendEstimatorV2( + backend=AerSimulator(method="matrix_product_state", matrix_product_state_max_bond_dimension=None) + ) + else: + raise RuntimeError("Local estimator backend is required when optimize_on_backend=False.") + elif _is_ibm_backend(backend) and RuntimeEstimatorV2 is not None: + estimator = RuntimeEstimatorV2(backend) + else: + estimator = BackendEstimatorV2(backend=backend) + if optimize_on_backend and _is_ibm_backend(backend) and hasattr(estimator, "options"): + if hasattr(estimator.options, "default_shots"): + estimator.options.default_shots = 500 + estimator.options.dynamical_decoupling.enable = True + estimator.options.dynamical_decoupling.sequence_type = "XY4" + estimator.options.twirling.enable_gates = True + estimator.options.twirling.num_randomizations = "auto" + + def objective_function(params: list[float]) -> float: + return __cost_func_estimator(params, candidate_circuit, cost_hamiltonian, estimator) + + optimizer = SPSA() + result = optimizer.minimize(fun=objective_function, x0=init_params) + optimized_circuit = candidate_circuit.assign_parameters(result.x) + return optimized_circuit + + +def __cost_func_estimator( + params: list[float], ansatz: QuantumCircuit, hamiltonian: SparsePauliOp, estimator: object +) -> float: + layout = getattr(ansatz, "layout", None) + isa_hamiltonian = hamiltonian.apply_layout(layout) if layout is not None else hamiltonian + pub = (ansatz, isa_hamiltonian, params) + job = estimator.run([pub]) + results = job.result()[0] + cost = results.data.evs + __objective_func_vals.append(cost) + return cost + + +def get_qaoa_circuit_optimized( + backend, + pm: BasePassManager, + qubo: QUBO, + *, + optimize_on_backend: bool = True, +) -> tuple[QuantumCircuit, dict[int, int], list[int]]: + circuit, cost_hamiltonian, var_to_qubit, all_vars = __from_qubo_matrix_to_circuit(qubo) + if optimize_on_backend: + try: + candidate_circuit = pm.run(circuit) + except TranspilerError as exc: + if _is_aer_backend(backend) and "not in Target" in str(exc): + try: + candidate_circuit = generate_preset_pass_manager(optimization_level=1).run(circuit) + except TranspilerError: + candidate_circuit = circuit.decompose(reps=10) + else: + raise + if _is_aer_backend(backend) and any( + str(inst.operation.name).lower() == "qaoa" for inst in candidate_circuit.data + ): + candidate_circuit = candidate_circuit.decompose(reps=10) + optimized_circ = __optimize_circuit(backend, candidate_circuit, cost_hamiltonian, optimize_on_backend=True) + else: + optimized_logical = __optimize_circuit(backend, circuit, cost_hamiltonian, optimize_on_backend=False) + try: + optimized_circ = pm.run(optimized_logical) + except TranspilerError: + optimized_circ = optimized_logical.decompose(reps=10) + measured_circ = optimized_circ.copy() + if measured_circ.num_clbits == 0: + measured_circ.measure_all() + return measured_circ, var_to_qubit, all_vars + + +def run_quantum_optimizer(backend, optimized_circuit: QuantumCircuit) -> dict[int, int]: + if _is_ibm_backend(backend) and RuntimeSamplerV2 is not None: + sampler = RuntimeSamplerV2(mode=backend) + else: + sampler = BackendSamplerV2(backend=backend) + if _is_ibm_backend(backend) and hasattr(sampler, "options"): + sampler.options.dynamical_decoupling.enable = True + sampler.options.dynamical_decoupling.sequence_type = "XY4" + sampler.options.twirling.enable_gates = True + sampler.options.twirling.num_randomizations = "auto" + pub = (optimized_circuit,) + job = sampler.run([pub], shots=500) + data_bin = job.result()[0].data + keys_method = getattr(data_bin, "keys", None) + available_keys = list(keys_method()) if callable(keys_method) else [] + if hasattr(data_bin, "meas") and hasattr(data_bin.meas, "get_int_counts"): + return data_bin.meas.get_int_counts() + if hasattr(data_bin, "c") and hasattr(data_bin.c, "get_int_counts"): + return data_bin.c.get_int_counts() + if available_keys: + for key in available_keys: + reg = getattr(data_bin, key, None) + if reg is not None and hasattr(reg, "get_int_counts"): + return reg.get_int_counts() + raise RuntimeError("No readable classical register found in SamplerV2 result") diff --git a/tests/test_ibm_adapter.py b/tests/test_ibm_adapter.py index b56354d..e0f327f 100644 --- a/tests/test_ibm_adapter.py +++ b/tests/test_ibm_adapter.py @@ -22,9 +22,8 @@ from qiskit.quantum_info import SparsePauliOp from qsplit.adapters.ibm.ibm_qaoa_cpu_noiseless import solve as cpu_solve -from qsplit.adapters.ibm.util import __from_qubo_matrix_to_circuit as from_qubo_matrix_to_circuit -from qsplit.adapters.ibm.util import __get_variables_mapping as get_variables_mapping -from qsplit.adapters.ibm.util import to_dataframe +from qsplit.adapters.ibm.util import get_variables_mapping, to_dataframe +from qsplit.adapters.ibm.util_qaoa import __from_qubo_matrix_to_circuit as from_qubo_matrix_to_circuit from qsplit.qubo import QUBO @@ -156,7 +155,6 @@ def test_to_dataframe_with_padding_variable(self): self.assertEqual(df.iloc[0]["energy"], 0.0) self.assertEqual(df.iloc[0][1], 0) - self.assertIn(-1, df.columns) ################################################## # cpu_noiseless # diff --git a/uv.lock b/uv.lock index 07bf104..b6bc5eb 100644 --- a/uv.lock +++ b/uv.lock @@ -209,8 +209,7 @@ name = "astunparse" version = "1.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-6-qsplit-iqm') or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow')" }, - { name = "six", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' or extra != 'extra-6-qsplit-iqm' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow')" }, + { name = "six" }, { name = "wheel" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f3/af/4182184d3c338792894f34a62672919db7ca008c89abee9b564dd34d8029/astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872", size = 18290, upload-time = "2019-12-22T18:12:13.129Z" } @@ -777,7 +776,7 @@ name = "cwl-upgrader" version = "1.2.14" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ruamel-yaml", version = "0.18.17", source = { registry = "https://pypi.org/simple" } }, + { name = "ruamel-yaml" }, { name = "schema-salad" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9c/21/bde273967127242d991c6ef63c427f9d75ced2a883859544381dae752ec3/cwl_upgrader-1.2.14.tar.gz", hash = "sha256:a20de876a666bb1510d37310d4225fc6df6572e314d1cb6c2202b742a0d76db6", size = 22095, upload-time = "2025-12-21T11:51:21.776Z" } @@ -794,7 +793,7 @@ dependencies = [ { name = "packaging", version = "26.0", source = { registry = "https://pypi.org/simple" } }, { name = "rdflib" }, { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" } }, - { name = "ruamel-yaml", version = "0.18.17", source = { registry = "https://pypi.org/simple" } }, + { name = "ruamel-yaml" }, { name = "schema-salad" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7f/00/80a18397dd81fc39ad5b61af227becebc9d537bcb679d14731023b0bee4a/cwl_utils-0.40.tar.gz", hash = "sha256:fb836fe71617e10cfefb74cfcb2ab7b4a6e36b36cebf2f04b3fb43e15bc74751", size = 365488, upload-time = "2025-09-09T20:53:57.952Z" } @@ -1638,25 +1637,24 @@ wheels = [ [[package]] name = "iqm-exa-common" -version = "27.4.5" +version = "27.5.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "iqm-data-definitions", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "jinja2", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "numpy", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "pydantic", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, - { name = "python-dotenv", version = "0.21.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, + { name = "python-dotenv", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "requests", version = "2.32.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, - { name = "ruamel-yaml", version = "0.17.32", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, - { name = "ruamel-yaml-clib", version = "0.2.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, - { name = "six", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, + { name = "ruamel-yaml", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, + { name = "six", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "types-requests", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "types-six", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "xarray", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/56/98827160372a1ab149ab626afe334da59aed1309b64d1dde27e9de8b2e30/iqm_exa_common-27.4.5.tar.gz", hash = "sha256:42caa485bcdb8ea833adf2f03c0f8e36dc4cfa606acaa44ccba89637a496b253", size = 239597, upload-time = "2026-02-18T17:20:50.988Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/00/e27a6eb472b74164c2e27cd64055ae1ebf02be45e9e718729ce7f4a20a9e/iqm_exa_common-27.5.3.tar.gz", hash = "sha256:8c18490d226169afc3923a6ad472705898503c75271811ca42d415f053b56ca7", size = 272000, upload-time = "2026-05-06T13:13:05.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/38/8e/338f62cf1cf2cb2d1440dc6dbf22fa86fec9fc88b7c642a9374da52a4a8f/iqm_exa_common-27.4.5-py3-none-any.whl", hash = "sha256:5dda62e66178d0ff31836e7eba9507230e4da52598bb61e6514594d998f017e5", size = 89879, upload-time = "2026-02-18T17:20:48.531Z" }, + { url = "https://files.pythonhosted.org/packages/5f/56/63499f5a58ff58c9e775edf323f5268199882d58b4ed1f3f3954c518e452/iqm_exa_common-27.5.3-py3-none-any.whl", hash = "sha256:1977173a8f6fae4c6c3f53c0f5f3a571c628ee94930716cb512dcf58118dfe44", size = 95169, upload-time = "2026-05-06T13:13:03.564Z" }, ] [[package]] @@ -1819,7 +1817,7 @@ dependencies = [ { name = "certifi" }, { name = "python-dateutil" }, { name = "pyyaml" }, - { name = "six", version = "1.17.0", source = { registry = "https://pypi.org/simple" } }, + { name = "six" }, { name = "urllib3" }, ] sdist = { url = "https://files.pythonhosted.org/packages/34/1f/b4312300a1e38c5896b4f5476a546f4a7c86fdd2d53b404699c3dd578bb0/kubernetes_asyncio-34.3.3.tar.gz", hash = "sha256:9f6f30f9745371b26b0bc922be6a3f81c0d6b3400e9f63d0aa1b92aa49cfcc48", size = 1274583, upload-time = "2026-01-12T23:15:27.78Z" } @@ -3010,7 +3008,7 @@ version = "2.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, - { name = "python-dotenv", version = "1.2.1", source = { registry = "https://pypi.org/simple" } }, + { name = "python-dotenv" }, { name = "typing-inspection" }, ] sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } @@ -3103,26 +3101,13 @@ name = "python-dateutil" version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six", version = "1.16.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.13' and extra == 'extra-6-qsplit-iqm') or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow')" }, - { name = "six", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' or extra != 'extra-6-qsplit-iqm' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow')" }, + { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] -[[package]] -name = "python-dotenv" -version = "0.21.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.13'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f5/d7/d548e0d5a68b328a8d69af833a861be415a17cb15ce3d8f0cd850073d2e1/python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49", size = 35930, upload-time = "2023-01-21T10:22:47.277Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/62/f19d1e9023aacb47241de3ab5a5d5fedf32c78a71a9e365bb2153378c141/python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a", size = 19284, upload-time = "2023-01-21T10:22:45.958Z" }, -] - [[package]] name = "python-dotenv" version = "1.2.1" @@ -3824,49 +3809,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, ] -[[package]] -name = "ruamel-yaml" -version = "0.17.32" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.13'", -] -sdist = { url = "https://files.pythonhosted.org/packages/63/dd/b4719a290e49015536bd0ab06ab13e3b468d8697bec6c2f668ac48b05661/ruamel.yaml-0.17.32.tar.gz", hash = "sha256:ec939063761914e14542972a5cba6d33c23b0859ab6342f61cf070cfc600efc2", size = 134455, upload-time = "2023-06-17T05:58:01.15Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/0e/2a05efa11ea33513fbdf4a2e2576fe94fd8fa5ad226dbb9c660886390974/ruamel.yaml-0.17.32-py3-none-any.whl", hash = "sha256:23cd2ed620231677564646b0c6a89d138b6822a0d78656df7abda5879ec4f447", size = 112158, upload-time = "2023-06-17T05:58:04.22Z" }, -] - [[package]] name = "ruamel-yaml" version = "0.18.17" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ruamel-yaml-clib", version = "0.2.15", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.15' and platform_python_implementation == 'CPython') or (python_full_version >= '3.15' and extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (python_full_version >= '3.15' and extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu') or (python_full_version >= '3.15' and extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum') or (platform_python_implementation != 'CPython' and extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (platform_python_implementation != 'CPython' and extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu') or (platform_python_implementation != 'CPython' and extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum')" }, + { name = "ruamel-yaml-clib", marker = "(python_full_version >= '3.13' and python_full_version < '3.15' and extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum') or (python_full_version >= '3.13' and python_full_version < '3.15' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (python_full_version >= '3.13' and python_full_version < '3.15' and extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version >= '3.13' and python_full_version < '3.15' and extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version >= '3.13' and python_full_version < '3.15' and extra != 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.13' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-iqm') or (python_full_version < '3.13' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.13' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.13' and platform_python_implementation == 'CPython' and extra != 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.13' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.15' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.15' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.15' and platform_python_implementation == 'CPython' and extra == 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.15' and platform_python_implementation == 'CPython' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version >= '3.15' and extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum') or (python_full_version >= '3.15' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (python_full_version >= '3.15' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (python_full_version < '3.13' and extra == 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (platform_python_implementation != 'CPython' and extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-quantinuum') or (platform_python_implementation != 'CPython' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum') or (platform_python_implementation != 'CPython' and extra == 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-streamflow') or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3a/2b/7a1f1ebcd6b3f14febdc003e658778d81e76b40df2267904ee6b13f0c5c6/ruamel_yaml-0.18.17.tar.gz", hash = "sha256:9091cd6e2d93a3a4b157ddb8fabf348c3de7f1fb1381346d985b6b247dcd8d3c", size = 149602, upload-time = "2025-12-17T20:02:55.757Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/af/fe/b6045c782f1fd1ae317d2a6ca1884857ce5c20f59befe6ab25a8603c43a7/ruamel_yaml-0.18.17-py3-none-any.whl", hash = "sha256:9c8ba9eb3e793efdf924b60d521820869d5bf0cb9c6f1b82d82de8295e290b9d", size = 121594, upload-time = "2025-12-17T20:02:07.657Z" }, ] -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.8" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.13'", -] -sdist = { url = "https://files.pythonhosted.org/packages/46/ab/bab9eb1566cd16f060b54055dd39cf6a34bfa0240c53a7218c43e974295b/ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512", size = 213824, upload-time = "2023-10-03T18:12:42.315Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/a2/eb5e9d088cb9d15c24d956944c09dca0a89108ad6e2e913c099ef36e3f0d/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1", size = 144636, upload-time = "2023-10-03T18:13:09.564Z" }, - { url = "https://files.pythonhosted.org/packages/66/98/8de4f22bbfd9135deb3422e96d450c4bc0a57d38c25976119307d2efe0aa/ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2", size = 135684, upload-time = "2023-10-03T18:13:12.11Z" }, - { url = "https://files.pythonhosted.org/packages/30/d3/5fe978cd01a61c12efd24d65fa68c6f28f28c8073a06cf11db3a854390ca/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92", size = 734571, upload-time = "2023-10-03T18:13:14.523Z" }, - { url = "https://files.pythonhosted.org/packages/55/b3/e2531a050758b717c969cbf76c103b75d8a01e11af931b94ba656117fbe9/ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_24_aarch64.whl", hash = "sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62", size = 643946, upload-time = "2023-11-09T07:40:20.598Z" }, - { url = "https://files.pythonhosted.org/packages/0d/aa/06db7ca0995b513538402e11280282c615b5ae5f09eb820460d35fb69715/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9", size = 692169, upload-time = "2023-10-22T15:35:38.378Z" }, - { url = "https://files.pythonhosted.org/packages/27/38/4cf4d482b84ecdf51efae6635cc5483a83cf5ca9d9c13e205a750e251696/ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d", size = 740325, upload-time = "2023-10-22T15:35:40.03Z" }, - { url = "https://files.pythonhosted.org/packages/6f/67/c62c6eea53a4feb042727a3d6c18f50dc99683c2b199c06bd2a9e3db8e22/ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa", size = 98639, upload-time = "2023-10-22T15:01:25.115Z" }, - { url = "https://files.pythonhosted.org/packages/10/d2/52a3d810d0b5b3720725c0504a27b3fced7b6f310fe928f7019d79387bc1/ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b", size = 115305, upload-time = "2023-10-22T15:01:27.265Z" }, -] - [[package]] name = "ruamel-yaml-clib" version = "0.2.15" @@ -3962,7 +3916,7 @@ dependencies = [ { name = "mypy-extensions" }, { name = "rdflib" }, { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" } }, - { name = "ruamel-yaml", version = "0.18.17", source = { registry = "https://pypi.org/simple" } }, + { name = "ruamel-yaml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/06/5a230cd2caeb0a642abafe1631db860d92731d5fdab724c54465c6766aab/schema_salad-8.9.20251102115403.tar.gz", hash = "sha256:66b535d8f18ed16436aa400f785b0acca9d757064aaefd311dbe2043c1737286", size = 602347, upload-time = "2025-11-02T12:11:17.998Z" } wheels = [ @@ -4081,47 +4035,10 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/c6/76dc613121b793286a3f91621d7b75a2b493e0390ddca50f11993eadf192/setuptools-82.0.0-py3-none-any.whl", hash = "sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0", size = 1003468, upload-time = "2026-02-08T15:08:38.723Z" }, ] -[[package]] -name = "six" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.13'", -] -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041, upload-time = "2021-05-05T14:18:18.379Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053, upload-time = "2021-05-05T14:18:17.237Z" }, -] - [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "python_full_version >= '3.13' and extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "python_full_version >= '3.13' and extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "python_full_version >= '3.13' and extra != 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "python_full_version >= '3.13' and extra == 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra == 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra == 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra == 'extra-6-qsplit-streamflow'", - "python_full_version >= '3.13' and extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra == 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", - "extra != 'extra-6-qsplit-dev' and extra != 'extra-6-qsplit-ibm-cpu' and extra != 'extra-6-qsplit-ibm-gpu' and extra != 'extra-6-qsplit-iqm' and extra != 'extra-6-qsplit-quantinuum' and extra != 'extra-6-qsplit-streamflow'", -] sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, @@ -4502,16 +4419,16 @@ wheels = [ [[package]] name = "xarray" -version = "2026.2.0" +version = "2024.10.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "packaging", version = "24.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, { name = "pandas", marker = "python_full_version < '3.13' or (extra == 'extra-6-qsplit-dev' and extra == 'extra-6-qsplit-ibm-gpu') or (extra == 'extra-6-qsplit-ibm-cpu' and extra == 'extra-6-qsplit-ibm-gpu')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/03/e3353b72e518574b32993989d8f696277bf878e9d508c7dd22e86c0dab5b/xarray-2026.2.0.tar.gz", hash = "sha256:978b6acb018770554f8fd964af4eb02f9bcc165d4085dbb7326190d92aa74bcf", size = 3111388, upload-time = "2026-02-13T22:20:50.18Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/f2/a3e3ec1ffd29b0b5be800d2606c229f04f303ee9e61a1377dc5c1996cf8a/xarray-2024.10.0.tar.gz", hash = "sha256:e369e2bac430e418c2448e5b96f07da4635f98c1319aa23cfeb3fbcb9a01d2e0", size = 3788358, upload-time = "2024-10-24T21:45:48.532Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/92/545eb2ca17fc0e05456728d7e4378bfee48d66433ae3b7e71948e46826fb/xarray-2026.2.0-py3-none-any.whl", hash = "sha256:e927d7d716ea71dea78a13417970850a640447d8dd2ceeb65c5687f6373837c9", size = 1405358, upload-time = "2026-02-13T22:20:47.847Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b7/9830def68e5575a24ca6d6f46b285d35ed27860beaa4f72848cd82870253/xarray-2024.10.0-py3-none-any.whl", hash = "sha256:ae1d38cb44a0324dfb61e492394158ae22389bf7de9f3c174309c17376df63a0", size = 1212984, upload-time = "2024-10-24T21:45:46.566Z" }, ] [[package]]