Skip to content

Fix sample_async noise model lifetime and isolation#3857

Merged
1tnguyen merged 10 commits intoNVIDIA:mainfrom
huaweil-nv:fix/sample-async-noise-model
Feb 24, 2026
Merged

Fix sample_async noise model lifetime and isolation#3857
1tnguyen merged 10 commits intoNVIDIA:mainfrom
huaweil-nv:fix/sample-async-noise-model

Conversation

@huaweil-nv
Copy link
Collaborator

Description

Summary

This PR fixes cudaq.sample_async(..., noise_model=...) for local simulators by ensuring the noise model is applied correctly during asynchronous execution and does not leak into subsequent calls.

Root cause

  • The async implementation could reference a noise model whose lifetime ended before the queued task executed.
  • Noise configuration was not scoped to the async task lifetime, which could cause state pollution.
  • Noise set/reset needed to be applied per-QPU (using the provided qpu_id).

Fix

  • Extend details::runSamplingAsync to accept an optional noise model and capture it by value inside the async task.
  • Set the noise model at the start of the async task and reset it on completion (including exception paths) to prevent state leakage.
  • Apply set/reset per-QPU using qpu_id.
  • Reject non-empty noise models on remote platforms with a clear error.
  • Update Python binding-side remote checks to respect the provided qpu_id.

Tests

Added regression tests in python/tests/builder/test_NoiseModel.py:

  • test_sample_async_with_noise
  • test_sample_async_noise_isolation

How to repro

import cudaq

cudaq.set_target("density-matrix-cpu")
cudaq.set_random_seed(42)

k = cudaq.make_kernel()
q = k.qalloc()
k.x(q)
k.mz(q)

noise = cudaq.NoiseModel()
noise.add_channel("x", [0], cudaq.DepolarizationChannel(1.0))

# Noise should be visible in async result.
noisy = cudaq.sample_async(k, shots_count=1000, noise_model=noise).get()
print("async noisy:", noisy)

# Subsequent calls without noise must remain clean (no state pollution).
clean = cudaq.sample(k, shots_count=200)
print("after clean:", clean)
assert clean.count("1") == 200

does not always occur

import cudaq, gc

cudaq.set_target("density-matrix-cpu")
cudaq.set_random_seed(42)

k = cudaq.make_kernel()
q = k.qalloc()
k.x(q)
k.mz(q)

def launch_once():
    # Noise model is intentionally scoped to this function.
    noise = cudaq.NoiseModel()
    noise.add_channel("x", [0], cudaq.DepolarizationChannel(1.0))
    fut = cudaq.sample_async(k, shots_count=200, noise_model=noise)
    return fut

futs = [launch_once() for _ in range(200)]
gc.collect()  # Try to encourage destruction of temporaries
for f in futs:
    _ = f.get()
print("done")

@huaweil-nv huaweil-nv requested review from 1tnguyen and sacpis February 4, 2026 10:03
@copy-pr-bot
Copy link

copy-pr-bot bot commented Feb 4, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@huaweil-nv huaweil-nv force-pushed the fix/sample-async-noise-model branch from 4d27d4d to 3bad7eb Compare February 4, 2026 11:06
Capture the noise model by value in async sampling tasks and set/reset it within
that task to avoid dangling pointers and state pollution. Also ensure noise is
applied/reset per-qpu and add Python regression tests covering async noise and
isolation.

Signed-off-by: huaweil <huaweil@nvidia.com>
@huaweil-nv huaweil-nv force-pushed the fix/sample-async-noise-model branch from 3bad7eb to dc99583 Compare February 4, 2026 11:08
Copy link
Collaborator

@sacpis sacpis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM. Thanks @huaweil-nv. Let's wait for Thien's review especially for the remote platform part.

Copy link
Collaborator

@1tnguyen 1tnguyen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@1tnguyen
Copy link
Collaborator

1tnguyen commented Feb 4, 2026

/ok to test 4714042

Command Bot: Processing...

github-actions bot pushed a commit that referenced this pull request Feb 4, 2026
@github-actions
Copy link

github-actions bot commented Feb 4, 2026

CUDA Quantum Docs Bot: A preview of the documentation can be found here.

@khalatepradnya
Copy link
Collaborator

Does this address #843?

Relax the DepolarizationChannel(p=1.0) expectation to match the theoretical
P(|0>) = 2/3 after an X gate and avoid finite-shot sampling flakiness.

Signed-off-by: huaweil <huaweil@nvidia.com>
@huaweil-nv
Copy link
Collaborator Author

Does this address #843?

in Python, cudaq.sample_async(..., noise_model=...) already accepts noise_model, but prior to this change it was not reliable (noise could be ignored and there was a lifetime/UB risk due to a dangling pointer). This PR makes async noise application correct and isolated by capturing the noise model in the async task and setting/resetting it within the task.
in C++, cudaq::sample_async(sample_options{ .noise = ... }) now properly applies the noise model in asynchronous execution (set/reset happens inside the task), and it is qpu_id-correct.

@1tnguyen 1tnguyen enabled auto-merge February 10, 2026 09:43
@1tnguyen
Copy link
Collaborator

1tnguyen commented Feb 10, 2026

/ok to test 78ce803

Command Bot: Processing...

@github-actions
Copy link

CUDA Quantum Docs Bot: A preview of the documentation can be found here.

@huaweil-nv
Copy link
Collaborator Author

huaweil-nv commented Feb 12, 2026

/ok to test 64bd8db

Command Bot: Processing...

@khalatepradnya
Copy link
Collaborator

khalatepradnya commented Feb 19, 2026

/ok to test 9130100

Command Bot: Processing...

Keep our fix: noise model is passed to runSamplingAsync and set/reset
within the async task, rather than setting it on the main thread which
would cause race conditions and state pollution.
@huaweil-nv
Copy link
Collaborator Author

huaweil-nv commented Feb 24, 2026

/ok to test 3b24303

Command Bot: Processing...

@1tnguyen 1tnguyen added this pull request to the merge queue Feb 24, 2026
github-actions bot pushed a commit that referenced this pull request Feb 24, 2026
@github-actions
Copy link

CUDA Quantum Docs Bot: A preview of the documentation can be found here.

Merged via the queue into NVIDIA:main with commit d9703ea Feb 24, 2026
206 of 207 checks passed
github-actions bot pushed a commit that referenced this pull request Feb 24, 2026
@bettinaheim bettinaheim added the bug fix To be listed under Bug Fixes in the release notes label Mar 11, 2026
@bettinaheim bettinaheim changed the title Fix sample_async noise model lifetime and isolation Fix sample_async noise model lifetime and isolation Mar 11, 2026
@bettinaheim bettinaheim added this to the release 0.14.0 milestone Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug fix To be listed under Bug Fixes in the release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants