Skip to content

Commit 669e265

Browse files
authored
Merge f681ace into 12e6f5f
2 parents 12e6f5f + f681ace commit 669e265

File tree

37 files changed

+1583
-147
lines changed

37 files changed

+1583
-147
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: 8 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;
@@ -47,6 +49,7 @@ import Std.OpenQASM.Angle.AddAngles;
4749
import Std.OpenQASM.Angle.SubtractAngles;
4850
import Std.OpenQASM.Angle.DivideAngleByInt;
4951
import Std.OpenQASM.Angle.NegAngle;
52+
import Std.Convert.BoolAsResult;
5053

5154
function ZERO_ANGLE() : Angle {
5255
return DoubleAsAngle(0., 1);
@@ -633,6 +636,11 @@ operation ccz(ctrl1 : Qubit, ctrl2 : Qubit, target : Qubit) : Unit is Adj + Ctl
633636
h(target);
634637
}
635638

639+
operation mresetz_checked(q : Qubit) : Result[] {
640+
let (r, b) = MResetZChecked(q);
641+
[r, BoolAsResult(b)]
642+
}
643+
636644
/// The ``BARRIER`` function is used to implement the `barrier` statement in QASM.
637645
/// The `@SimulatableIntrinsic` attribute is used to mark the operation for QIR
638646
/// 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,

source/compiler/qsc/src/interpret.rs

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

@@ -592,11 +596,15 @@ impl Interpreter {
592596
receiver: &mut impl Receiver,
593597
expr: Option<&str>,
594598
noise: Option<PauliNoise>,
599+
qubit_loss: Option<f64>,
595600
) -> InterpretResult {
596601
let mut sim = match noise {
597602
Some(noise) => SparseSim::new_with_noise(&noise),
598603
None => SparseSim::new(),
599604
};
605+
if let Some(loss) = qubit_loss {
606+
sim.set_loss(loss);
607+
}
600608
self.run_with_sim(&mut sim, receiver, expr)
601609
}
602610

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
}

source/compiler/qsc_circuit/src/builder.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,17 @@ impl Backend for Builder {
5555
self.push_gate(gate("H", [q]));
5656
}
5757

58-
fn m(&mut self, q: usize) -> Self::ResultType {
58+
fn m(&mut self, q: usize) -> Option<Self::ResultType> {
5959
let mapped_q = self.map(q);
6060
// In the Circuit schema, result id is per-qubit
6161
let res_id = self.num_measurements_for_qubit(mapped_q);
6262
let id = self.remapper.m(q);
6363

6464
self.push_gate(measurement_gate(mapped_q.0, res_id));
65-
id
65+
Some(id)
6666
}
6767

68-
fn mresetz(&mut self, q: usize) -> Self::ResultType {
68+
fn mresetz(&mut self, q: usize) -> Option<Self::ResultType> {
6969
let mapped_q = self.map(q);
7070
// In the Circuit schema, result id is per-qubit
7171
let res_id = self.num_measurements_for_qubit(mapped_q);
@@ -78,7 +78,7 @@ impl Backend for Builder {
7878
// a measurement and a reset gate.
7979
self.push_gate(measurement_gate(mapped_q.0, res_id));
8080
self.push_gate(ket_gate("0", [mapped_q]));
81-
id
81+
Some(id)
8282
}
8383

8484
fn reset(&mut self, q: usize) {

0 commit comments

Comments
 (0)