QuantumKatas/QEC_BitFlipCode/Tests.qs

351 строка
12 KiB
Plaintext

// 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 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 {
using (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(M) + GetOracleCallsCount(Measure);
Fact(nm <= 1, $"You are allowed to do at most one measurement, and you did {nm}");
}
}
operation T01_MeasureParity_Test () : 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 {
using (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]);
}
operation T02_Encode_Test () : 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]);
}
}
operation T03_DetectErrorOnLeftQubit_Test () : Unit {
using (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 {
using (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]];
}
operation T04_CorrectErrorOnLeftQubit_Test () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, NoOp<Qubit[]>, CorrectErrorOnLeftQubit);
let errors = PauliErrors();
for (idxError in 0 .. 1) {
AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), NoOp<Qubit[]>);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 05
//////////////////////////////////////////////////////////////////////////
operation T05_DetectErrorOnAnyQubit_Test () : Unit {
let errors = PauliErrors();
using (register = Qubit[3]) {
for (idxError in 0 .. Length(errors) - 1) {
let θ = RandomReal(12);
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
//////////////////////////////////////////////////////////////////////////
operation T06_CorrectErrorOnAnyQubit_Test () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, NoOp<Qubit[]>, CorrectErrorOnAnyQubit);
let errors = PauliErrors();
for (idxError in 0 .. Length(errors) - 1) {
Message($"Task 06: Testing on {errors[idxError]}...");
AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), NoOp<Qubit[]>);
}
}
//////////////////////////////////////////////////////////////////////////
// Task 07
//////////////////////////////////////////////////////////////////////////
operation T07_LogicalX_Test () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, LogicalX, CorrectErrorOnAnyQubit_Reference);
let errors = PauliErrors();
for (idxError in 0 .. Length(errors) - 1) {
Message($"Task 07: Testing on {errors[idxError]}...");
AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), ApplyPauli([PauliX], _));
}
}
//////////////////////////////////////////////////////////////////////////
// Task 08
//////////////////////////////////////////////////////////////////////////
operation T08_LogicalZ_Test () : Unit {
let partialBind = BindErrorCorrectionRound(Encode_Reference, _, LogicalZ, CorrectErrorOnAnyQubit_Reference);
let errors = PauliErrors();
for (idxError in 0 .. Length(errors) - 1) {
Message($"Task 08: Testing on {errors[idxError]}...");
AssertOperationsEqualReferenced(1, partialBind(errors[idxError]), ApplyToEachA(Z, _));
}
}
}