From c31c1f03fb75f1d3189e3866552dfd5f8d409ed5 Mon Sep 17 00:00:00 2001 From: smaugdela Date: Thu, 11 Sep 2025 18:10:58 +0200 Subject: [PATCH 01/13] WIP implementing noise_model client-sided, as well as unit tests. Broken right now. --- qiskit_scaleway/backends/aer/backend.py | 1 + qiskit_scaleway/backends/base_job.py | 3 + tests/test_aer_multiple_circuits.py | 96 ++++++++++++++++++++++++- tests/test_estimator.py | 2 +- tests/test_sampler.py | 2 +- 5 files changed, 101 insertions(+), 3 deletions(-) diff --git a/qiskit_scaleway/backends/aer/backend.py b/qiskit_scaleway/backends/aer/backend.py index 4f4691a..41d0424 100644 --- a/qiskit_scaleway/backends/aer/backend.py +++ b/qiskit_scaleway/backends/aer/backend.py @@ -71,6 +71,7 @@ def _default_options(self): shots=1000, memory=False, seed_simulator=None, + noise_model=None, method="automatic", precision="double", max_shot_size=None, diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 1d2b232..4146164 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -89,6 +89,9 @@ def submit(self, session_id: str) -> None: ), ) + if options.get("noise_model"): + options["noise_model"] = options["noise_model"].to_dict(True) + backend_data = QaaSJobBackendData( name=self.backend().name, version=self.backend().version, diff --git a/tests/test_aer_multiple_circuits.py b/tests/test_aer_multiple_circuits.py index 01a99e6..db8522f 100644 --- a/tests/test_aer_multiple_circuits.py +++ b/tests/test_aer_multiple_circuits.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +from typing import Literal import numpy as np import random +import dotenv from qiskit import QuantumCircuit from qiskit_scaleway import ScalewayProvider +dotenv.load_dotenv() + def _random_qiskit_circuit(size: int) -> QuantumCircuit: num_qubits = size @@ -47,13 +51,14 @@ def _random_qiskit_circuit(size: int) -> QuantumCircuit: def test_aer_multiple_circuits(): + provider = ScalewayProvider( project_id=os.environ["QISKIT_SCALEWAY_PROJECT_ID"], secret_key=os.environ["QISKIT_SCALEWAY_SECRET_KEY"], url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend("aer_simulation_pop_c16m128") + backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) assert backend is not None @@ -86,3 +91,92 @@ def test_aer_multiple_circuits(): assert result.success finally: backend.delete_session(session_id) + + +def _get_noise_model(): + import qiskit_aer.noise as noise + + # Error probabilities + prob_1 = 0.001 # 1-qubit gate + prob_2 = 0.01 # 2-qubit gate + + # Depolarizing quantum errors + error_1 = noise.depolarizing_error(prob_1, 1) + error_2 = noise.depolarizing_error(prob_2, 2) + + # Add errors to noise model + noise_model = noise.NoiseModel() + noise_model.add_all_qubit_quantum_error(error_1, ['rz', 'sx', 'x']) + noise_model.add_all_qubit_quantum_error(error_2, ['cx']) + + return noise_model + + +def _bell_state_circuit(): + qc = QuantumCircuit(2, 2) + qc.h(0) + qc.cx(0, 1) + qc.measure_all() + return qc + + +def _simple_one_state_circuit(init_state: Literal["0", "1"] = "0"): + qc = QuantumCircuit(1, 1) + if init_state == "1": + qc.x(0) + qc.measure_all() + return qc + + +def test_aer_with_noise_model(): + + provider = ScalewayProvider( + project_id=os.environ["QISKIT_SCALEWAY_PROJECT_ID"], + secret_key=os.environ["QISKIT_SCALEWAY_SECRET_KEY"], + url=os.getenv("QISKIT_SCALEWAY_API_URL"), + ) + + backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) + + assert backend is not None + + session_id = backend.start_session( + name="my-aer-session-autotest", + deduplication_id=f"my-aer-session-autotest-{random.randint(1, 1000)}", + max_duration="15m", + ) + + assert session_id is not None + + try: + qc1 = _bell_state_circuit() + qc2 = _simple_one_state_circuit('0') + qc3 = _simple_one_state_circuit('1') + + run_ideal_result = backend.run( + [qc1, qc2, qc3], + shots=1000, + max_parallel_experiments=0, + session_id=session_id, + ).result() + + run_noisy_result = backend.run( + [qc1, qc2, qc3], + shots=1000, + max_parallel_experiments=0, + session_id=session_id, + noise_model=_get_noise_model() + ).result() + + ideal_results = run_ideal_result.results + noisy_results = run_noisy_result.results + + assert len(ideal_results) == len(noisy_results) == 3 + + for i, ideal_result in enumerate(ideal_results): + assert len(ideal_result.data.counts) < len(noisy_results[i].data.counts) + finally: + backend.delete_session(session_id) + + +{'GJS_DEBUG_TOPICS': 'JS ERROR;JS LOG', 'LANGUAGE': 'en', 'USER': 'smagdela', 'LC_TIME': 'fr_FR.UTF-8', 'FONTCONFIG_PATH': '/etc/fonts', 'GIO_MODULE_DIR': '/home/smagdela/snap/code/common/.cache/gio-modules', 'XDG_SESSION_TYPE': 'wayland', 'GTK_EXE_PREFIX_VSCODE_SNAP_ORIG': '', 'GDK_BACKEND_VSCODE_SNAP_ORIG': '', 'SHLVL': '2', 'LESS': '-R', 'HOME': '/home/smagdela', 'LOCPATH_VSCODE_SNAP_ORIG': '', 'OLDPWD': '/home/smagdela/Documents/qiskit-scaleway', 'DESKTOP_SESSION': 'ubuntu', 'GTK_PATH': '/snap/code/205/usr/lib/x86_64-linux-gnu/gtk-3.0', 'NVM_BIN': '/home/smagdela/.nvm/versions/node/v24.7.0/bin', 'LSCOLORS': 'Gxfxcxdxbxegedabagacad', 'NVM_INC': '/home/smagdela/.nvm/versions/node/v24.7.0/include/node', 'ZSH': '/home/smagdela/.oh-my-zsh', 'XDG_DATA_HOME_VSCODE_SNAP_ORIG': '', 'GTK_IM_MODULE_FILE': '/home/smagdela/snap/code/common/.cache/immodules/immodules.cache', 'GNOME_SHELL_SESSION_MODE': 'ubuntu', 'GTK_MODULES': 'gail:atk-bridge', 'GSETTINGS_SCHEMA_DIR_VSCODE_SNAP_ORIG': '', 'PAGER': 'less', 'LC_MONETARY': 'fr_FR.UTF-8', 'MANAGERPID': '4094', 'SYSTEMD_EXEC_PID': '4472', 'GSM_SKIP_SSH_AGENT_WORKAROUND': 'true', 'DBUS_SESSION_BUS_ADDRESS': 'unix:path=/run/user/1000/bus', 'GIT_TOKEN': 'P-FDofUbEdSBTpPkuUGxJG86MQp1OjJ6dgk.01.0z159r3sl', 'COLORTERM': 'truecolor', 'NVM_DIR': '/home/smagdela/.nvm', 'WAYLAND_DISPLAY': 'wayland-0', 'LOGNAME': 'smagdela', 'JOURNAL_STREAM': '9:33927', 'XDG_CONFIG_DIRS_VSCODE_SNAP_ORIG': '', 'MEMORY_PRESSURE_WATCH': '/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/session.slice/org.gnome.Shell@wayland.service/memory.pressure', 'XDG_SESSION_CLASS': 'user', 'XDG_DATA_DIRS_VSCODE_SNAP_ORIG': '/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'USERNAME': 'smagdela', 'TERM': 'xterm-256color', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'PATH': '/home/smagdela/Documents/qiskit-scaleway/.venv/bin:/home/smagdela/.nvm/versions/node/v24.7.0/bin:/home/smagdela/.local/bin:/home/smagdela/.local/share/gnome-shell/extensions/ddterm@amezin.github.com/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin:/home/smagdela/.vscode/extensions/ms-python.debugpy-2025.10.0-linux-x64/bundled/scripts/noConfigScripts', 'SESSION_MANAGER': 'local/smagdela-ThinkPad-T14s-Gen-4:@/tmp/.ICE-unix/4411,unix/smagdela-ThinkPad-T14s-Gen-4:/tmp/.ICE-unix/4411', 'GTK_EXE_PREFIX': '/snap/code/205/usr', 'INVOCATION_ID': '74ca5276f26f49b8bff19734f8ce64fb', 'PAPERSIZE': 'a4', 'XDG_MENU_PREFIX': 'gnome-', 'LC_ADDRESS': 'fr_FR.UTF-8', 'GNOME_SETUP_DISPLAY': ':1', 'XDG_RUNTIME_DIR': '/run/user/1000', 'GIT_USER': 'smagdelaine', 'DISPLAY': ':0', 'LOCPATH': '/snap/code/205/usr/lib/locale', 'LANG': 'en_US.UTF-8', 'XDG_CURRENT_DESKTOP': 'Unity', 'LC_TELEPHONE': 'fr_FR.UTF-8', 'GIO_MODULE_DIR_VSCODE_SNAP_ORIG': '', 'XDG_DATA_HOME': '/home/smagdela/snap/code/205/.local/share', 'XMODIFIERS': '@im=ibus', 'XDG_SESSION_DESKTOP': 'ubuntu', 'XAUTHORITY': '/run/user/1000/.mutter-Xwaylandauth.P6OWC3', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.avif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:*~=00;90:*#=00;90:*.bak=00;90:*.crdownload=00;90:*.dpkg-dist=00;90:*.dpkg-new=00;90:*.dpkg-old=00;90:*.dpkg-tmp=00;90:*.old=00;90:*.orig=00;90:*.part=00;90:*.rej=00;90:*.rpmnew=00;90:*.rpmorig=00;90:*.rpmsave=00;90:*.swp=00;90:*.tmp=00;90:*.ucf-dist=00;90:*.ucf-new=00;90:*.ucf-old=00;90:', 'SSH_AUTH_SOCK': '/run/user/1000/keyring/ssh', 'GSETTINGS_SCHEMA_DIR': '/home/smagdela/snap/code/205/.local/share/glib-2.0/schemas', 'SHELL': '/usr/bin/zsh', 'LC_NAME': 'fr_FR.UTF-8', 'QT_ACCESSIBILITY': '1', 'GDMSESSION': 'ubuntu', 'GTK_PATH_VSCODE_SNAP_ORIG': '', 'FONTCONFIG_FILE': '/etc/fonts/fonts.conf', 'GTK_IM_MODULE_FILE_VSCODE_SNAP_ORIG': '', 'LC_MEASUREMENT': 'fr_FR.UTF-8', 'GJS_DEBUG_OUTPUT': 'stderr', 'LC_IDENTIFICATION': 'fr_FR.UTF-8', 'QT_IM_MODULE': 'ibus', 'PWD': '/home/smagdela/Documents/qiskit-scaleway/tests', 'NVM_CD_FLAGS': '-q', 'XDG_DATA_DIRS': '/home/smagdela/snap/code/205/.local/share:/home/smagdela/snap/code/205:/snap/code/205/usr/share:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'LC_NUMERIC': 'fr_FR.UTF-8', 'LC_PAPER': 'fr_FR.UTF-8', 'MEMORY_PRESSURE_WRITE': 'c29tZSAyMDAwMDAgMjAwMDAwMAA=', 'VTE_VERSION': '7800', 'CHROME_DESKTOP': 'code.desktop', 'ORIGINAL_XDG_CURRENT_DESKTOP': 'ubuntu:GNOME', 'GDK_BACKEND': 'x11', 'TERM_PROGRAM': 'vscode', 'TERM_PROGRAM_VERSION': '1.103.2', 'PYDEVD_DISABLE_FILE_VALIDATION': '1', 'VSCODE_DEBUGPY_ADAPTER_ENDPOINTS': '/home/smagdela/.vscode/extensions/ms-python.debugpy-2025.10.0-linux-x64/.noConfigDebugAdapterEndpoints/endpoint-6061cecf75d47dd9.txt', 'BUNDLED_DEBUGPY_PATH': '/home/smagdela/.vscode/extensions/ms-python.debugpy-2025.10.0-linux-x64/bundled/libs/debugpy', 'GIT_ASKPASS': '/snap/code/205/usr/share/code/resources/app/extensions/git/dist/askpass.sh', 'VSCODE_GIT_ASKPASS_NODE': '/snap/code/205/usr/share/code/code', 'VSCODE_GIT_ASKPASS_EXTRA_ARGS': '', 'VSCODE_GIT_ASKPASS_MAIN': '/snap/code/205/usr/share/code/resources/app/extensions/git/dist/askpass-main.js', 'VSCODE_GIT_IPC_HANDLE': '/run/user/1000/vscode-git-2ba3223998.sock', 'VSCODE_INJECTION': '1', 'ZDOTDIR': '/home/smagdela', 'USER_ZDOTDIR': '/home/smagdela', 'VIRTUAL_ENV': '/home/smagdela/Documents/qiskit-scaleway/.venv', 'PS1': '(.venv) %(?:%{\x1b[01;32m%}%1{➜%} :%{\x1b[01;31m%}%1{➜%} ) %{\x1b[36m%}%c%{\x1b[00m%} $(git_prompt_info)', 'VIRTUAL_ENV_PROMPT': '(.venv) ', '_': '/home/smagdela/Documents/qiskit-scaleway/.venv/bin/pytest', 'PYTEST_VERSION': '8.4.2', 'QISKIT_IN_PARALLEL': 'FALSE', 'PYTEST_CURRENT_TEST': 'tests/test_aer_multiple_circuits.py::test_aer_multiple_circuits (call)', 'QISKIT_SCALEWAY_BACKEND_NAME': 'aer_simulation_local'} \ No newline at end of file diff --git a/tests/test_estimator.py b/tests/test_estimator.py index 061f871..0f34e57 100644 --- a/tests/test_estimator.py +++ b/tests/test_estimator.py @@ -32,7 +32,7 @@ def test_estimator(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend("aer_simulation_pop_c16m128") + backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) assert backend is not None diff --git a/tests/test_sampler.py b/tests/test_sampler.py index 6eacc70..9ae1a7c 100644 --- a/tests/test_sampler.py +++ b/tests/test_sampler.py @@ -30,7 +30,7 @@ def test_sampler(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend("aer_simulation_pop_c16m128") + backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) assert backend is not None From dd7eb2b6ad1aacaca328cf1a3804c4a05a94f96f Mon Sep 17 00:00:00 2001 From: smaugdela Date: Fri, 12 Sep 2025 10:53:11 +0200 Subject: [PATCH 02/13] Added protection, as well as reformated tests so that it properly tests what is relevant (cannot get noise errors if there are no instructions). --- qiskit_scaleway/backends/base_job.py | 2 +- tests/test_aer_multiple_circuits.py | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 4146164..4d01454 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -89,7 +89,7 @@ def submit(self, session_id: str) -> None: ), ) - if options.get("noise_model"): + if options and options.get("noise_model"): options["noise_model"] = options["noise_model"].to_dict(True) backend_data = QaaSJobBackendData( diff --git a/tests/test_aer_multiple_circuits.py b/tests/test_aer_multiple_circuits.py index db8522f..253c303 100644 --- a/tests/test_aer_multiple_circuits.py +++ b/tests/test_aer_multiple_circuits.py @@ -97,8 +97,8 @@ def _get_noise_model(): import qiskit_aer.noise as noise # Error probabilities - prob_1 = 0.001 # 1-qubit gate - prob_2 = 0.01 # 2-qubit gate + prob_1 = 0.01 # 1-qubit gate + prob_2 = 0.1 # 2-qubit gate # Depolarizing quantum errors error_1 = noise.depolarizing_error(prob_1, 1) @@ -120,10 +120,9 @@ def _bell_state_circuit(): return qc -def _simple_one_state_circuit(init_state: Literal["0", "1"] = "0"): +def _simple_one_state_circuit(): qc = QuantumCircuit(1, 1) - if init_state == "1": - qc.x(0) + qc.x(0) qc.measure_all() return qc @@ -150,18 +149,17 @@ def test_aer_with_noise_model(): try: qc1 = _bell_state_circuit() - qc2 = _simple_one_state_circuit('0') - qc3 = _simple_one_state_circuit('1') + qc2 = _simple_one_state_circuit() run_ideal_result = backend.run( - [qc1, qc2, qc3], + [qc1, qc2], shots=1000, max_parallel_experiments=0, session_id=session_id, ).result() run_noisy_result = backend.run( - [qc1, qc2, qc3], + [qc1, qc2], shots=1000, max_parallel_experiments=0, session_id=session_id, @@ -171,12 +169,10 @@ def test_aer_with_noise_model(): ideal_results = run_ideal_result.results noisy_results = run_noisy_result.results - assert len(ideal_results) == len(noisy_results) == 3 + assert len(ideal_results) == len(noisy_results) == 2 for i, ideal_result in enumerate(ideal_results): assert len(ideal_result.data.counts) < len(noisy_results[i].data.counts) finally: backend.delete_session(session_id) - -{'GJS_DEBUG_TOPICS': 'JS ERROR;JS LOG', 'LANGUAGE': 'en', 'USER': 'smagdela', 'LC_TIME': 'fr_FR.UTF-8', 'FONTCONFIG_PATH': '/etc/fonts', 'GIO_MODULE_DIR': '/home/smagdela/snap/code/common/.cache/gio-modules', 'XDG_SESSION_TYPE': 'wayland', 'GTK_EXE_PREFIX_VSCODE_SNAP_ORIG': '', 'GDK_BACKEND_VSCODE_SNAP_ORIG': '', 'SHLVL': '2', 'LESS': '-R', 'HOME': '/home/smagdela', 'LOCPATH_VSCODE_SNAP_ORIG': '', 'OLDPWD': '/home/smagdela/Documents/qiskit-scaleway', 'DESKTOP_SESSION': 'ubuntu', 'GTK_PATH': '/snap/code/205/usr/lib/x86_64-linux-gnu/gtk-3.0', 'NVM_BIN': '/home/smagdela/.nvm/versions/node/v24.7.0/bin', 'LSCOLORS': 'Gxfxcxdxbxegedabagacad', 'NVM_INC': '/home/smagdela/.nvm/versions/node/v24.7.0/include/node', 'ZSH': '/home/smagdela/.oh-my-zsh', 'XDG_DATA_HOME_VSCODE_SNAP_ORIG': '', 'GTK_IM_MODULE_FILE': '/home/smagdela/snap/code/common/.cache/immodules/immodules.cache', 'GNOME_SHELL_SESSION_MODE': 'ubuntu', 'GTK_MODULES': 'gail:atk-bridge', 'GSETTINGS_SCHEMA_DIR_VSCODE_SNAP_ORIG': '', 'PAGER': 'less', 'LC_MONETARY': 'fr_FR.UTF-8', 'MANAGERPID': '4094', 'SYSTEMD_EXEC_PID': '4472', 'GSM_SKIP_SSH_AGENT_WORKAROUND': 'true', 'DBUS_SESSION_BUS_ADDRESS': 'unix:path=/run/user/1000/bus', 'GIT_TOKEN': 'P-FDofUbEdSBTpPkuUGxJG86MQp1OjJ6dgk.01.0z159r3sl', 'COLORTERM': 'truecolor', 'NVM_DIR': '/home/smagdela/.nvm', 'WAYLAND_DISPLAY': 'wayland-0', 'LOGNAME': 'smagdela', 'JOURNAL_STREAM': '9:33927', 'XDG_CONFIG_DIRS_VSCODE_SNAP_ORIG': '', 'MEMORY_PRESSURE_WATCH': '/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/session.slice/org.gnome.Shell@wayland.service/memory.pressure', 'XDG_SESSION_CLASS': 'user', 'XDG_DATA_DIRS_VSCODE_SNAP_ORIG': '/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'USERNAME': 'smagdela', 'TERM': 'xterm-256color', 'GNOME_DESKTOP_SESSION_ID': 'this-is-deprecated', 'PATH': '/home/smagdela/Documents/qiskit-scaleway/.venv/bin:/home/smagdela/.nvm/versions/node/v24.7.0/bin:/home/smagdela/.local/bin:/home/smagdela/.local/share/gnome-shell/extensions/ddterm@amezin.github.com/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin:/home/smagdela/.vscode/extensions/ms-python.debugpy-2025.10.0-linux-x64/bundled/scripts/noConfigScripts', 'SESSION_MANAGER': 'local/smagdela-ThinkPad-T14s-Gen-4:@/tmp/.ICE-unix/4411,unix/smagdela-ThinkPad-T14s-Gen-4:/tmp/.ICE-unix/4411', 'GTK_EXE_PREFIX': '/snap/code/205/usr', 'INVOCATION_ID': '74ca5276f26f49b8bff19734f8ce64fb', 'PAPERSIZE': 'a4', 'XDG_MENU_PREFIX': 'gnome-', 'LC_ADDRESS': 'fr_FR.UTF-8', 'GNOME_SETUP_DISPLAY': ':1', 'XDG_RUNTIME_DIR': '/run/user/1000', 'GIT_USER': 'smagdelaine', 'DISPLAY': ':0', 'LOCPATH': '/snap/code/205/usr/lib/locale', 'LANG': 'en_US.UTF-8', 'XDG_CURRENT_DESKTOP': 'Unity', 'LC_TELEPHONE': 'fr_FR.UTF-8', 'GIO_MODULE_DIR_VSCODE_SNAP_ORIG': '', 'XDG_DATA_HOME': '/home/smagdela/snap/code/205/.local/share', 'XMODIFIERS': '@im=ibus', 'XDG_SESSION_DESKTOP': 'ubuntu', 'XAUTHORITY': '/run/user/1000/.mutter-Xwaylandauth.P6OWC3', 'LS_COLORS': 'rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=00:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.avif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:*~=00;90:*#=00;90:*.bak=00;90:*.crdownload=00;90:*.dpkg-dist=00;90:*.dpkg-new=00;90:*.dpkg-old=00;90:*.dpkg-tmp=00;90:*.old=00;90:*.orig=00;90:*.part=00;90:*.rej=00;90:*.rpmnew=00;90:*.rpmorig=00;90:*.rpmsave=00;90:*.swp=00;90:*.tmp=00;90:*.ucf-dist=00;90:*.ucf-new=00;90:*.ucf-old=00;90:', 'SSH_AUTH_SOCK': '/run/user/1000/keyring/ssh', 'GSETTINGS_SCHEMA_DIR': '/home/smagdela/snap/code/205/.local/share/glib-2.0/schemas', 'SHELL': '/usr/bin/zsh', 'LC_NAME': 'fr_FR.UTF-8', 'QT_ACCESSIBILITY': '1', 'GDMSESSION': 'ubuntu', 'GTK_PATH_VSCODE_SNAP_ORIG': '', 'FONTCONFIG_FILE': '/etc/fonts/fonts.conf', 'GTK_IM_MODULE_FILE_VSCODE_SNAP_ORIG': '', 'LC_MEASUREMENT': 'fr_FR.UTF-8', 'GJS_DEBUG_OUTPUT': 'stderr', 'LC_IDENTIFICATION': 'fr_FR.UTF-8', 'QT_IM_MODULE': 'ibus', 'PWD': '/home/smagdela/Documents/qiskit-scaleway/tests', 'NVM_CD_FLAGS': '-q', 'XDG_DATA_DIRS': '/home/smagdela/snap/code/205/.local/share:/home/smagdela/snap/code/205:/snap/code/205/usr/share:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop', 'LC_NUMERIC': 'fr_FR.UTF-8', 'LC_PAPER': 'fr_FR.UTF-8', 'MEMORY_PRESSURE_WRITE': 'c29tZSAyMDAwMDAgMjAwMDAwMAA=', 'VTE_VERSION': '7800', 'CHROME_DESKTOP': 'code.desktop', 'ORIGINAL_XDG_CURRENT_DESKTOP': 'ubuntu:GNOME', 'GDK_BACKEND': 'x11', 'TERM_PROGRAM': 'vscode', 'TERM_PROGRAM_VERSION': '1.103.2', 'PYDEVD_DISABLE_FILE_VALIDATION': '1', 'VSCODE_DEBUGPY_ADAPTER_ENDPOINTS': '/home/smagdela/.vscode/extensions/ms-python.debugpy-2025.10.0-linux-x64/.noConfigDebugAdapterEndpoints/endpoint-6061cecf75d47dd9.txt', 'BUNDLED_DEBUGPY_PATH': '/home/smagdela/.vscode/extensions/ms-python.debugpy-2025.10.0-linux-x64/bundled/libs/debugpy', 'GIT_ASKPASS': '/snap/code/205/usr/share/code/resources/app/extensions/git/dist/askpass.sh', 'VSCODE_GIT_ASKPASS_NODE': '/snap/code/205/usr/share/code/code', 'VSCODE_GIT_ASKPASS_EXTRA_ARGS': '', 'VSCODE_GIT_ASKPASS_MAIN': '/snap/code/205/usr/share/code/resources/app/extensions/git/dist/askpass-main.js', 'VSCODE_GIT_IPC_HANDLE': '/run/user/1000/vscode-git-2ba3223998.sock', 'VSCODE_INJECTION': '1', 'ZDOTDIR': '/home/smagdela', 'USER_ZDOTDIR': '/home/smagdela', 'VIRTUAL_ENV': '/home/smagdela/Documents/qiskit-scaleway/.venv', 'PS1': '(.venv) %(?:%{\x1b[01;32m%}%1{➜%} :%{\x1b[01;31m%}%1{➜%} ) %{\x1b[36m%}%c%{\x1b[00m%} $(git_prompt_info)', 'VIRTUAL_ENV_PROMPT': '(.venv) ', '_': '/home/smagdela/Documents/qiskit-scaleway/.venv/bin/pytest', 'PYTEST_VERSION': '8.4.2', 'QISKIT_IN_PARALLEL': 'FALSE', 'PYTEST_CURRENT_TEST': 'tests/test_aer_multiple_circuits.py::test_aer_multiple_circuits (call)', 'QISKIT_SCALEWAY_BACKEND_NAME': 'aer_simulation_local'} \ No newline at end of file From 1211db7718b63cd6fdf5a79b65c44042ad13bd1c Mon Sep 17 00:00:00 2001 From: smaugdela Date: Fri, 12 Sep 2025 14:02:06 +0200 Subject: [PATCH 03/13] NoiseModel now follows the QaaSxxxData type models. --- qiskit_scaleway/backends/base_job.py | 5 +++-- tests/test_aer_multiple_circuits.py | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 4d01454..28a90be 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -34,6 +34,7 @@ QaaSJobRunData, QaaSJobBackendData, QaaSCircuitSerializationFormat, + QaaSNoiseModelData, ) @@ -89,8 +90,7 @@ def submit(self, session_id: str) -> None: ), ) - if options and options.get("noise_model"): - options["noise_model"] = options["noise_model"].to_dict(True) + noise_model = QaaSNoiseModelData(noise_model=options.pop("noise_model", None).to_dict(True)) if options.get("noise_model") else None backend_data = QaaSJobBackendData( name=self.backend().name, @@ -107,6 +107,7 @@ def submit(self, session_id: str) -> None: backend=backend_data, run=run_data, client=client_data, + noise_model=noise_model, ) ) diff --git a/tests/test_aer_multiple_circuits.py b/tests/test_aer_multiple_circuits.py index 253c303..1226aba 100644 --- a/tests/test_aer_multiple_circuits.py +++ b/tests/test_aer_multiple_circuits.py @@ -96,7 +96,7 @@ def test_aer_multiple_circuits(): def _get_noise_model(): import qiskit_aer.noise as noise - # Error probabilities + # Error probabilities (exaggerated to get a noticeable effect for demonstration) prob_1 = 0.01 # 1-qubit gate prob_2 = 0.1 # 2-qubit gate @@ -175,4 +175,3 @@ def test_aer_with_noise_model(): assert len(ideal_result.data.counts) < len(noisy_results[i].data.counts) finally: backend.delete_session(session_id) - From caa6a18ca0285b7a58d3de070c1d6e1a2acd3f9d Mon Sep 17 00:00:00 2001 From: smaugdela Date: Tue, 16 Sep 2025 13:55:32 +0200 Subject: [PATCH 04/13] WIP implementing QaaSNoiseModelData model. --- qiskit_scaleway/backends/base_job.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 28a90be..0476476 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -90,7 +90,11 @@ def submit(self, session_id: str) -> None: ), ) - noise_model = QaaSNoiseModelData(noise_model=options.pop("noise_model", None).to_dict(True)) if options.get("noise_model") else None + if options.get("noise_model"): + noise_model = QaaSNoiseModelData( + serialization_format = QaaSCircuitSerializationFormat.JSON, + noise_model_serialization = json.dumps(noise_model.to_dict(True)) + ) backend_data = QaaSJobBackendData( name=self.backend().name, From 98d3ef0074802a3c1dbdaed79049ae5f028a0e05 Mon Sep 17 00:00:00 2001 From: smaugdela Date: Tue, 16 Sep 2025 18:10:48 +0200 Subject: [PATCH 05/13] Done implementing the noise_model feature. --- qiskit_scaleway/backends/base_job.py | 6 ++++-- tests/test_aer_multiple_circuits.py | 4 ---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 0476476..9533b88 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -35,6 +35,7 @@ QaaSJobBackendData, QaaSCircuitSerializationFormat, QaaSNoiseModelData, + QaaSNoiseModelSerializationFormat, ) @@ -90,9 +91,10 @@ def submit(self, session_id: str) -> None: ), ) - if options.get("noise_model"): + noise_model = options.pop("noise_model", None) + if noise_model: noise_model = QaaSNoiseModelData( - serialization_format = QaaSCircuitSerializationFormat.JSON, + serialization_format = QaaSNoiseModelSerializationFormat.JSON, noise_model_serialization = json.dumps(noise_model.to_dict(True)) ) diff --git a/tests/test_aer_multiple_circuits.py b/tests/test_aer_multiple_circuits.py index 1226aba..0084fe8 100644 --- a/tests/test_aer_multiple_circuits.py +++ b/tests/test_aer_multiple_circuits.py @@ -12,16 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -from typing import Literal import numpy as np import random -import dotenv from qiskit import QuantumCircuit from qiskit_scaleway import ScalewayProvider -dotenv.load_dotenv() - def _random_qiskit_circuit(size: int) -> QuantumCircuit: num_qubits = size From 575a51a00624cb2a45e9804c056d0c07e9890b0b Mon Sep 17 00:00:00 2001 From: smaugdela Date: Fri, 26 Sep 2025 16:31:53 +0200 Subject: [PATCH 06/13] WIP implementing patch to make all noise models compatible on AER. --- qiskit_scaleway/backends/base_job.py | 35 ++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 9533b88..681f410 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -14,6 +14,7 @@ import time import httpx import json +import numpy as np import randomname from typing import List, Union, Optional, Dict @@ -94,9 +95,13 @@ def submit(self, session_id: str) -> None: noise_model = options.pop("noise_model", None) if noise_model: noise_model = QaaSNoiseModelData( - serialization_format = QaaSNoiseModelSerializationFormat.JSON, - noise_model_serialization = json.dumps(noise_model.to_dict(True)) + serialization_format = QaaSNoiseModelSerializationFormat.PATCHED_JSON, + noise_model_serialization = json.dumps(_encode_numpy_complex(noise_model.to_dict(False))) ) + # noise_model = QaaSNoiseModelData( + # serialization_format = QaaSNoiseModelSerializationFormat.JSON, + # noise_model_serialization = json.dumps(noise_model.to_dict(True)) + # ) backend_data = QaaSJobBackendData( name=self.backend().name, @@ -204,3 +209,29 @@ def _wait_for_result( raise JobError("Job error") time.sleep(fetch_interval) + + +def _encode_numpy_complex(obj): + """ + Recursively traverses a structure and converts numpy arrays and + complex numbers into a JSON-serializable format. + """ + if isinstance(obj, np.ndarray): + return { + '__ndarray__': True, + 'data': _encode_numpy_complex(obj.tolist()), # Recursively encode data + 'dtype': obj.dtype.name, + 'shape': obj.shape, + } + elif isinstance(obj, (complex, np.complex128)): + return { + '__complex__': True, + 'real': obj.real, + 'imag': obj.imag + } + elif isinstance(obj, dict): + return {key: _encode_numpy_complex(value) for key, value in obj.items()} + elif isinstance(obj, (list, tuple)): + return [_encode_numpy_complex(item) for item in obj] + else: + return obj From 8feecff9ed2ef35ec835e67bc9980dd22703f7e2 Mon Sep 17 00:00:00 2001 From: smaugdela Date: Mon, 29 Sep 2025 16:37:18 +0200 Subject: [PATCH 07/13] Added compression logic to avoid issues with request content being huge. --- qiskit_scaleway/backends/base_job.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 681f410..a3c983e 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import time +import zlib import httpx import json import numpy as np @@ -94,14 +95,17 @@ def submit(self, session_id: str) -> None: noise_model = options.pop("noise_model", None) if noise_model: + noise_model_dict = _encode_numpy_complex(noise_model.to_dict(False)) noise_model = QaaSNoiseModelData( serialization_format = QaaSNoiseModelSerializationFormat.PATCHED_JSON, - noise_model_serialization = json.dumps(_encode_numpy_complex(noise_model.to_dict(False))) + noise_model_serialization = zlib.compress(json.dumps(noise_model_dict).encode()), ) + ### Uncomment to use standard JSON serialization, provided there is no more issue with AER deserialization logic # noise_model = QaaSNoiseModelData( # serialization_format = QaaSNoiseModelSerializationFormat.JSON, - # noise_model_serialization = json.dumps(noise_model.to_dict(True)) + # noise_model_serialization = json.dumps(noise_model.to_dict(True)).encode() # ) + ### backend_data = QaaSJobBackendData( name=self.backend().name, From 828a53c5a79ec5d1e101518f63212f3e7a41f448 Mon Sep 17 00:00:00 2001 From: smaugdela Date: Fri, 3 Oct 2025 17:09:08 +0200 Subject: [PATCH 08/13] WIP upgrading to qiskit 2. Unit tests not passing for qsim, aqt and sampler. --- qiskit_scaleway/backends/aer/backend.py | 33 ++++++++++++++--- qiskit_scaleway/primitives/estimator.py | 2 +- qiskit_scaleway/primitives/estimator_v1.py | 41 ---------------------- requirements.txt | 2 +- 4 files changed, 31 insertions(+), 47 deletions(-) delete mode 100644 qiskit_scaleway/primitives/estimator_v1.py diff --git a/qiskit_scaleway/backends/aer/backend.py b/qiskit_scaleway/backends/aer/backend.py index 41d0424..32ac412 100644 --- a/qiskit_scaleway/backends/aer/backend.py +++ b/qiskit_scaleway/backends/aer/backend.py @@ -11,7 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from qiskit.providers import Options, convert_to_target +from typing import List +from qiskit.providers import Options +from qiskit.transpiler import Target from qiskit_aer.backends.aer_simulator import BASIS_GATES, AerBackendConfiguration from qiskit_aer.backends.aerbackend import NAME_MAPPING @@ -49,9 +51,14 @@ def __init__(self, provider, client: QaaSClient, platform: QaaSPlatform): } ) self._properties = None - self._target = convert_to_target( - self._configuration, self._properties, None, NAME_MAPPING - ) + + self._target = self._convert_to_target() + + ### LEGACY qiskit 1.4 + # self._target = convert_to_target( + # self._configuration, properties=self._properties, defaults=None, custom_name_mapping=NAME_MAPPING + # ) + ### def __repr__(self) -> str: return f"" @@ -106,3 +113,21 @@ def _default_options(self): fusion_max_qubit=None, fusion_threshold=None, ) + + def _convert_to_target(self) -> Target: + args_lis: List[str] = [ + "basis_gates", + "num_qubits", + "coupling_map", + "instruction_durations", + "concurrent_measurements", + "dt", + "timing_constraints", + "custom_name_mapping", + ] + + conf_dict = self._configuration.to_dict() + if conf_dict.get("custom_name_mapping") is None: + conf_dict["custom_name_mapping"] = NAME_MAPPING + + return Target.from_configuration(**{k: conf_dict[k] for k in args_lis if k in conf_dict}) diff --git a/qiskit_scaleway/primitives/estimator.py b/qiskit_scaleway/primitives/estimator.py index d75b7a1..8659564 100644 --- a/qiskit_scaleway/primitives/estimator.py +++ b/qiskit_scaleway/primitives/estimator.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from qiskit.primitives import BackendEstimatorV2 -from qiskit.primitives.backend_estimator import ( +from qiskit.primitives.backend_estimator_v2 import ( _prepare_counts, _run_circuits, ) diff --git a/qiskit_scaleway/primitives/estimator_v1.py b/qiskit_scaleway/primitives/estimator_v1.py deleted file mode 100644 index ecc8ac9..0000000 --- a/qiskit_scaleway/primitives/estimator_v1.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2025 Scaleway -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from qiskit.primitives import BackendEstimator - - -class Estimator(BackendEstimator): - def __init__( - self, - backend, - session_id: str, - options: dict | None = None, - abelian_grouping: bool = True, - skip_transpilation: bool = False, - ): - if not session_id: - raise Exception("session_id must be not None") - - self._session_id = session_id - - super().__init__( - backend, - bound_pass_manager=backend.get_pass_manager(), - options=options, - abelian_grouping=abelian_grouping, - skip_transpilation=skip_transpilation, - ) - - def _run(self, circuits, observables, parameter_values, **run_options): - run_options["session_id"] = self._session_id - return super()._run(circuits, observables, parameter_values, **run_options) diff --git a/requirements.txt b/requirements.txt index 7669c08..7c05d12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -qiskit==1.4.2 +qiskit~=2.1 qiskit-aer==0.17 randomname>=0.2.1 dataclasses-json>=0.6.4 From d08150c0c53d9015c32a86366958ed918798399f Mon Sep 17 00:00:00 2001 From: smaugdela Date: Mon, 6 Oct 2025 16:35:11 +0200 Subject: [PATCH 09/13] Unit tests passing on staging except for noise_model which is not present on staging right now. --- qiskit_scaleway/primitives/__init__.py | 1 - tests/test_sampler.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/qiskit_scaleway/primitives/__init__.py b/qiskit_scaleway/primitives/__init__.py index 8af5392..97e0f61 100644 --- a/qiskit_scaleway/primitives/__init__.py +++ b/qiskit_scaleway/primitives/__init__.py @@ -12,5 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. from .estimator import Estimator -from .estimator_v1 import Estimator as EstimatorV1 from .sampler import Sampler diff --git a/tests/test_sampler.py b/tests/test_sampler.py index 9ae1a7c..d003f77 100644 --- a/tests/test_sampler.py +++ b/tests/test_sampler.py @@ -15,8 +15,7 @@ import numpy as np import random -from qiskit import transpile -from qiskit.circuit.library import IQP +from qiskit.circuit.library import iqp from qiskit.quantum_info import random_hermitian from qiskit_scaleway import ScalewayProvider @@ -47,11 +46,12 @@ def test_sampler(): n_qubits = 10 mat = np.real(random_hermitian(n_qubits, seed=1234)) - circuit = IQP(mat) + circuit = iqp(mat) circuit.measure_all() - isa_circuit = transpile(circuit, backend=backend, optimization_level=1) - job = sampler.run([isa_circuit], shots=100) + # isa_circuit = transpile(circuit, backend=backend, optimization_level=1) + # job = sampler.run([isa_circuit], shots=100) + job = sampler.run([circuit], shots=100) result = job.result() assert result is not None From 96c1e779059ecfe7a686a05e006ff036ae499d0a Mon Sep 17 00:00:00 2001 From: smaugdela Date: Tue, 14 Oct 2025 14:19:38 +0200 Subject: [PATCH 10/13] Added var env loading for backend type in Qsim unit test. --- tests/test_qsim_simple_circuit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_qsim_simple_circuit.py b/tests/test_qsim_simple_circuit.py index 6940bfa..9e75766 100644 --- a/tests/test_qsim_simple_circuit.py +++ b/tests/test_qsim_simple_circuit.py @@ -25,7 +25,7 @@ def test_qsim_simple_circuit(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend("qsim_simulation_pop_c16m128") + backend = provider.get_backend(os.getenv("QSIM_SCALEWAY_BACKEND_NAME", "qsim_simulation_pop_c16m128")) assert backend is not None From e6767918142bffa961af79a01134ece340d74d30 Mon Sep 17 00:00:00 2001 From: smaugdela Date: Tue, 14 Oct 2025 14:21:08 +0200 Subject: [PATCH 11/13] Update to version 0.3: Now running Qiskit v2! --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c72513c..bafa476 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ setup( name="qiskit_scaleway", - version="0.2.12", + version="0.3.0", project_urls={ "Documentation": "https://www.scaleway.com/en/quantum-as-a-service/", "Source": "https://github.com/scaleway/qiskit-scaleway", From b3177bc481a912e7b07c7a3250dd92400ef51588 Mon Sep 17 00:00:00 2001 From: smaugdela Date: Tue, 14 Oct 2025 17:10:59 +0200 Subject: [PATCH 12/13] Reformatted using black. Deleted unecessary comments. Renamed NoiseModel serialization format to match the rest of the code. --- qiskit_scaleway/backends/aer/backend.py | 10 +++------- qiskit_scaleway/backends/base_job.py | 20 +++++++++----------- tests/test_aer_multiple_circuits.py | 16 ++++++++++------ tests/test_estimator.py | 4 +++- tests/test_qsim_simple_circuit.py | 4 +++- tests/test_sampler.py | 6 +++--- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/qiskit_scaleway/backends/aer/backend.py b/qiskit_scaleway/backends/aer/backend.py index 32ac412..a3330c9 100644 --- a/qiskit_scaleway/backends/aer/backend.py +++ b/qiskit_scaleway/backends/aer/backend.py @@ -54,12 +54,6 @@ def __init__(self, provider, client: QaaSClient, platform: QaaSPlatform): self._target = self._convert_to_target() - ### LEGACY qiskit 1.4 - # self._target = convert_to_target( - # self._configuration, properties=self._properties, defaults=None, custom_name_mapping=NAME_MAPPING - # ) - ### - def __repr__(self) -> str: return f"" @@ -130,4 +124,6 @@ def _convert_to_target(self) -> Target: if conf_dict.get("custom_name_mapping") is None: conf_dict["custom_name_mapping"] = NAME_MAPPING - return Target.from_configuration(**{k: conf_dict[k] for k in args_lis if k in conf_dict}) + return Target.from_configuration( + **{k: conf_dict[k] for k in args_lis if k in conf_dict} + ) diff --git a/qiskit_scaleway/backends/base_job.py b/qiskit_scaleway/backends/base_job.py index 61a83a4..1391ffd 100644 --- a/qiskit_scaleway/backends/base_job.py +++ b/qiskit_scaleway/backends/base_job.py @@ -98,8 +98,10 @@ def submit(self, session_id: str) -> None: if noise_model: noise_model_dict = _encode_numpy_complex(noise_model.to_dict(False)) noise_model = QaaSNoiseModelData( - serialization_format = QaaSNoiseModelSerializationFormat.PATCHED_JSON, - noise_model_serialization = zlib.compress(json.dumps(noise_model_dict).encode()), + serialization_format=QaaSNoiseModelSerializationFormat.AER_COMPRESSED_JSON, + noise_model_serialization=zlib.compress( + json.dumps(noise_model_dict).encode() + ), ) ### Uncomment to use standard JSON serialization, provided there is no more issue with AER deserialization logic # noise_model = QaaSNoiseModelData( @@ -224,17 +226,13 @@ def _encode_numpy_complex(obj): """ if isinstance(obj, np.ndarray): return { - '__ndarray__': True, - 'data': _encode_numpy_complex(obj.tolist()), # Recursively encode data - 'dtype': obj.dtype.name, - 'shape': obj.shape, + "__ndarray__": True, + "data": _encode_numpy_complex(obj.tolist()), # Recursively encode data + "dtype": obj.dtype.name, + "shape": obj.shape, } elif isinstance(obj, (complex, np.complex128)): - return { - '__complex__': True, - 'real': obj.real, - 'imag': obj.imag - } + return {"__complex__": True, "real": obj.real, "imag": obj.imag} elif isinstance(obj, dict): return {key: _encode_numpy_complex(value) for key, value in obj.items()} elif isinstance(obj, (list, tuple)): diff --git a/tests/test_aer_multiple_circuits.py b/tests/test_aer_multiple_circuits.py index 0084fe8..450614c 100644 --- a/tests/test_aer_multiple_circuits.py +++ b/tests/test_aer_multiple_circuits.py @@ -54,7 +54,9 @@ def test_aer_multiple_circuits(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) + backend = provider.get_backend( + os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128") + ) assert backend is not None @@ -94,7 +96,7 @@ def _get_noise_model(): # Error probabilities (exaggerated to get a noticeable effect for demonstration) prob_1 = 0.01 # 1-qubit gate - prob_2 = 0.1 # 2-qubit gate + prob_2 = 0.1 # 2-qubit gate # Depolarizing quantum errors error_1 = noise.depolarizing_error(prob_1, 1) @@ -102,8 +104,8 @@ def _get_noise_model(): # Add errors to noise model noise_model = noise.NoiseModel() - noise_model.add_all_qubit_quantum_error(error_1, ['rz', 'sx', 'x']) - noise_model.add_all_qubit_quantum_error(error_2, ['cx']) + noise_model.add_all_qubit_quantum_error(error_1, ["rz", "sx", "x"]) + noise_model.add_all_qubit_quantum_error(error_2, ["cx"]) return noise_model @@ -131,7 +133,9 @@ def test_aer_with_noise_model(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) + backend = provider.get_backend( + os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128") + ) assert backend is not None @@ -159,7 +163,7 @@ def test_aer_with_noise_model(): shots=1000, max_parallel_experiments=0, session_id=session_id, - noise_model=_get_noise_model() + noise_model=_get_noise_model(), ).result() ideal_results = run_ideal_result.results diff --git a/tests/test_estimator.py b/tests/test_estimator.py index 0f34e57..ba6938f 100644 --- a/tests/test_estimator.py +++ b/tests/test_estimator.py @@ -32,7 +32,9 @@ def test_estimator(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) + backend = provider.get_backend( + os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128") + ) assert backend is not None diff --git a/tests/test_qsim_simple_circuit.py b/tests/test_qsim_simple_circuit.py index 9e75766..c127648 100644 --- a/tests/test_qsim_simple_circuit.py +++ b/tests/test_qsim_simple_circuit.py @@ -25,7 +25,9 @@ def test_qsim_simple_circuit(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend(os.getenv("QSIM_SCALEWAY_BACKEND_NAME", "qsim_simulation_pop_c16m128")) + backend = provider.get_backend( + os.getenv("QSIM_SCALEWAY_BACKEND_NAME", "qsim_simulation_pop_c16m128") + ) assert backend is not None diff --git a/tests/test_sampler.py b/tests/test_sampler.py index d003f77..2ca5a7b 100644 --- a/tests/test_sampler.py +++ b/tests/test_sampler.py @@ -29,7 +29,9 @@ def test_sampler(): url=os.getenv("QISKIT_SCALEWAY_API_URL"), ) - backend = provider.get_backend(os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")) + backend = provider.get_backend( + os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128") + ) assert backend is not None @@ -49,8 +51,6 @@ def test_sampler(): circuit = iqp(mat) circuit.measure_all() - # isa_circuit = transpile(circuit, backend=backend, optimization_level=1) - # job = sampler.run([isa_circuit], shots=100) job = sampler.run([circuit], shots=100) result = job.result() From 8f7aaa2241e95f0a615a0987c2d0ef0ff815e5bb Mon Sep 17 00:00:00 2001 From: smaugdela Date: Wed, 15 Oct 2025 17:00:14 +0200 Subject: [PATCH 13/13] Update requirements.txt, as well as examples to match qiskit 2 API. --- examples/run_aqt_estimator.py | 8 ++++---- requirements.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/run_aqt_estimator.py b/examples/run_aqt_estimator.py index a5f46cb..b36018f 100644 --- a/examples/run_aqt_estimator.py +++ b/examples/run_aqt_estimator.py @@ -19,7 +19,7 @@ from qiskit.circuit.library import n_local from qiskit_scaleway import ScalewayProvider -from qiskit_scaleway.primitives import EstimatorV1 +from qiskit_scaleway.primitives import Estimator provider = ScalewayProvider( project_id="", @@ -33,7 +33,7 @@ session_id = backend.start_session(name="test-session", deduplication_id="my-workshop") # Create an estimator (v1) with the backend and the session -estimator = EstimatorV1(backend=backend, session_id=session_id) +estimator = Estimator(backend=backend, session_id=session_id) # Specify the problem Hamiltonian hamiltonian = SparsePauliOp.from_list( @@ -56,7 +56,7 @@ def cost_function( params, ansatz: QuantumCircuit, hamiltonian: BaseOperator, - estimator: EstimatorV1, + estimator: Estimator, ) -> float: """Cost function for the VQE. @@ -64,7 +64,7 @@ def cost_function( on the state prepared by the Ansatz circuit. """ return float( - estimator.run(ansatz, hamiltonian, parameter_values=params).result().values[0] + estimator.run([(ansatz, hamiltonian, params)]).result().values[0] ) diff --git a/requirements.txt b/requirements.txt index 7c05d12..2abc369 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ qiskit~=2.1 -qiskit-aer==0.17 +qiskit-aer~=0.17 randomname>=0.2.1 dataclasses-json>=0.6.4 dataclasses>=0.6 -scaleway-qaas-client>=0.1.16 \ No newline at end of file +scaleway-qaas-client>=0.1.23 \ No newline at end of file