forked from microsoft/QuantumKatas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Tests.qs
346 lines (263 loc) · 11.5 KB
/
Tests.qs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
//////////////////////////////////////////////////////////////////////
// This file contains parts of the testing harness.
// You should not modify anything in this file.
// The tasks themselves can be found in Tasks.qs file.
//////////////////////////////////////////////////////////////////////
namespace Quantum.Kata.QEC_BitFlipCode {
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Bitwise;
open Microsoft.Quantum.Random;
open Quantum.Kata.Utils;
//////////////////////////////////////////////////////////////////////////
// Task 01
//////////////////////////////////////////////////////////////////////////
function ToString_Bitmask (bits : Int) : String {
let b1 = bits / 4;
let b2 = (bits / 2) % 2;
let b3 = bits % 2;
return $"{b1}{b2}{b3}";
}
operation StatePrep_Bitmask (qs : Qubit[], bits : Int) : Unit is Adj {
if bits / 4 == 1 {
X(qs[0]);
}
if (bits / 2) % 2 == 1 {
X(qs[1]);
}
if bits % 2 == 1 {
X(qs[2]);
}
}
function FindFirstDiff_Reference (bits1 : Int[], bits2 : Int[]) : Int {
mutable firstDiff = -1;
for i in 0 .. Length(bits1) - 1 {
if bits1[i] != bits2[i] and firstDiff == -1 {
set firstDiff = i;
}
}
return firstDiff;
}
function IntToBoolArray (n : Int) : Int[] {
return [(n / 4) % 2, (n / 2) % 2, n % 2];
}
operation StatePrep_TwoBitmasks (qs : Qubit[], bits1 : Int[], bits2 : Int[]) : Unit is Adj {
let firstDiff = FindFirstDiff_Reference(bits1, bits2);
H(qs[firstDiff]);
for i in 0 .. Length(qs) - 1 {
if bits1[i] == bits2[i] {
if bits1[i] == 1 {
X(qs[i]);
}
} else {
if i > firstDiff {
CNOT(qs[firstDiff], qs[i]);
if bits1[i] != bits1[firstDiff] {
X(qs[i]);
}
}
}
}
}
operation TestParityOnState (statePrep : (Qubit[] => Unit is Adj), parity : Int, stateStr : String) : Unit {
use register = Qubit[3];
// prepare basis state to test on
statePrep(register);
ResetOracleCallsCount();
let res = MeasureParity(register);
// check that the returned parity is correct
Fact((res == Zero) == (parity == 0), $"Failed on {stateStr}.");
// check that the state has not been modified
Adjoint statePrep(register);
AssertAllZero(register);
let nm = GetOracleCallsCount(Measure);
Fact(nm <= 1, $"You are allowed to do at most one measurement, and you did {nm}");
}
@Test("Microsoft.Quantum.Katas.CounterSimulator")
operation T01_MeasureParity () : Unit {
// test on all basis states
for bits in 0 .. 7 {
let bitsStr = ToString_Bitmask(bits);
TestParityOnState(StatePrep_Bitmask(_, bits), Parity(bits), $"basis state |{bitsStr}⟩");
}
// test on all superpositions of two basis states of the same parity
for b1 in 0 .. 7 {
let bits1 = IntToBoolArray(b1);
let bitsStr1 = ToString_Bitmask(b1);
for b2 in b1 + 1 .. 7 {
if Parity(b1) == Parity(b2) {
let bits2 = IntToBoolArray(b2);
let bitsStr2 = ToString_Bitmask(b2);
let p = Parity(b1);
Message($"Testing on |{bitsStr1}⟩ + |{bitsStr2}⟩ with parity {p}");
TestParityOnState(StatePrep_TwoBitmasks(_, bits1, bits2), Parity(b1), $"state |{bitsStr1}⟩ + |{bitsStr2}⟩");
}
}
}
}
//////////////////////////////////////////////////////////////////////////
// Task 02
//////////////////////////////////////////////////////////////////////////
operation AssertEqualOnZeroState (
statePrep : (Qubit[] => Unit is Adj),
testImpl : (Qubit[] => Unit),
refImpl : (Qubit[] => Unit is Adj)) : Unit {
use qs = Qubit[3];
// prepare state
statePrep(qs);
// apply operation that needs to be tested
testImpl(qs);
// apply adjoint reference operation and adjoint state prep
Adjoint refImpl(qs);
Adjoint statePrep(qs);
// assert that all qubits end up in |0⟩ state
AssertAllZero(qs);
}
operation StatePrep_Rotate (qs : Qubit[], alpha : Double) : Unit is Adj {
Ry(2.0 * alpha, qs[0]);
}
@Test("QuantumSimulator")
operation T02_Encode () : Unit {
for i in 0 .. 36 {
let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0;
AssertEqualOnZeroState(StatePrep_Rotate(_, alpha), Encode, Encode_Reference);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 03
//////////////////////////////////////////////////////////////////////////
operation StatePrep_WithError (qs : Qubit[], alpha : Double, hasError : Bool) : Unit is Adj {
StatePrep_Rotate(qs, alpha);
Encode_Reference(qs);
if hasError {
X(qs[0]);
}
}
@Test("QuantumSimulator")
operation T03_DetectErrorOnLeftQubit () : Unit {
use register = Qubit[3];
for i in 0 .. 36 {
let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0;
StatePrep_WithError(register, alpha, false);
EqualityFactR(DetectErrorOnLeftQubit(register), Zero, "Failed on a state without X error.");
Adjoint StatePrep_WithError(register, alpha, false);
AssertAllZero(register);
StatePrep_WithError(register, alpha, true);
EqualityFactR(DetectErrorOnLeftQubit(register), One, "Failed on a state with X error.");
Adjoint StatePrep_WithError(register, alpha, true);
AssertAllZero(register);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 04
//////////////////////////////////////////////////////////////////////////
operation BindErrorCorrectionRoundImpl (
encoder : (Qubit[] => Unit is Adj),
error : Pauli[],
logicalOp : (Qubit[] => Unit),
correction : (Qubit[] => Unit),
dataRegister : Qubit[]) : Unit {
use auxiliary = Qubit[2];
let register = dataRegister + auxiliary;
// encode the logical qubit (dataRegister) into physical representation (register)
encoder(register);
// apply error (or no error)
ApplyPauli(error, register);
// perform logical operation on (possibly erroneous) state
logicalOp(register);
// apply correction to get the state back to correct one
correction(register);
// apply decoding to get back to 1-qubit state
Adjoint encoder(register);
AssertAllZero(auxiliary);
}
function BindErrorCorrectionRound (
encoder : (Qubit[] => Unit is Adj),
error : Pauli[],
logicalOp : (Qubit[] => Unit),
correction : (Qubit[] => Unit)) : (Qubit[] => Unit) {
return BindErrorCorrectionRoundImpl(encoder, error, logicalOp, correction, _);
}
// list of errors which can be corrected by the code (the first element corresponds to no error)
function PauliErrors () : Pauli[][] {
return [[PauliI, PauliI, PauliI],
[PauliX, PauliI, PauliI],
[PauliI, PauliX, PauliI],
[PauliI, PauliI, PauliX]];
}
@Test("QuantumSimulator")
operation T04_CorrectErrorOnLeftQubit () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, NoOp, CorrectErrorOnLeftQubit);
let errors = PauliErrors();
for idxError in 0 .. 1 {
AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), NoOp);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 05
//////////////////////////////////////////////////////////////////////////
@Test("QuantumSimulator")
operation T05_DetectErrorOnAnyQubit () : Unit {
let errors = PauliErrors();
use register = Qubit[3];
for idxError in 0 .. Length(errors) - 1 {
let θ = DrawRandomDouble(0.0, 1.0);
let statePrep = BoundCA([H, Rz(θ, _)]);
mutable errorStr = "no error";
if idxError > 0 {
set errorStr = $"error on qubit {idxError}";
}
Message($"Testing with {errorStr}.");
statePrep(Head(register));
Encode_Reference(register);
ApplyPauli(errors[idxError], register);
EqualityFactI(DetectErrorOnAnyQubit(register), idxError, $"Failed on state with {errorStr}.");
ApplyPauli(errors[idxError], register);
Adjoint Encode_Reference(register);
Adjoint statePrep(Head(register));
AssertAllZero(register);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 06
//////////////////////////////////////////////////////////////////////////
@Test("QuantumSimulator")
operation T06_CorrectErrorOnAnyQubit () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, NoOp, CorrectErrorOnAnyQubit);
let errors = PauliErrors();
for pauliError in errors {
Message($"Task 06: Testing on {pauliError}...");
AssertOperationsEqualReferenced(1, partialBind(pauliError), NoOp);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 07
//////////////////////////////////////////////////////////////////////////
@Test("QuantumSimulator")
operation T07_LogicalX () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, LogicalX, CorrectErrorOnAnyQubit_Reference);
let errors = PauliErrors();
for pauliError in errors {
Message($"Task 07: Testing on {pauliError}...");
AssertOperationsEqualReferenced(1, partialBind(pauliError), ApplyPauli([PauliX], _));
}
}
//////////////////////////////////////////////////////////////////////////
// Task 08
//////////////////////////////////////////////////////////////////////////
@Test("QuantumSimulator")
operation T08_LogicalZ () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, LogicalZ, CorrectErrorOnAnyQubit_Reference);
let errors = PauliErrors();
for pauliError in errors {
Message($"Task 08: Testing on {pauliError}...");
AssertOperationsEqualReferenced(1, partialBind(pauliError), ApplyToEachA(Z, _));
}
}
}