Skip to content

Commit 5a3bb11

Browse files
authored
Merge 4a1a017 into 90459fa
2 parents 90459fa + 4a1a017 commit 5a3bb11

File tree

37 files changed

+1680
-125
lines changed

37 files changed

+1680
-125
lines changed

library/core/qir.qs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,9 @@ namespace QIR.Runtime {
2727
}
2828
}
2929

30-
export __quantum__rt__qubit_allocate, __quantum__rt__qubit_release, AllocateQubitArray, ReleaseQubitArray;
30+
function __quantum__rt__read_loss(r : Result) : Bool {
31+
body intrinsic;
32+
}
33+
34+
export __quantum__rt__qubit_allocate, __quantum__rt__qubit_release, AllocateQubitArray, ReleaseQubitArray, __quantum__rt__read_loss;
3135
}

library/std/src/Std/Diagnostics.qs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,30 @@ function ConfigurePauliNoise(px : Double, py : Double, pz : Double) : Unit {
389389
body intrinsic;
390390
}
391391

392+
/// # Summary
393+
/// Configures qubit loss during simulation.
394+
///
395+
/// # Description
396+
/// This function configures qubit loss for simulation. The parameter `p` represents
397+
/// the probability of a qubit loss during simulation. If `p` is greater than 0.0, the simulator will mark qubits
398+
/// as lost with the given probability during each operation that acts on them.
399+
/// Qubits that are lost are reset to the |0⟩ state but are not released. Loss is reported when the qubit is measured,
400+
/// and then the qubit is considered "reloaded" and can be used again.
401+
///
402+
/// # Input
403+
/// ## p
404+
/// The probability of a qubit being lost during simulation. Must be between 0.0 and 1.0.
405+
///
406+
/// # Remarks
407+
/// This operation is useful for simulating qubit loss for those modalities where qubit loss is a factor.
408+
/// Note that the value returned from a measurement of a lost qubit is neither `Zero` nor `One`, but rather a special
409+
/// value indicating that the qubit was lost. This value cannot be used in comparisons and will cause a runtime
410+
/// failure if compared to another value.
411+
/// To perform a measurement that includes a check for qubit loss, use the `MResetZChecked` operation.
412+
function ConfigureQubitLoss(p : Double) : Unit {
413+
body intrinsic;
414+
}
415+
392416
/// # Summary
393417
/// Applies configured noise to a qubit.
394418
///
@@ -445,6 +469,7 @@ export
445469
StartCountingQubits,
446470
StopCountingQubits,
447471
ConfigurePauliNoise,
472+
ConfigureQubitLoss,
448473
ApplyIdleNoise,
449474
BitFlipNoise,
450475
PhaseFlipNoise,

library/std/src/Std/Measurement.qs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import Std.Core.*;
66
import Std.Intrinsic.*;
77
import Std.Diagnostics.*;
8-
open QIR.Intrinsic;
8+
import QIR.Intrinsic.*;
9+
import QIR.Runtime.*;
910

1011
/// # Summary
1112
/// Jointly measures a register of qubits in the Pauli Z basis.
@@ -160,5 +161,51 @@ operation MeasureInteger(target : Qubit[]) : Int {
160161

161162
number
162163
}
163-
export MeasureAllZ, MeasureEachZ, MResetEachZ, MResetX, MResetY, MResetZ, MeasureInteger;
164164

