microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.25.1

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

library/std/src/Std/Diagnostics.qs

478lines · modecode

1open QIR.Intrinsic;
2
3/// # Summary
4/// Dumps the current target machine's status.
5///
6/// # Description
7/// This method allows you to dump information about the current quantum state.
8/// The actual information generated and the semantics are specific to each target machine.
9///
10/// For the local sparse-state simulator distributed as part of the
11/// Quantum Development Kit, this method will write the wave function as a
12/// one-dimensional array of pairs of state indices and complex numbers, in which each element represents
13/// the amplitudes of the probability of measuring the corresponding state.
14///
15/// # Example
16/// When run on the sparse-state simulator, the following snippet dumps
17/// the Bell state (|00⟩ + |11⟩ ) / √2 to the console:
18/// ```qsharp
19/// use left = Qubit();
20/// use right = Qubit();
21/// within {
22/// H(left);
23/// CNOT(left, right);
24/// } apply {
25/// DumpMachine();
26/// }
27/// ```
28function DumpMachine() : Unit {
29 body intrinsic;
30}
31
32/// # Summary
33/// Dumps the current target machine's status associated with the given qubits.
34///
35/// # Input
36/// ## qubits
37/// The list of qubits to report.
38///
39/// # Remarks
40/// This method allows you to dump the information associated with the state of the
41/// given qubits.
42///
43/// For the local sparse-state simulator distributed as part of the
44/// Quantum Development Kit, this method will write the
45/// state of the given qubits (i.e. the wave function of the corresponding subsystem) as a
46/// one-dimensional array of pairs of state indices and complex numbers, in which each element represents
47/// the amplitudes of the probability of measuring the corresponding state.
48/// If the given qubits are entangled with some other qubit and their
49/// state can't be separated, it fails with a runtime error indicating that the qubits are entangled.
50///
51/// # Example
52/// When run on the sparse-state simulator, the following snippet dumps
53/// the Bell state (|00⟩ + |11⟩ ) / √2 to the console:
54/// ```qsharp
55/// use left = Qubit();
56/// use right = Qubit();
57/// within {
58/// H(left);
59/// CNOT(left, right);
60/// } apply {
61/// DumpRegister([left, right]);
62/// }
63/// ```
64function DumpRegister(register : Qubit[]) : Unit {
65 body intrinsic;
66}
67
68/// # Summary
69/// Given an operation, dumps the matrix representation of the operation action on the given
70/// number of qubits.
71///
72/// # Input
73/// ## nQubits
74/// The number of qubits on which the given operation acts.
75/// ## op
76/// The operation that is to be diagnosed.
77///
78/// # Remarks
79/// When run on the sparse-state simulator, the following snippet
80/// will output the matrix
81/// $\left(\begin{matrix} 0.707 & 0.707 \\\\ 0.707 & -0.707\end{matrix}\right)$:
82///
83/// ```qsharp
84/// operation DumpH() : Unit {
85/// DumpOperation(1, qs => H(qs[0]));
86/// }
87/// ```
88/// Calling this operation has no observable effect from within Q#.
89/// Note that if `DumpOperation` is called when there are other qubits allocated,
90/// the matrix displayed may reflect any global phase that has accumulated from operations
91/// on those other qubits.
92@SimulatableIntrinsic()
93operation DumpOperation(nQubits : Int, op : Qubit[] => Unit) : Unit {
94 use (targets, extra) = (Qubit[nQubits], Qubit[nQubits]);
95 for i in 0..nQubits - 1 {
96 H(targets[i]);
97 CNOT(targets[i], extra[i]);
98 }
99 op(targets);
100 DumpMatrix(targets + extra);
101 ResetAll(targets + extra);
102}
103
104function DumpMatrix(qs : Qubit[]) : Unit {
105 body intrinsic;
106}
107
108/// # Summary
109/// Checks whether a qubit is in the |0⟩ state, returning true if it is.
110///
111/// # Description
112/// This operation checks whether a qubit is in the |0⟩ state. It will return true only
113/// if the qubit is deterministically in the |0⟩ state, and will return false otherwise. This operation
114/// does not change the state of the qubit.
115///
116/// # Input
117/// ## qubit
118/// The qubit to check.
119/// # Output
120/// True if the qubit is in the |0⟩ state, false otherwise.
121///
122/// # Remarks
123/// This operation is useful for checking whether a qubit is in the |0⟩ state during simulation. It is not possible to check
124/// this on hardware without measuring the qubit, which could change the state.
125operation CheckZero(qubit : Qubit) : Bool {
126 body intrinsic;
127}
128
129/// # Summary
130/// Checks whether all qubits in the provided array are in the |0⟩ state. Returns true if they are.
131///
132/// # Description
133/// This operation checks whether all qubits in the provided array are in the |0⟩ state. It will return true only
134/// if all qubits are deterministically in the |0⟩ state, and will return false otherwise. This operation
135/// does not change the state of the qubits.
136///
137/// # Input
138/// ## qubits
139/// The qubits to check.
140/// # Output
141/// True if all qubits are in the |0⟩ state, false otherwise.
142///
143/// # Remarks
144/// This operation is useful for checking whether a qubit is in the |0⟩ state during simulation. It is not possible to check
145/// this on hardware without measuring the qubit, which could change the state.
146operation CheckAllZero(qubits : Qubit[]) : Bool {
147 for q in qubits {
148 if not CheckZero(q) {
149 return false;
150 }
151 }
152
153 return true;
154}
155
156/// # Summary
157/// Checks whether a given condition is true, failing with a message if it is not.
158///
159/// # Description
160/// This function checks whether a given condition is true. If the condition is false, the operation fails with the given message,
161/// terminating the program.
162///
163/// # Input
164/// ## actual
165/// The condition to check.
166/// ## message
167/// The message to use in the failure if the condition is false.
168function Fact(actual : Bool, message : String) : Unit {
169 if (not actual) {
170 fail message;
171 }
172}
173
174/// # Summary
175/// Given two operations, checks that they act identically for all input states.
176///
177/// # Description
178/// This check is implemented by using the Choi–Jamiołkowski isomorphism to reduce
179/// this check to a check on two entangled registers.
180/// Thus, this operation needs only a single call to each operation being tested,
181/// but requires twice as many qubits to be allocated.
182/// This check can be used to ensure, for instance, that an optimized version of an
183/// operation acts identically to its naïve implementation, or that an operation
184/// which acts on a range of non-quantum inputs agrees with known cases.
185///
186/// # Remarks
187/// This operation requires that the operation modeling the expected behavior is
188/// adjointable, so that the inverse can be performed on the target register alone.
189/// Formally, one can specify a transpose operation, which relaxes this requirement,
190/// but the transpose operation is not in general physically realizable for arbitrary
191/// quantum operations and thus is not included here as an option.
192///
193/// # Input
194/// ## nQubits
195/// Number of qubits to pass to each operation.
196/// ## actual
197/// Operation to be tested.
198/// ## expected
199/// Operation defining the expected behavior for the operation under test.
200/// # Output
201/// True if operations are equal, false otherwise.
202operation CheckOperationsAreEqual(
203 nQubits : Int,
204 actual : (Qubit[] => Unit),
205 expected : (Qubit[] => Unit is Adj)
206) : Bool {
207
208 // Prepare a reference register entangled with the target register.
209 use reference = Qubit[nQubits];
210 use target = Qubit[nQubits];
211
212 // Apply operations.
213 within {
214 for i in 0..nQubits - 1 {
215 H(reference[i]);
216 CNOT(reference[i], target[i]);
217 }
218 } apply {
219 actual(target);
220 Adjoint expected(target);
221 }
222
223 // Check and return result.
224 let areEqual = CheckAllZero(reference) and CheckAllZero(target);
225 ResetAll(target);
226 ResetAll(reference);
227 areEqual
228}
229
230/// # Summary
231/// Starts counting the number of times the given operation is called. Fails if the operation is already being counted.
232///
233/// # Description
234/// This operation allows you to count the number of times a given operation is called. If the given operation is already
235/// being counted, calling `StartCountingOperation` again will trigger a runtime failure. Counting is based on the specific
236/// specialization of the operation invoked, so `X` and `Adjoint X` are counted separately.
237/// Likewise `Controlled X`, `CNOT`, and `CX` are independent operations that are counted separately, as are `Controlled X`
238/// and `Controlled Adjoint X`.
239///
240/// # Input
241/// ## callable
242/// The operation to be counted.
243///
244/// # Remarks
245/// Counting operation calls requires specific care in what operation is passed as input. For example, `StartCountingOperation(H)` will
246/// count only the number of times `H` is called, while `StartCountingOperation(Adjoint H)` will count only the number of times `Adjoint H` is called, even
247/// though `H` is self-adjoint. This is due to how the execution treats the invocation of these operations as distinct by their specialization.
248/// In the same way, `StartCountingOperation(Controlled X)` will count only the number of times `Controlled X` is called, while
249/// `StartCountingOperation(CNOT)` will count only the number of times `CNOT` is called.
250///
251/// When counting lambdas, the symbol the lambda is bound to is used to identify the operation and it is counted as a separate operation. For example,
252/// ```qsharp
253/// let myOp = q => H(q);
254/// StartCountingOperation(myOp);
255/// ```
256/// Will count specifically calls to `myOp` and not `H`. By contrast, the following code will count calls to `H` itself:
257/// ```qsharp
258/// let myOp = H;
259/// StartCountingOperation(myOp);
260/// ```
261/// This is because this code does not define a lambda and instead just creates a binding to `H` directly.
262@Config(Unrestricted)
263operation StartCountingOperation<'In, 'Out>(callable : 'In => 'Out) : Unit {
264 body intrinsic;
265}
266
267/// # Summary
268/// Stops counting the number of times the given operation is called and returns the count. Fails
269/// if the operation was not being counted.
270///
271/// # Description
272/// This operation allows you to stop counting the number of times a given operation is called and returns the count.
273/// If the operation was not being counted, it triggers a runtime failure.
274///
275/// # Input
276/// ## callable
277/// The operation whose count will be returned.
278/// # Output
279/// The number of times the operation was called since the last call to `StartCountingOperation`.
280@Config(Unrestricted)
281operation StopCountingOperation<'In, 'Out>(callable : 'In => 'Out) : Int {
282 body intrinsic;
283}
284
285/// # Summary
286/// Starts counting the number of times the given function is called. Fails if the function is already being counted.
287///
288/// # Description
289/// This operation allows you to count the number of times a given function is called. If the given function is already
290/// being counted, calling `StartCountingFunction` again will trigger a runtime failure.
291///
292/// # Input
293/// ## callable
294/// The function to be counted.
295///
296/// # Remarks
297/// When counting lambdas, the symbol the lambda is bound to is used to identify the function and it is counted as a separate function. For example,
298/// ```qsharp
299/// let myFunc = i -> AbsI(i);
300/// StartCountingFunction(myFunc);
301/// ```
302/// Will count specifically calls to `myFunc` and not `AbsI`. By contrast, the following code will count calls to `AbsI` itself:
303/// ```qsharp
304/// let myFunc = AbsI;
305/// StartCountingFunction(myFunc);
306/// ```
307/// This is because this code does not define a lambda and instead just creates a binding to `AbsI` directly.
308@Config(Unrestricted)
309operation StartCountingFunction<'In, 'Out>(callable : 'In -> 'Out) : Unit {
310 body intrinsic;
311}
312
313/// # Summary
314/// Stops counting the number of times the given function is called and returns the count. Fails
315/// if the function was not being counted.
316///
317/// # Description
318/// This operation allows you to stop counting the number of times a given function is called and returns the count.
319/// If the function was not being counted, it triggers a runtime failure.
320///
321/// # Input
322/// ## callable
323/// The function whose count will be returned.
324/// # Output
325/// The number of times the function was called since the last call to `StartCountingFunction`.
326@Config(Unrestricted)
327operation StopCountingFunction<'In, 'Out>(callable : 'In -> 'Out) : Int {
328 body intrinsic;
329}
330
331/// # Summary
332/// Starts counting the number of qubits allocated. Fails if qubits are already being counted.
333///
334/// # Description
335/// This operation allows you to count the number of qubits allocated until `StopCountingQubits` is called.
336/// The counter is incremented only when a new unique qubit is allocated, so reusing the same qubit multiple times
337/// across separate allocations does not increment the counter.
338///
339/// # Remarks
340/// This operation is useful for tracking the number of unique qubits allocated in a given scope. Along with
341/// `StopCountingQubits`, it can be used to verify that a given operation does not allocate more qubits than
342/// expected. For example,
343/// ```qsharp
344/// StartCountingQubits();
345/// testOperation();
346/// let qubitsAllocated = StopCountingQubits();
347/// Fact(qubitsAllocated <= 4, "Operation should not allocate more than 4 qubits.");
348/// ```
349@Config(Unrestricted)
350operation StartCountingQubits() : Unit {
351 body intrinsic;
352}
353
354/// # Summary
355/// Stops counting the number of qubits allocated and returns the count. Fails if the qubits were not being counted.
356///
357/// # Description
358/// This operation allows you to stop counting the number of qubits allocated and returns the count since the
359/// last call to `StartCountingQubits`. If the qubits were not being counted, it triggers a runtime failure.
360///
361/// # Output
362/// The number of unique qubits allocated since the last call to `StartCountingQubits`.
363@Config(Unrestricted)
364operation StopCountingQubits() : Int {
365 body intrinsic;
366}
367
368/// # Summary
369/// Configures Pauli noise for simulation.
370///
371/// # Description
372/// This function configures Pauli noise for simulation. Parameters represent
373/// probabilities of applying X, Y, and Z gates and must add up to at most 1.0.
374/// Noise is applied after each gate and before each measurement in the simulator
375/// backend. Decompositions may affect the number of times noise is applied.
376/// Use 0.0 for all parameters to simulate without noise.
377///
378/// # Input
379/// ## px
380/// Probability of applying X gate.
381/// ## py
382/// Probability of applying Y gate.
383/// ## pz
384/// Probability of applying Z gate.
385function ConfigurePauliNoise(px : Double, py : Double, pz : Double) : Unit {
386 body intrinsic;
387}
388
389/// # Summary
390/// Configures qubit loss during simulation.
391///
392/// # Description
393/// This function configures qubit loss for simulation. The parameter `p` represents
394/// the probability of a qubit loss during simulation. If `p` is greater than 0.0, the simulator will mark qubits
395/// as lost with the given probability during each operation that acts on them.
396/// Qubits that are lost are reset to the |0⟩ state but are not released. Loss is reported when the qubit is measured,
397/// and then the qubit is considered "reloaded" and can be used again.
398///
399/// # Input
400/// ## p
401/// The probability of a qubit being lost during simulation. Must be between 0.0 and 1.0.
402///
403/// # Remarks
404/// This operation is useful for simulating qubit loss for those modalities where qubit loss is a factor.
405/// Note that the value returned from a measurement of a lost qubit is neither `Zero` nor `One`, but rather a special
406/// value indicating that the qubit was lost. This value cannot be used in comparisons and will cause a runtime
407/// failure if compared to another value.
408/// To perform a measurement that includes a check for qubit loss, use the `MResetZChecked` operation.
409function ConfigureQubitLoss(p : Double) : Unit {
410 body intrinsic;
411}
412
413/// # Summary
414/// Applies configured noise or loss to a qubit.
415///
416/// # Description
417/// This operation applies configured noise and/or loss to a qubit during simulation. For example,
418/// if configured noise is a bit-flip noise with 5% probability, the X gate will be applied
419/// with 5% probability. If no noise is configured, no noise is applied.
420/// This is useful to simulate noise during idle periods. It could also be used to
421/// apply noise immediately after qubit allocation.
422///
423/// # Input
424/// ## qubit
425/// The qubit to which noise is applied.
426///
427/// # See Also
428/// - [Std.Diagnostics.ConfigurePauliNoise](xref:Qdk.Std.Diagnostics.ConfigurePauliNoise)
429/// - [Std.Diagnostics.ConfigureQubitLoss](xref:Qdk.Std.Diagnostics.ConfigureQubitLoss)
430operation ApplyIdleNoise(qubit : Qubit) : Unit {
431 body intrinsic;
432}
433
434/// # Summary
435/// The bit flip noise with probability `p`.
436function BitFlipNoise(p : Double) : (Double, Double, Double) {
437 (p, 0.0, 0.0)
438}
439
440/// # Summary
441/// The phase flip noise with probability `p`.
442function PhaseFlipNoise(p : Double) : (Double, Double, Double) {
443 (0.0, 0.0, p)
444}
445
446/// # Summary
447/// The depolarizing noise with probability `p`.
448function DepolarizingNoise(p : Double) : (Double, Double, Double) {
449 (p / 3.0, p / 3.0, p / 3.0)
450}
451
452/// # Summary
453/// No noise for noiseless operation.
454function NoNoise() : (Double, Double, Double) {
455 (0.0, 0.0, 0.0)
456}
457
458export
459 DumpMachine,
460 DumpRegister,
461 DumpOperation,
462 CheckZero,
463 CheckAllZero,
464 Fact,
465 CheckOperationsAreEqual,
466 StartCountingOperation,
467 StopCountingOperation,
468 StartCountingFunction,
469 StopCountingFunction,
470 StartCountingQubits,
471 StopCountingQubits,
472 ConfigurePauliNoise,
473 ConfigureQubitLoss,
474 ApplyIdleNoise,
475 BitFlipNoise,
476 PhaseFlipNoise,
477 DepolarizingNoise,
478 NoNoise;
479