Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4213a95
add loss policies to `NoiseConfig` and support for it in all QIR simu…
orpuente-MS Jun 10, 2026
021b21d
start NoisePolicy enum from 0
orpuente-MS Jun 13, 2026
1af491a
fix test comment
orpuente-MS Jun 13, 2026
941d453
reuse is_1q_op
orpuente-MS Jun 13, 2026
8ee6259
noise policies only on multi-qubit gates + always apply idle noise on…
orpuente-MS Jun 16, 2026
91350db
add warning when setting `.on_loss` on single-qubit gate
orpuente-MS Jun 16, 2026
640f973
more reasonable swap behavior
orpuente-MS Jun 18, 2026
885da6a
store policy in `policy` field instead of `q3`
orpuente-MS Jun 18, 2026
badd30c
Merge branch 'main' into oscarpuente/qubit-loss-policies
orpuente-MS Jun 19, 2026
ce23c8d
Merge branch 'main' into oscarpuente/qubit-loss-policies
orpuente-MS Jun 20, 2026
6ef40fc
fix struct size calculation after merge
orpuente-MS Jun 20, 2026
52a758b
fix flaky tests after merge
orpuente-MS Jun 20, 2026
abb2aec
deny loss policies that don't make sense
orpuente-MS Jun 26, 2026
001a90a
add errors for invalid loss policies in simulators
orpuente-MS Jun 26, 2026
8846322
add tests for allowed and forbiden policy combinations
orpuente-MS Jun 26, 2026
cf8e841
fix LossPolicy enum docstrings
orpuente-MS Jun 26, 2026
45a0a9f
fix typo
orpuente-MS Jun 26, 2026
5d3f33a
Merge branch 'main' into oscarpuente/qubit-loss-policies
orpuente-MS Jun 26, 2026
b14acb9
fix errors after merge
orpuente-MS Jun 26, 2026
d3dbd7e
apply pauli noise after noise-policy handling in GPU simulator
orpuente-MS Jun 26, 2026
1a41741
fix LossPolicy python type signature
orpuente-MS Jun 26, 2026
3d18306
clarify `DEFAULT_SINGLE_QUBIT_LOSS_POLICIES` semantics
orpuente-MS Jun 29, 2026
0b09fdf
Merge branch 'main' into oscarpuente/qubit-loss-policies
orpuente-MS Jun 29, 2026
05dae7c
Merge branch 'main' into oscarpuente/qubit-loss-policies
orpuente-MS Jun 30, 2026
bceb1ab
Merge branch 'main' into oscarpuente/qubit-loss-policies
orpuente-MS Jul 7, 2026
fbce4ad
add `mz`, `mresetz`, and `mov` to single-qubit-gates match
orpuente-MS Jul 7, 2026
e4b0b83
document swap assumption in `NoiseConfig` python API
orpuente-MS Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion source/compiler/qsc_eval/src/backend/noise_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
use expect_test::{Expect, expect};
use num_bigint::BigUint;
use num_complex::Complex;
use qdk_simulators::noise_config::{NoiseConfig, NoiseTable, encode_pauli};
use qdk_simulators::noise_config::{LossPolicy, NoiseConfig, NoiseTable, encode_pauli};
use std::fmt::Write;