165+
/// # Summary
166+
/// Performs a single-qubit measurement in the Pauli Z basis, resetting the `target` to the |0⟩ state after the measurement.
167+
/// Additionally, it checks if the measurement result indicates a loss and returns `true` when a loss is detected. If the qubit
168+
/// is lost, the result value will not be `Zero` or `One` and any use of that result in a comparison will cause a runtime failure.
169+
/// This operation is not supported on all hardware targets.
170+
///
171+
/// # Input
172+
/// ## target
173+
/// A single qubit to be measured.
174+
///
175+
/// # Output
176+
/// A tuple containing the measurement result and a Boolean `true` if the result indicates a loss, otherwise `false`.
177+
///
178+
/// # Remarks
179+
/// This operation is useful for detecting qubit loss during execution. During simulation, qubit loss probability can be
180+
/// configured via the `ConfigureQubitLoss` operation. When compiled to QIR, this uses the `__quantum__rt__read_loss` intrinsic,
181+
/// which may not be supported on all hardware targets and could result in compilation errors when submitting to those targets.
182+
operation MResetZChecked(target : Qubit) : (Result, Bool) {
183+
let res = MResetZ(target);
184+
(res, IsLossResult(res))
185+
}
186+
187+
/// # Summary
188+
/// Checks if the measurement result indicates qubit loss. Such measurement results are not `Zero` or `One`
189+
/// and using such a result in comparisons causes a runtime failure.
190+
/// This operation is not supported on all hardware targets.
191+
///
192+
/// # Input
193+
/// ## res
194+
/// The measurement result to check.
195+
///
196+
/// # Output
197+
/// A Boolean value indicating whether the result indicates a loss (`true`) or not (`false`).
198+
///
199+
/// # Remarks
200+
/// This operation is useful for detecting qubit loss during execution. After measurement result from qubit loss
201+
/// cannot be used in a comparison, and any attempt to do so will result in a runtime failure. During simulation, qubit loss
202+
/// probability can be configured via the `ConfigureQubitLoss` operation. When compiled to QIR, this uses the
203+
/// `__quantum__rt__read_loss` intrinsic, which may not be supported on all hardware targets and could result in compilation errors when submitting to those targets.
204+
///
205+
/// # See also
206+
/// - [Std.Measurement.MResetZChecked](xref:Qdk.Std.Measurement.MResetZChecked)
207+
operation IsLossResult(res : Result) : Bool {
208+
__quantum__rt__read_loss(res)
209+
}
210+
211+
export MeasureAllZ, MeasureEachZ, MResetEachZ, MResetX, MResetY, MResetZ, MeasureInteger, MResetZChecked, IsLossResult;

library/std/src/Std/OpenQASM/Intrinsic.qs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export rxx, ryy, rzz;
3838
// that Qiskit wont emit correctly.
3939
export dcx, ecr, r, rzx, cs, csdg, sxdg, csx, rccx, c3sqrtx, c3x, rc3x, xx_minus_yy, xx_plus_yy, ccz;
4040

41+
export mresetz_checked;
42+
4143
export __quantum__qis__barrier__body;
4244

4345
import Std.OpenQASM.Angle.Angle;
@@ -633,6 +635,18 @@ operation ccz(ctrl1 : Qubit, ctrl2 : Qubit, target : Qubit) : Unit is Adj + Ctl
633635
h(target);
634636
}
635637

638+
/// A resetting measurement operation that checks for qubit loss.
639+
/// Returns 0 if the qubit measurement was `Zero`, 1 if it was `One`,
640+
/// and 2 if the measurement indicated qubit loss.
641+
operation mresetz_checked(q : Qubit) : Int {
642+
let (r, b) = Std.Measurement.MResetZChecked(q);
643+
if b {
644+
2
645+
} else {
646+
Std.OpenQASM.Convert.ResultAsInt(r)
647+
}
648+
}
649+
636650
/// The ``BARRIER`` function is used to implement the `barrier` statement in QASM.
637651
/// The `@SimulatableIntrinsic` attribute is used to mark the operation for QIR
638652
/// generation.

