From a8a20c56607396e99454ce8fe04e5f74ccd848da Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Sat, 16 May 2026 19:24:05 +0900 Subject: [PATCH 1/9] Support Qiskit 2.4 --- .github/workflows/docs.yml | 2 +- .../workflows/mpi_test_on_pull_request.yml | 8 +++- .github/workflows/publish_mpi_to_pypi.yml | 2 +- .github/workflows/publish_to_pypi_ci.yml | 2 +- .github/workflows/test_on_pull_request.yml | 2 +- CMakeLists.txt | 2 +- README.md | 2 +- docs/source/installation.md | 2 +- pyproject.toml | 6 +-- .../advanced_circuits/test_operators.py | 14 +++---- .../qiskit_tutorials/algorithms/test_qaoa.py | 4 +- .../qiskit_tutorials/algorithms/test_vqe.py | 4 +- .../operators/test_operator_flow.py | 12 +++--- .../test_summary_of_quantum_operations.py | 29 +++++++++----- test/python/test_basics.py | 40 ++++++++++++++----- test/python/test_estimator.py | 8 ++-- test/python/test_sampler.py | 6 +-- 17 files changed, 90 insertions(+), 55 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2a482f2..96ef444 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.14" - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y doxygen graphviz diff --git a/.github/workflows/mpi_test_on_pull_request.yml b/.github/workflows/mpi_test_on_pull_request.yml index 449de12..45a34c5 100644 --- a/.github/workflows/mpi_test_on_pull_request.yml +++ b/.github/workflows/mpi_test_on_pull_request.yml @@ -4,9 +4,13 @@ on: pull_request jobs: mpi_test: - name: MPI test + name: MPI test (py${{ matrix.python-version }}) runs-on: ubuntu-latest timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.14"] steps: - uses: actions/checkout@v4 with: @@ -23,7 +27,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: ${{ matrix.python-version }} - name: Build C++ MPI tests run: | diff --git a/.github/workflows/publish_mpi_to_pypi.yml b/.github/workflows/publish_mpi_to_pypi.yml index b7af39d..07ec066 100644 --- a/.github/workflows/publish_mpi_to_pypi.yml +++ b/.github/workflows/publish_mpi_to_pypi.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.14" - name: Install build tools run: python -m pip install --upgrade pip build diff --git a/.github/workflows/publish_to_pypi_ci.yml b/.github/workflows/publish_to_pypi_ci.yml index 16a9c9d..b291e8f 100644 --- a/.github/workflows/publish_to_pypi_ci.yml +++ b/.github/workflows/publish_to_pypi_ci.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm] - python-tag: ["39", "310", "311", "312", "313"] + python-tag: ["310", "311", "312", "313", "314"] include: - os: ubuntu-latest arch: x86_64 diff --git a/.github/workflows/test_on_pull_request.yml b/.github/workflows/test_on_pull_request.yml index 0d23971..3d7a3dc 100644 --- a/.github/workflows/test_on_pull_request.yml +++ b/.github/workflows/test_on_pull_request.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] include: - os: ubuntu-latest arch: x86_64 diff --git a/CMakeLists.txt b/CMakeLists.txt index f083e70..48a4922 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ add_subdirectory(test) option(BINDINGS "Configure for building Python bindings" ON) if (BINDINGS) - find_package(Python 3.9 COMPONENTS Interpreter Development.Module REQUIRED) + find_package(Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED) execute_process( COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir OUTPUT_STRIP_TRAILING_WHITESPACE diff --git a/README.md b/README.md index 823ae39..7d972a1 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ pip install qdd Supported environment: - Linux x86_64 and aarch64 -- Python 3.9 through 3.13 +- Python 3.10 through 3.14 ## Quick Start diff --git a/docs/source/installation.md b/docs/source/installation.md index 83e5175..504efc5 100644 --- a/docs/source/installation.md +++ b/docs/source/installation.md @@ -33,5 +33,5 @@ complete `mpirun -n 2 python ...` example. ## Supported Environment -QDD currently targets Linux on x86_64 and aarch64 with Python 3.9 through 3.13. +QDD currently targets Linux on x86_64 and aarch64 with Python 3.10 through 3.14. Source builds require a C++17 compiler and CMake 3.25 or newer. diff --git a/pyproject.toml b/pyproject.toml index 44dabb5..b1fd2dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,20 +12,20 @@ dynamic = ["version"] description = "Qiskit Provider for QDD" authors = [{name = "Yusuke Kimura", email = "yusuke-kimura@fujitsu.com"}] readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = {file = "LICENSE.txt"} classifiers = [ "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ - "qiskit~=2.2.0", + "qiskit>=2.0,<2.5", ] [project.urls] diff --git a/test/python/qiskit_tutorials/advanced_circuits/test_operators.py b/test/python/qiskit_tutorials/advanced_circuits/test_operators.py index 740c82e..67f93ca 100644 --- a/test/python/qiskit_tutorials/advanced_circuits/test_operators.py +++ b/test/python/qiskit_tutorials/advanced_circuits/test_operators.py @@ -30,13 +30,13 @@ def test_using_operators_in_circuits(): # Add to a circuit circ = QuantumCircuit(2, 2) circ.append(xx, [0, 1]) - circ.measure([0, 1], [0, 1]) - - backend = QddProvider().get_backend() - circ = transpile( - circ, backend, basis_gates=["rx", "ry", "rz", "cx"], seed_transpiler=50 - ) - job = backend.run(circ, seed_simulator=80) + circ.measure([0, 1], [0, 1]) + + backend = QddProvider().get_backend() + circ = transpile( + circ, basis_gates=["rx", "ry", "rz", "cx"], seed_transpiler=50 + ) + job = backend.run(circ, seed_simulator=80) assert job.result().get_counts(0) == {"11": 1024} # Add Pauli to a circuit directly diff --git a/test/python/qiskit_tutorials/algorithms/test_qaoa.py b/test/python/qiskit_tutorials/algorithms/test_qaoa.py index 631ecd1..dcc7256 100644 --- a/test/python/qiskit_tutorials/algorithms/test_qaoa.py +++ b/test/python/qiskit_tutorials/algorithms/test_qaoa.py @@ -1,7 +1,7 @@ # The code in this file has been written using part of the code in the Qiskit tutorial: # https://learning.quantum.ibm.com/tutorial/quantum-approximate-optimization-algorithm import numpy as np -from qiskit.circuit.library import QAOAAnsatz +from qiskit.circuit.library import qaoa_ansatz from qiskit.quantum_info import SparsePauliOp from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager from scipy.optimize import minimize @@ -48,7 +48,7 @@ def build_max_cut_paulis(graph: rx.PyGraph): cost_hamiltonian = SparsePauliOp.from_list(max_cut_paulis) - circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=2) + circuit = qaoa_ansatz(cost_operator=cost_hamiltonian, reps=2) circuit.measure_all() backend = QddProvider().get_backend() diff --git a/test/python/qiskit_tutorials/algorithms/test_vqe.py b/test/python/qiskit_tutorials/algorithms/test_vqe.py index a233001..55f3564 100644 --- a/test/python/qiskit_tutorials/algorithms/test_vqe.py +++ b/test/python/qiskit_tutorials/algorithms/test_vqe.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from qiskit.circuit.library import EfficientSU2 +from qiskit.circuit.library import efficient_su2 from qiskit.primitives import StatevectorEstimator as QiskitEstimator from qiskit.quantum_info import SparsePauliOp from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager @@ -23,7 +23,7 @@ def test_vqe(): [("YZ", 0.3980), ("ZI", -0.3980), ("ZZ", -0.0113), ("XX", 0.1810)] ) - ansatz = EfficientSU2(hamiltonian.num_qubits) + ansatz = efficient_su2(hamiltonian.num_qubits) num_params = ansatz.num_parameters pm = generate_preset_pass_manager(backend=backend, optimization_level=3) diff --git a/test/python/qiskit_tutorials/operators/test_operator_flow.py b/test/python/qiskit_tutorials/operators/test_operator_flow.py index 2b97205..c5efeb3 100644 --- a/test/python/qiskit_tutorials/operators/test_operator_flow.py +++ b/test/python/qiskit_tutorials/operators/test_operator_flow.py @@ -41,12 +41,12 @@ def test_circuit_sampler(): operator=two_qubit_h2, time=evo_time, synthesis=SuzukiTrotter() ) h2_measurement = two_qubit_h2.adjoint() - bell = QuantumCircuit(2) - bell.h(0) - bell.cx(0, 1) - evo_circ = bell.compose(evo_gate) - - estimator = Estimator() + bell = QuantumCircuit(2) + bell.h(0) + bell.cx(0, 1) + evo_circ = bell.compose(evo_gate).decompose() + + estimator = Estimator() estimator_qiskit = QiskitEstimator() for time in range(8): diff --git a/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py b/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py index 8b1d247..763357b 100644 --- a/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py +++ b/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py @@ -5,8 +5,9 @@ - Conditional gates """ -from qiskit import QuantumCircuit -from qiskit_aer import Aer +from qiskit import QuantumCircuit +from qiskit.synthesis import synth_mcx_noaux_v24 +from qiskit_aer import Aer from qdd import QddProvider from test.python.helpers.circuit_helper import ( @@ -15,7 +16,7 @@ ) -def test_gates_1_to_many_mapping(): +def test_gates_1_to_many_mapping(): gates = [ ("cy", [], [1, 3]), ("cz", [], [1, 3]), @@ -30,13 +31,21 @@ def test_gates_1_to_many_mapping(): ] aer_backend = Aer.get_backend("aer_simulator") - qdd_backend = QddProvider().get_backend() - for qiskit_gate_name, gate_params, target_qubits in gates: - circ = QuantumCircuit(6) - circ.h(1) - circ.h(2) - qiskit_gate_method = getattr(circ, qiskit_gate_name) - qiskit_gate_method(*gate_params, *target_qubits) + qdd_backend = QddProvider().get_backend() + for qiskit_gate_name, gate_params, target_qubits in gates: + circ = QuantumCircuit(6) + circ.h(1) + circ.h(2) + if qiskit_gate_name == "mcx": + controls, target = target_qubits + circ.compose( + synth_mcx_noaux_v24(len(controls)), + [*controls, target], + inplace=True, + ) + else: + qiskit_gate_method = getattr(circ, qiskit_gate_name) + qiskit_gate_method(*gate_params, *target_qubits) circ.measure_all() _, aer_counts = get_counts( circ, aer_backend, n_shots=5000, optimization_level=0 diff --git a/test/python/test_basics.py b/test/python/test_basics.py index 9fca0c3..1cf29bb 100644 --- a/test/python/test_basics.py +++ b/test/python/test_basics.py @@ -10,7 +10,7 @@ import cmath, math import pytest from qiskit import QiskitError, QuantumCircuit, transpile -from qiskit_aer import Aer +from qiskit_aer import Aer, AerSimulator from qiskit.circuit import Parameter from qiskit.circuit.random import random_circuit from qiskit.transpiler import TranspilerError @@ -340,7 +340,7 @@ def test_mv_multiply_count(): def test_mm_multiply(): backend = QddProvider().get_backend() - aer_backend = Aer.get_backend("unitary_simulator") + aer_backend = AerSimulator(method="unitary") for i in range(10): nQubits = 10 circ = random_circuit(num_qubits=nQubits, depth=3, max_operands=2, seed=i*i) @@ -349,7 +349,9 @@ def test_mm_multiply(): result_medge = backend.merge_circuit(circ1, 100000) qdd_unitary = result_medge.getEigenMatrix(nQubits) - circ2 = transpile(circ, backend=aer_backend, optimization_level=0) + circ_aer = circ.copy() + circ_aer.save_unitary() + circ2 = transpile(circ_aer, backend=aer_backend, optimization_level=0) aer_result = aer_backend.run(circ2).result() aer_unitary = aer_result.get_unitary(circ2) aer_unitary_np = np.asarray(aer_unitary) @@ -367,7 +369,13 @@ def test_qdd_gate(): backend = QddProvider().get_backend("statevector_simulator") for i in range(10): nQubits = 10 - circ = transpile(random_circuit(num_qubits=nQubits, depth=3, max_operands=2), backend=backend) + circ = transpile( + random_circuit( + num_qubits=nQubits, depth=3, max_operands=2, seed=i * i + ), + backend=backend, + optimization_level=0, + ) result1 = backend.run(circ).result().to_dict()["results"][0]["edge"] vector1 = result1.getEigenVector() @@ -388,11 +396,23 @@ def test_qdd_gate(): def test_qdd_gate_merge(): backend = QddProvider().get_backend() - aer_backend = Aer.get_backend("unitary_simulator") + aer_backend = AerSimulator(method="unitary") for i in range(10): nQubits = 10 - circ1 = transpile(random_circuit(num_qubits=nQubits, depth=3, max_operands=2), backend=backend, optimization_level=0) - circ2 = transpile(random_circuit(num_qubits=nQubits, depth=3, max_operands=2), backend=backend, optimization_level=0) + circ1 = transpile( + random_circuit( + num_qubits=nQubits, depth=3, max_operands=2, seed=i * i + ), + backend=backend, + optimization_level=0, + ) + circ2 = transpile( + random_circuit( + num_qubits=nQubits, depth=3, max_operands=2, seed=1000 + i * i + ), + backend=backend, + optimization_level=0, + ) result_medge2 = backend.merge_circuit(circ2, 100000) qgate2 = QDDGate(nQubits, result_medge2) @@ -404,7 +424,9 @@ def test_qdd_gate_merge(): unitary_merged = result_all.getEigenMatrix(nQubits) circ1.append(circ2, list(range(nQubits))) - all_circ = transpile(circ1, backend=aer_backend, optimization_level=0) + circ_aer = circ1.copy() + circ_aer.save_unitary() + all_circ = transpile(circ_aer, backend=aer_backend, optimization_level=0) aer_result = aer_backend.run(all_circ).result() aer_unitary = aer_result.get_unitary(all_circ) aer_unitary_np = np.asarray(aer_unitary) @@ -424,5 +446,5 @@ def test_qddgate_error(): result_medge2 = backend.merge_circuit(circ2, 100000) qgate2 = QDDGate(nQubits, result_medge2) circ1.append(qgate2, list(range(nQubits))) - with pytest.raises(TypeError): + with pytest.raises((TypeError, TranspilerError)): circ1 = transpile(circ1, backend=backend, optimization_level=0) diff --git a/test/python/test_estimator.py b/test/python/test_estimator.py index 77cb028..335b0b1 100644 --- a/test/python/test_estimator.py +++ b/test/python/test_estimator.py @@ -3,13 +3,13 @@ from math import sqrt import pytest from qiskit.primitives import StatevectorEstimator as QiskitEstimator -from qiskit.circuit.library import RealAmplitudes +from qiskit.circuit.library import real_amplitudes from qiskit.quantum_info import SparsePauliOp from qdd.qdd_estimator import Estimator def test_estimator(): - psi1 = RealAmplitudes(num_qubits=2, reps=2) + psi1 = real_amplitudes(num_qubits=2, reps=2) H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) @@ -47,8 +47,8 @@ def test_estimator2(): # , # ] - psi1 = RealAmplitudes(num_qubits=2, reps=2) - psi2 = RealAmplitudes(num_qubits=2, reps=3) + psi1 = real_amplitudes(num_qubits=2, reps=2) + psi2 = real_amplitudes(num_qubits=2, reps=3) H1 = SparsePauliOp.from_list([("II", 1), ("IZ", 2), ("XI", 3)]) H2 = SparsePauliOp.from_list([("IZ", 1)]) diff --git a/test/python/test_sampler.py b/test/python/test_sampler.py index d6d9d6c..4f0f461 100644 --- a/test/python/test_sampler.py +++ b/test/python/test_sampler.py @@ -3,7 +3,7 @@ import pytest from qiskit.primitives import StatevectorSampler as QiskitSampler from qiskit import QuantumCircuit -from qiskit.circuit.library import RealAmplitudes +from qiskit.circuit.library import real_amplitudes from qiskit.result import QuasiDistribution from qdd.qdd_sampler import Sampler @@ -56,9 +56,9 @@ def test_sampler(): def test_sampler_param(): # two parameterized circuits - pqc = RealAmplitudes(num_qubits=2, reps=2) + pqc = real_amplitudes(num_qubits=2, reps=2) pqc.measure_all() - pqc2 = RealAmplitudes(num_qubits=2, reps=3) + pqc2 = real_amplitudes(num_qubits=2, reps=3) pqc2.measure_all() theta1 = [0, 1, 1, 2, 3, 5] From 2accb30c9c9c1d03cb0255c1bf51d2d9ef40b0ac Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Sat, 16 May 2026 20:05:18 +0900 Subject: [PATCH 2/9] Update Python version support in workflows and documentation --- .../workflows/mpi_test_on_pull_request.yml | 2 +- .github/workflows/publish_to_pypi_ci.yml | 2 +- .github/workflows/test_on_pull_request.yml | 2 +- CMakeLists.txt | 2 +- README.md | 2 +- docs/source/installation.md | 2 +- pyproject.toml | 6 ++++-- .../test_summary_of_quantum_operations.py | 19 +++++++++++++------ 8 files changed, 23 insertions(+), 14 deletions(-) diff --git a/.github/workflows/mpi_test_on_pull_request.yml b/.github/workflows/mpi_test_on_pull_request.yml index 45a34c5..1ca3f2c 100644 --- a/.github/workflows/mpi_test_on_pull_request.yml +++ b/.github/workflows/mpi_test_on_pull_request.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.11", "3.14"] + python-version: ["3.9", "3.11", "3.14"] steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/publish_to_pypi_ci.yml b/.github/workflows/publish_to_pypi_ci.yml index b291e8f..12a3707 100644 --- a/.github/workflows/publish_to_pypi_ci.yml +++ b/.github/workflows/publish_to_pypi_ci.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm] - python-tag: ["310", "311", "312", "313", "314"] + python-tag: ["39", "310", "311", "312", "313", "314"] include: - os: ubuntu-latest arch: x86_64 diff --git a/.github/workflows/test_on_pull_request.yml b/.github/workflows/test_on_pull_request.yml index 3d7a3dc..8d674aa 100644 --- a/.github/workflows/test_on_pull_request.yml +++ b/.github/workflows/test_on_pull_request.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm] - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] include: - os: ubuntu-latest arch: x86_64 diff --git a/CMakeLists.txt b/CMakeLists.txt index 48a4922..f083e70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ add_subdirectory(test) option(BINDINGS "Configure for building Python bindings" ON) if (BINDINGS) - find_package(Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED) + find_package(Python 3.9 COMPONENTS Interpreter Development.Module REQUIRED) execute_process( COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir OUTPUT_STRIP_TRAILING_WHITESPACE diff --git a/README.md b/README.md index 7d972a1..d2919ba 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ pip install qdd Supported environment: - Linux x86_64 and aarch64 -- Python 3.10 through 3.14 +- Python 3.9 through 3.14 ## Quick Start diff --git a/docs/source/installation.md b/docs/source/installation.md index 504efc5..81fc7f3 100644 --- a/docs/source/installation.md +++ b/docs/source/installation.md @@ -33,5 +33,5 @@ complete `mpirun -n 2 python ...` example. ## Supported Environment -QDD currently targets Linux on x86_64 and aarch64 with Python 3.10 through 3.14. +QDD currently targets Linux on x86_64 and aarch64 with Python 3.9 through 3.14. Source builds require a C++17 compiler and CMake 3.25 or newer. diff --git a/pyproject.toml b/pyproject.toml index b1fd2dd..2f800d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,11 +12,12 @@ dynamic = ["version"] description = "Qiskit Provider for QDD" authors = [{name = "Yusuke Kimura", email = "yusuke-kimura@fujitsu.com"}] readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.9" license = {file = "LICENSE.txt"} classifiers = [ "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -25,7 +26,8 @@ classifiers = [ ] dependencies = [ - "qiskit>=2.0,<2.5", + "qiskit>=2.0,<2.3; python_version < '3.10'", + "qiskit>=2.0,<2.5; python_version >= '3.10'", ] [project.urls] diff --git a/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py b/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py index 763357b..8d08458 100644 --- a/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py +++ b/test/python/qiskit_tutorials/quantum_circuits/test_summary_of_quantum_operations.py @@ -6,8 +6,12 @@ """ from qiskit import QuantumCircuit -from qiskit.synthesis import synth_mcx_noaux_v24 from qiskit_aer import Aer + +try: + from qiskit.synthesis import synth_mcx_noaux_v24 +except ImportError: + synth_mcx_noaux_v24 = None from qdd import QddProvider from test.python.helpers.circuit_helper import ( @@ -38,11 +42,14 @@ def test_gates_1_to_many_mapping(): circ.h(2) if qiskit_gate_name == "mcx": controls, target = target_qubits - circ.compose( - synth_mcx_noaux_v24(len(controls)), - [*controls, target], - inplace=True, - ) + if synth_mcx_noaux_v24 is None: + circ.mcx(controls, target) + else: + circ.compose( + synth_mcx_noaux_v24(len(controls)), + [*controls, target], + inplace=True, + ) else: qiskit_gate_method = getattr(circ, qiskit_gate_name) qiskit_gate_method(*gate_params, *target_qubits) From 32cc315a8784d8b41e7f10c052c9839dd554a4a6 Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Sat, 16 May 2026 20:23:14 +0900 Subject: [PATCH 3/9] Remove Python 3.14 from the test workflow (due to qiskit-aer) --- .github/workflows/test_on_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_on_pull_request.yml b/.github/workflows/test_on_pull_request.yml index 8d674aa..0d23971 100644 --- a/.github/workflows/test_on_pull_request.yml +++ b/.github/workflows/test_on_pull_request.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, ubuntu-24.04-arm] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] include: - os: ubuntu-latest arch: x86_64 From c7021adabddcda089e5217e4ea3305536b9da41f Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Sat, 16 May 2026 20:29:24 +0900 Subject: [PATCH 4/9] Update GitHub Actions workflows to use latest action versions --- .github/workflows/docs.yml | 10 +++++----- .github/workflows/mpi_test_on_pull_request.yml | 6 +++--- .github/workflows/publish_mpi_to_pypi.yml | 6 +++--- .github/workflows/publish_to_pypi_ci.yml | 14 +++++++------- .github/workflows/test_on_pull_request.yml | 8 ++++---- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 96ef444..39af966 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,11 +14,11 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.14" @@ -28,7 +28,7 @@ jobs: - name: Install Python package and documentation dependencies run: | python -m pip install --upgrade pip - python -m pip install .[docs] + python -m pip install '.[docs]' - name: Generate C++ XML run: | @@ -38,7 +38,7 @@ jobs: - name: Build HTML run: sphinx-build -b html docs/source docs/build/html - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v5 if: github.event_name != 'pull_request' with: path: docs/build/html @@ -55,4 +55,4 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/mpi_test_on_pull_request.yml b/.github/workflows/mpi_test_on_pull_request.yml index 1ca3f2c..7bf1089 100644 --- a/.github/workflows/mpi_test_on_pull_request.yml +++ b/.github/workflows/mpi_test_on_pull_request.yml @@ -12,7 +12,7 @@ jobs: matrix: python-version: ["3.9", "3.11", "3.14"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -25,7 +25,7 @@ jobs: libboost-mpi-dev \ libboost-serialization-dev - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -50,7 +50,7 @@ jobs: run: | python -m pip install --upgrade pip build python -m pip install mpi4py - CMAKE_ARGS="-DisMPI=ON" CC=mpicc CXX=mpicxx python -m pip install .[test] + CMAKE_ARGS="-DisMPI=ON" CC=mpicc CXX=mpicxx python -m pip install '.[test]' - name: Run Python MPI tests run: | diff --git a/.github/workflows/publish_mpi_to_pypi.yml b/.github/workflows/publish_mpi_to_pypi.yml index 07ec066..a195e8e 100644 --- a/.github/workflows/publish_mpi_to_pypi.yml +++ b/.github/workflows/publish_mpi_to_pypi.yml @@ -16,11 +16,11 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: "3.14" @@ -34,7 +34,7 @@ jobs: fi bash scripts/build_mpi_sdist.sh - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: qdd-mpi-sdist path: dist-mpi/*.tar.gz diff --git a/.github/workflows/publish_to_pypi_ci.yml b/.github/workflows/publish_to_pypi_ci.yml index 12a3707..f57214f 100644 --- a/.github/workflows/publish_to_pypi_ci.yml +++ b/.github/workflows/publish_to_pypi_ci.yml @@ -21,14 +21,14 @@ jobs: - os: ubuntu-24.04-arm arch: aarch64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: pypa/cibuildwheel@v3.3.0 env: CIBW_ARCHS_LINUX: ${{ matrix.arch }} CIBW_BUILD: cp${{ matrix.python-tag }}-manylinux_${{ matrix.arch }} - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: wheels-${{ matrix.arch }}-cp${{ matrix.python-tag }} path: ./wheelhouse/*.whl @@ -36,12 +36,12 @@ jobs: build_sdist: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 - run: pipx run build --sdist - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: sdist path: dist/*.tar.gz @@ -56,12 +56,12 @@ jobs: permissions: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: path: dist/ pattern: "wheels-*" merge-multiple: true - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v7 with: path: dist/ name: sdist diff --git a/.github/workflows/test_on_pull_request.yml b/.github/workflows/test_on_pull_request.yml index 0d23971..c4f09cb 100644 --- a/.github/workflows/test_on_pull_request.yml +++ b/.github/workflows/test_on_pull_request.yml @@ -18,11 +18,11 @@ jobs: arch: aarch64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -30,7 +30,7 @@ jobs: run: python -m pip install --upgrade pip build - name: Install package and test dependencies - run: python -m pip install .[test] + run: python -m pip install '.[test]' - name: Run pytest run: | @@ -55,7 +55,7 @@ jobs: arch: aarch64 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Show CMake version run: cmake --version From f022e4f54ee9200db8f1726415809dee7fa363f2 Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Sat, 16 May 2026 20:35:22 +0900 Subject: [PATCH 5/9] Enhance MPI testing workflow --- .../workflows/mpi_test_on_pull_request.yml | 77 ++++++++++++++----- test/python/conftest.py | 14 ++++ 2 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 test/python/conftest.py diff --git a/.github/workflows/mpi_test_on_pull_request.yml b/.github/workflows/mpi_test_on_pull_request.yml index 7bf1089..733580b 100644 --- a/.github/workflows/mpi_test_on_pull_request.yml +++ b/.github/workflows/mpi_test_on_pull_request.yml @@ -3,14 +3,20 @@ name: MPI test on pull request on: pull_request jobs: - mpi_test: - name: MPI test (py${{ matrix.python-version }}) - runs-on: ubuntu-latest + mpi_python_test: + name: Python MPI test (${{ matrix.os }} ${{ matrix.arch }} / py${{ matrix.python-version }}) + runs-on: ${{ matrix.os }} timeout-minutes: 20 strategy: fail-fast: false matrix: - python-version: ["3.9", "3.11", "3.14"] + os: [ubuntu-latest, ubuntu-24.04-arm] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + include: + - os: ubuntu-latest + arch: x86_64 + - os: ubuntu-24.04-arm + arch: aarch64 steps: - uses: actions/checkout@v6 with: @@ -29,6 +35,54 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install qdd with MPI bindings + run: | + python -m pip install --upgrade pip build + python -m pip install mpi4py + CMAKE_ARGS="-DisMPI=ON" CC=mpicc CXX=mpicxx python -m pip install '.[test]' + + - name: Run Python MPI tests + run: | + rm -rf /tmp/qdd-mpi-tests + mkdir -p /tmp/qdd-mpi-tests + cp pyproject.toml /tmp/qdd-mpi-tests/pyproject.toml + cp -r test /tmp/qdd-mpi-tests/test + cd /tmp/qdd-mpi-tests + export QDD_TEST_USE_MPI=1 + mpirun --oversubscribe -np 2 python -m pytest \ + -o addopts="" \ + test/python \ + -m "not slow and not no_mpi_support" \ + --strict-markers + + mpi_cpp_test: + name: C++ MPI test (${{ matrix.os }} ${{ matrix.arch }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, ubuntu-24.04-arm] + include: + - os: ubuntu-latest + arch: x86_64 + - os: ubuntu-24.04-arm + arch: aarch64 + + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install OpenMPI and Boost + run: | + sudo apt-get update + sudo apt-get install -y \ + libopenmpi-dev \ + openmpi-bin \ + libboost-mpi-dev \ + libboost-serialization-dev + - name: Build C++ MPI tests run: | cmake -S . -B build-mpi \ @@ -45,18 +99,3 @@ jobs: mpirun --oversubscribe -np 2 ./build-mpi/test/mpi_test mpirun --oversubscribe -np 2 ./build-mpi/test/mpi_test_grover 6 mpirun --oversubscribe -np 2 ./build-mpi/test/mpi_qcbm 6 - - - name: Install qdd with MPI bindings - run: | - python -m pip install --upgrade pip build - python -m pip install mpi4py - CMAKE_ARGS="-DisMPI=ON" CC=mpicc CXX=mpicxx python -m pip install '.[test]' - - - name: Run Python MPI tests - run: | - rm -rf /tmp/qdd-mpi-tests - mkdir -p /tmp/qdd-mpi-tests - cp pyproject.toml /tmp/qdd-mpi-tests/pyproject.toml - cp -r test /tmp/qdd-mpi-tests/test - cd /tmp/qdd-mpi-tests - mpirun --oversubscribe -np 2 python -m pytest -o addopts="" test/python/test_mpi.py diff --git a/test/python/conftest.py b/test/python/conftest.py new file mode 100644 index 0000000..eeb3914 --- /dev/null +++ b/test/python/conftest.py @@ -0,0 +1,14 @@ +import os + + +if os.environ.get("QDD_TEST_USE_MPI") == "1": + from qdd.qdd_backend import QddBackend + + _default_options = QddBackend._default_options.__func__ + + def _default_options_with_mpi(cls): + options = _default_options(cls) + options.update_options(use_mpi=True) + return options + + QddBackend._default_options = classmethod(_default_options_with_mpi) From de3accb7e5d559b99fd0fb76a14a316af5845c64 Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Sun, 17 May 2026 17:47:11 +0900 Subject: [PATCH 6/9] Fix random number generation in the tests --- test/python/test_gate.py | 43 +++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/test/python/test_gate.py b/test/python/test_gate.py index 29faa2d..b5cfb30 100644 --- a/test/python/test_gate.py +++ b/test/python/test_gate.py @@ -17,6 +17,9 @@ import scipy.stats sampler_qdd = qdd_sampler() +# MPI backend broadcasts rank 0's circuit; every rank must build the same oracle circuit. +rng = random.Random(1234) +unitary_rng = np.random.RandomState(1234) qc_size = 6 @@ -41,7 +44,7 @@ def test_1q_0param_gates(): qis_gate = qis() num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -54,11 +57,11 @@ def test_1q_1param_gates(): if qis == qiskit_gates.MCPhaseGate: continue for _ in range(10): - para = random.uniform(-math.pi, math.pi) + para = rng.uniform(-math.pi, math.pi) qis_gate = qis(para) num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -69,12 +72,12 @@ def test_1q_1param_gates(): def test_1q_2param_gates(): for qis, _ in _qiskit_gates_1q_2param.items(): for _ in range(20): - para1 = random.uniform(-math.pi, math.pi) - para2 = random.uniform(-math.pi, math.pi) + para1 = rng.uniform(-math.pi, math.pi) + para2 = rng.uniform(-math.pi, math.pi) qis_gate = qis(para1, para2) num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -85,13 +88,13 @@ def test_1q_2param_gates(): def test_1q_3param_gates(): for qis, _ in _qiskit_gates_1q_3param.items(): for _ in range(30): - para1 = random.uniform(-math.pi, math.pi) - para2 = random.uniform(-math.pi, math.pi) - para3 = random.uniform(-math.pi, math.pi) + para1 = rng.uniform(-math.pi, math.pi) + para2 = rng.uniform(-math.pi, math.pi) + para3 = rng.uniform(-math.pi, math.pi) qis_gate = qis(para1, para2, para3) num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -102,14 +105,14 @@ def test_1q_3param_gates(): def test_1q_4param_gates(): for qis, _ in _qiskit_gates_1q_4param.items(): for _ in range(40): - para1 = random.uniform(-math.pi, math.pi) - para2 = random.uniform(-math.pi, math.pi) - para3 = random.uniform(-math.pi, math.pi) - para4 = random.uniform(-math.pi, math.pi) + para1 = rng.uniform(-math.pi, math.pi) + para2 = rng.uniform(-math.pi, math.pi) + para3 = rng.uniform(-math.pi, math.pi) + para4 = rng.uniform(-math.pi, math.pi) qis_gate = qis(para1, para2, para3, para4) num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -122,7 +125,7 @@ def test_2q_0param_gates(): qis_gate = qis() num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -133,11 +136,11 @@ def test_2q_0param_gates(): def test_2q_1param_gates(): for qis, _ in _qiskit_gates_2q_1param.items(): for _ in range(10): - para = random.uniform(-math.pi, math.pi) + para = rng.uniform(-math.pi, math.pi) qis_gate = qis(para) num_qubits = qis_gate.num_qubits for _ in range(3): - targets = random.sample(range(qc_size), num_qubits) + targets = rng.sample(range(qc_size), num_qubits) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qis_gate, targets) @@ -148,9 +151,9 @@ def test_2q_1param_gates(): def test_unitary(): bit = 3 for _ in range(10): - random_matrix = scipy.stats.unitary_group.rvs(2**bit) + random_matrix = scipy.stats.unitary_group.rvs(2**bit, random_state=unitary_rng) for _ in range(3): - targets = random.sample(range(qc_size), bit) + targets = rng.sample(range(qc_size), bit) qc = QuantumCircuit(qc_size) qc.h(range(qc_size)) qc.append(qiskit_gates.UnitaryGate(random_matrix), targets) From e7caf922c98247d39ce6d334bd3e410d6ac6585b Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Mon, 18 May 2026 10:18:11 +0900 Subject: [PATCH 7/9] Add seed to random circuits in tests for reproducibility --- test/python/test_basics.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/python/test_basics.py b/test/python/test_basics.py index 1cf29bb..64f63cd 100644 --- a/test/python/test_basics.py +++ b/test/python/test_basics.py @@ -281,9 +281,9 @@ def test_gc_mat_correctness(): backend = QddProvider().get_backend() for i in range(10): nQubits = 10 - circ = random_circuit(num_qubits=nQubits, depth=3, max_operands=2) + circ = random_circuit(num_qubits=nQubits, depth=3, max_operands=2, seed=i * i) - circ = transpile(circ, backend=backend) + circ = transpile(circ, backend=backend, optimization_level=0) result_medge = backend.merge_circuit(circ, 100000) qdd_unitary = result_medge.getEigenMatrix(nQubits) @@ -440,8 +440,14 @@ def test_qdd_gate_merge(): def test_qddgate_error(): backend = QddProvider().get_backend() nQubits = 10 - circ1 = transpile(random_circuit(num_qubits=nQubits, depth=3, max_operands=2), backend=backend) - circ2 = transpile(random_circuit(num_qubits=nQubits, depth=3, max_operands=2), backend=backend) + circ1 = transpile( + random_circuit(num_qubits=nQubits, depth=3, max_operands=2, seed=0), + backend=backend, + ) + circ2 = transpile( + random_circuit(num_qubits=nQubits, depth=3, max_operands=2, seed=1), + backend=backend, + ) result_medge2 = backend.merge_circuit(circ2, 100000) qgate2 = QDDGate(nQubits, result_medge2) From b0d3a95504a1540cdfb6b841f9e4b2cda9c69f30 Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Mon, 18 May 2026 10:29:26 +0900 Subject: [PATCH 8/9] Add Qiskit version test workflow --- .../qiskit_version_test_on_pull_request.yml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/qiskit_version_test_on_pull_request.yml diff --git a/.github/workflows/qiskit_version_test_on_pull_request.yml b/.github/workflows/qiskit_version_test_on_pull_request.yml new file mode 100644 index 0000000..621da2e --- /dev/null +++ b/.github/workflows/qiskit_version_test_on_pull_request.yml @@ -0,0 +1,38 @@ +name: Qiskit version test on pull request + +on: pull_request + +jobs: + qiskit_version_test: + name: Qiskit ${{ matrix.qiskit-version }} test (py${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.14"] + qiskit-version: ["2.0", "2.1", "2.2", "2.3", "2.4"] + + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + + - name: Install package with selected Qiskit + run: | + python -m pip install --upgrade pip build + python -m pip install '.[test]' 'qiskit~=${{ matrix.qiskit-version }}.0' + python -m pip check + python -c "import qiskit; print(f'qiskit {qiskit.__version__}')" + + - name: Run pytest + run: | + rm -rf /tmp/qdd-qiskit-tests + mkdir -p /tmp/qdd-qiskit-tests + cp pyproject.toml /tmp/qdd-qiskit-tests/pyproject.toml + cp -r test /tmp/qdd-qiskit-tests/test + cd /tmp/qdd-qiskit-tests + python -m pytest -m "not slow and not mpi" From 9cf7b024e5cfacf8a35d174d352f3350afb9770d Mon Sep 17 00:00:00 2001 From: Yusuke Kimura Date: Mon, 18 May 2026 10:46:37 +0900 Subject: [PATCH 9/9] Spcify python ver for qiskit version tests --- .../qiskit_version_test_on_pull_request.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/qiskit_version_test_on_pull_request.yml b/.github/workflows/qiskit_version_test_on_pull_request.yml index 621da2e..2ce60fb 100644 --- a/.github/workflows/qiskit_version_test_on_pull_request.yml +++ b/.github/workflows/qiskit_version_test_on_pull_request.yml @@ -9,8 +9,17 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.14"] - qiskit-version: ["2.0", "2.1", "2.2", "2.3", "2.4"] + include: + - qiskit-version: "2.0" + python-version: "3.13" + - qiskit-version: "2.1" + python-version: "3.13" + - qiskit-version: "2.2" + python-version: "3.13" + - qiskit-version: "2.3" + python-version: "3.13" + - qiskit-version: "2.4" + python-version: "3.14" steps: - uses: actions/checkout@v6