#[test]
Expand Down Expand Up @@ -258,6 +258,7 @@ fn noise_config_with_single_qubit_fault(
qubits: 1,
pauli_strings: vec![encode_pauli(pauli)],
probabilities: vec![1.0],
on_loss: LossPolicy::Skip,
};
set_gate(&mut config, table);
config
Expand All @@ -274,6 +275,7 @@ fn noise_config_with_two_qubit_fault(
qubits: 2,
pauli_strings: vec![encode_pauli(pauli)],
probabilities: vec![1.0],
on_loss: LossPolicy::Skip,
};
set_gate(&mut config, table);
config
Expand Down Expand Up @@ -525,6 +527,7 @@ fn noise_config_mz_with_loss() {
let mut config = NoiseConfig::NOISELESS;
config.mz = NoiseTable {
qubits: 1,
on_loss: LossPolicy::Skip,
pauli_strings: vec![encode_pauli("L")],
probabilities: vec![1.0],
};
Expand All @@ -545,6 +548,7 @@ fn noise_config_gate_loss_causes_measurement_loss() {
let mut config = NoiseConfig::NOISELESS;
config.x = NoiseTable {
qubits: 1,
on_loss: LossPolicy::Skip,
pauli_strings: vec![encode_pauli("L")],
probabilities: vec![1.0],
};
Expand Down
3 changes: 2 additions & 1 deletion source/compiler/stim_compiler/src/qir.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use qdk_simulators::noise_config::{NoiseConfig, NoiseTable, encode_pauli};
use qdk_simulators::noise_config::{LossPolicy, NoiseConfig, NoiseTable, encode_pauli};

use crate::parser::*;
use miette::Diagnostic;
Expand Down Expand Up @@ -975,6 +975,7 @@ impl<'noise> Compiler<'noise> {
qubits: columns.len() as u32,
pauli_strings,
probabilities,
on_loss: LossPolicy::Skip,
};
self.noise.intrinsics.insert(id, table);

Expand Down
26 changes: 26 additions & 0 deletions source/qdk_package/qdk/_native.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,31 @@ class QirInstruction: ...
class IdleNoiseParams:
s_probability: float

class LossPolicy(Enum):
"""
Specifies the behavior of a multi-qubit gate when at least one of its
qubit operands is lost.
"""

# If any operand of a gate is lost, skip the gate entirely.
# This policy can apply to all multi-qubit gates.
SKIP = 0
# If any operand of a gate is lost, propagate the loss to the other operands.
# This policy can apply to all multi-qubit gates.
PROPAGATE = 1
# For multi-qubit rotations, degrade the unitary to its single-qubit version
# on the surviving operand (e.g. rxx -> rx). Falls back to SKIP for gates with
# no single-qubit reduction (cx, cy, cz, swap, and single-qubit gates).
# This policy only applies to the rxx, ryy, and rzz gates, in which case
# they degrade to rx, ry, and rz on the remaining qubit respectively.
DEGRADE = 2
# Skip the gate and instead apply an S adjoint to each surviving operand.
# This policy can apply to all multi-qubit gates.
RESIDUAL_S_DAGGER = 3
# This policy only applies to the swap gate, in which case the qubit states
# are exchanged, including their loss flags.
APPLY_ANYWAY = 4

class NoiseTable:
# Deprecated. Setting `loss` distributes the per-qubit loss probability
# across the correlated loss fault strings ('L' for a single-qubit
Expand All @@ -901,6 +926,7 @@ class NoiseTable:
# `loss` reconstructs that per-qubit probability. Prefer setting the loss
# fault strings directly via `set_pauli_noise`.
loss: float
on_loss: LossPolicy

def __init__(self, num_qubits: int):
"""
Expand Down
7 changes: 6 additions & 1 deletion source/qdk_package/qdk/simulation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
to individual gate intrinsics to model depolarizing, bit-flip, phase-flip,
or correlated noise channels.

- :class:`~qdk.simulation.LossPolicy` — selects how a gate behaves when one of
its qubit operands is lost. Assign it to a noise table's ``on_loss`` attribute
(e.g. ``noise.cx.on_loss = LossPolicy.SKIP``).

- :func:`~qdk.simulation.run_qir` — simulates QIR as given in one of
three backend simulators: clifford, gpu or cpu.

Expand All @@ -26,7 +30,7 @@
"""

from .._device._atom import NeutralAtomDevice
from ._simulation import NoiseConfig, run_qir
from ._simulation import NoiseConfig, LossPolicy, run_qir
from ._noisy_simulator import (
NoisySimulatorError,
DensityMatrixSimulator,
Expand All @@ -40,6 +44,7 @@
__all__ = [
"NeutralAtomDevice",
"NoiseConfig",
"LossPolicy",
"run_qir",
"NoisySimulatorError",
"Operation",
Expand Down
1 change: 1 addition & 0 deletions source/qdk_package/qdk/simulation/_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
run_cpu_adaptive,
run_cpu_full_state,
NoiseConfig,
LossPolicy,
GpuContext,
try_create_gpu_adapter,
Result,
Expand Down
4 changes: 3 additions & 1 deletion source/qdk_package/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
},
noisy_simulator::register_noisy_simulator_submodule,
qir_simulation::{
IdleNoiseParams, NoiseConfig, NoiseTable, QirInstruction, QirInstructionId,
IdleNoiseParams, LossPolicy, NoiseConfig, NoiseTable, QirInstruction, QirInstructionId,
cpu_simulators::{
run_clifford, run_clifford_adaptive, run_cpu_adaptive, run_cpu_full_state,
},
Expand Down Expand Up @@ -105,6 +105,7 @@ fn verify_classes_are_sendable() {
is_send::<NoiseConfig>();
is_send::<NoiseTable>();
is_send::<IdleNoiseParams>();
is_send::<LossPolicy>();
}

#[pymodule]
Expand Down Expand Up @@ -134,6 +135,7 @@ fn _native<'a>(py: Python<'a>, m: &Bound<'a, PyModule>) -> PyResult<()> {
m.add_class::<NoiseConfig>()?;
m.add_class::<NoiseTable>()?;
m.add_class::<IdleNoiseParams>()?;
m.add_class::<LossPolicy>()?;
m.add_function(wrap_pyfunction!(physical_estimates, m)?)?;
m.add_function(wrap_pyfunction!(run_clifford, m)?)?;
m.add_function(wrap_pyfunction!(try_create_gpu_adapter, m)?)?;
Expand Down
Loading
Loading