samples/notebooks/noise.ipynb

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"cell_type": "markdown",
55
"metadata": {},
66
"source": [
7-
"# Simulating Pauli noise\n",
8-
"This notebook shows how to run simulations with Pauli noise, such as bit-flip or depolarizing noise.\n",
7+
"# Simulating Pauli noise and Qubit Loss\n",
8+
"This notebook shows how to run simulations with Pauli noise, such as bit-flip or depolarizing noise, as well as qubit loss.\n",
99
"\n",
1010
"First, make sure prerequisites are available. Packages `qsharp` and `qsharp_widgets` must be already installed."
1111
]
1212
},
1313
{
1414
"cell_type": "code",
15-
"execution_count": 5,
15+
"execution_count": null,
1616
"metadata": {},
1717
"outputs": [],
1818
"source": [
@@ -31,7 +31,7 @@
3131
},
3232
{
3333
"cell_type": "code",
34-
"execution_count": 6,
34+
"execution_count": null,
3535
"metadata": {
3636
"vscode": {
3737
"languageId": "qsharp"
@@ -101,7 +101,7 @@
101101
},
102102
{
103103
"cell_type": "code",
104-
"execution_count": 9,
104+
"execution_count": null,
105105
"metadata": {
106106
"vscode": {
107107
"languageId": "qsharp"
@@ -182,6 +182,69 @@
182182
"result = qsharp.run(\"Cat5()\", 1000, noise=(0.0, 0.1, 0.0))\n",
183183
"display(qsharp_widgets.Histogram(result))"
184184
]
185+
},
186+
{
187+
"cell_type": "markdown",
188+
"metadata": {},
189+
"source": [
190+
"## Simulating with Qubit Loss\n",
191+
"\n",
192+
"For some qubit modalities, a qubit may undergo \"loss\" during execution. This means the physical system backing the qubit is unable to be measured or operated on. To test behaviors of an algorithm in the presence of qubit loss, you can use the `qubit_loss` parameter to `qsharp.run` and set the probability of a qubit being lost on each operation. Qubit loss is reported only at measurement time, where a special `Loss` value is returned that is neither `One` nor `Zero`. During simulation, a lost qubit is no longer acted on by gates and remains in the $\\ket{0}$ state until it is measured or reset, at which point the simulation reloads a fresh qubit to support future operations.\n",
193+
"\n",
194+
"In this example, we'll set the qubit loss probability to high 50% to ensure some qubits are lost during simulation:"
195+
]
196+
},
197+
{
198+
"cell_type": "code",
199+
"execution_count": null,
200+
"metadata": {},
201+
"outputs": [],
202+
"source": [
203+
"result = qsharp.run(\"BellPair()\", 100, qubit_loss=0.5)\n",
204+
"display(qsharp_widgets.Histogram(result))"
205+
]
206+
},
207+
{
208+
"cell_type": "markdown",
209+
"metadata": {},
210+
"source": [
211+
"Note that the `Loss` value is not usable inside of the simulation, and any comparisons on that value will trigger a runtime failure. To avoid this failure, use `IsLossResult` to check whether the result value corresponds to qubit loss before using it in any branching logic:\n"
212+
]
213+
},
214+
{
215+
"cell_type": "code",
216+
"execution_count": null,
217+
"metadata": {
218+
"vscode": {
219+
"languageId": "qsharp"
220+
}
221+
},
222+
"outputs": [],
223+
"source": [
224+
"%%qsharp\n",
225+
"\n",
226+
"operation CheckForLoss() : (Bool, Bool) {\n",
227+
" use q = Qubit();\n",
228+
" H(q);\n",
229+
" let res = MResetZ(q);\n",
230+
" if IsLossResult(res) {\n",
231+
" return (true, false);\n",
232+
" } elif res == One {\n",
233+
" return (false, true);\n",
234+
" } else {\n",
235+
" return (false, false);\n",
236+
" }\n",
237+
"}"
238+
]
239+
},
240+
{
241+
"cell_type": "code",
242+
"execution_count": null,
243+
"metadata": {},
244+
"outputs": [],
245+
"source": [
246+
"qsharp_widgets.Histogram(qsharp.run(\"CheckForLoss()\", 100, qubit_loss=0.5))"
247+
]
185248
}
186249
],
187250
"metadata": {
@@ -200,7 +263,7 @@
200263
"name": "python",
201264
"nbconvert_exporter": "python",
202265
"pygments_lexer": "ipython3",
203-
"version": "3.11.9"
266+
"version": "3.11.13"
204267
}
205268
},
206269
"nbformat": 4,

samples/notebooks/openqasm.ipynb

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,73 @@
333333
"\n",
334334
"print(compile(parameterized_program, 1.57))"
335335
]
336+
},
337+
{
338+
"cell_type": "markdown",
339+
"id": "31d20b6c",
340+
"metadata": {},
341+
"source": [
342+
"When running an OpenQASM program in simulation with qubit loss, additional `Result.Loss` values may be returned that indicate the measured qubit was lost during execution:"
343+
]
344+
},
345+
{
346+
"cell_type": "code",
347+
"execution_count": null,
348+
"id": "168c3226",
349+
"metadata": {},
350+
"outputs": [],
351+
"source": [
352+
"init()\n",
353+
"\n",
354+
"source = \"\"\"\n",
355+
"include \"stdgates.inc\";\n",
356+
"bit[2] c;\n",
357+
"qubit[2] q;\n",
358+
"c = measure q;\n",
359+
"\"\"\"\n",
360+
"\n",
361+
"import_openqasm(source, name=\"measure2\")\n",
362+
"\n",
363+
"from qsharp.code import measure2\n",
364+
"\n",
365+
"Histogram(run(measure2, shots=1000, qubit_loss=0.1))"
366+
]
367+
},
368+
{
369+
"cell_type": "markdown",
370+
"id": "4773ac0e",
371+
"metadata": {},
372+
"source": [
373+
"By using the special include `\"qdk.inc\"` you can check for loss at runtime using the `mresetz_checked` function. It returns an integer with two bits to indicate whether or not loss has occurred, such that `0` or `1` correspond to the qubit measurement and `2` corresponds to loss:"
374+
]
375+
},
376+
{
377+
"cell_type": "code",
378+
"execution_count": null,
379+
"id": "02a97625",
380+
"metadata": {},
381+
"outputs": [],
382+
"source": [
383+
"source = \"\"\"\n",
384+
"include \"stdgates.inc\";\n",
385+
"include \"qdk.inc\";\n",
386+
"qubit q;\n",
387+
"output int res;\n",
388+
"h q;\n",
389+
"res = mresetz_checked(q);\n",
390+
"\"\"\"\n",
391+
"\n",
392+
"import_openqasm(source, name=\"mresetz_checked_example\")\n",
393+
"\n",
394+
"from qsharp.code import mresetz_checked_example\n",
395+
"\n",
396+
"Histogram(run(mresetz_checked_example, shots=1000, qubit_loss=0.1))\n"
397+
]
336398
}
337399
],
338400
"metadata": {
339401
"kernelspec": {
340-
"display_name": ".venv",
402+
"display_name": "Python 3",
341403
"language": "python",
342404
"name": "python3"
343405
},
@@ -351,7 +413,7 @@
351413
"name": "python",
352414
"nbconvert_exporter": "python",
353415
"pygments_lexer": "ipython3",
354-
"version": "3.13.3"
416+
"version": "3.11.13"
355417
}
356418
},
357419
"nbformat": 4,

source/compiler/qsc/src/interpret.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,11 +578,15 @@ impl Interpreter {
578578
callable: Value,
579579
args: Value,
580580
noise: Option<PauliNoise>,
581+
qubit_loss: Option<f64>,
581582
) -> InterpretResult {
582583
let mut sim = match noise {
583584
Some(noise) => SparseSim::new_with_noise(&noise),
584585
None => SparseSim::new(),
585586
};
587+
if let Some(loss) = qubit_loss {
588+
sim.set_loss(loss);
589+
}
586590
self.invoke_with_sim(&mut sim, receiver, callable, args)
587591
}
588592

@@ -593,11 +597,15 @@ impl Interpreter {
593597
receiver: &mut impl Receiver,
594598
expr: Option<&str>,
595599
noise: Option<PauliNoise>,
600+
qubit_loss: Option<f64>,
596601
) -> InterpretResult {
597602
let mut sim = match noise {
598603
Some(noise) => SparseSim::new_with_noise(&noise),
599604
None => SparseSim::new(),
600605
};
606+
if let Some(loss) = qubit_loss {
607+
sim.set_loss(loss);
608+
}
601609
self.run_with_sim(&mut sim, receiver, expr)
602610
}
603611

source/compiler/qsc/src/interpret/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ mod given_interpreter {
2424
let mut cursor = Cursor::new(Vec::<u8>::new());
2525
let mut receiver = CursorReceiver::new(&mut cursor);
2626
(
27-
interpreter.run(&mut receiver, Some(expr), None),
27+
interpreter.run(&mut receiver, Some(expr), None, None),
2828
receiver.dump(),
2929
)
3030
}

0 commit comments

Comments
 (0)