829 строки
33 KiB
Plaintext
829 строки
33 KiB
Plaintext
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// This file contains testing harness for all tasks.
|
|
// You should not modify anything in this file.
|
|
// The tasks themselves can be found in Tasks.qs file.
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
namespace Quantum.Kata.Measurements {
|
|
|
|
open Microsoft.Quantum.Intrinsic;
|
|
open Microsoft.Quantum.Canon;
|
|
open Microsoft.Quantum.Diagnostics;
|
|
open Microsoft.Quantum.Convert;
|
|
open Microsoft.Quantum.Math;
|
|
open Microsoft.Quantum.Arrays;
|
|
open Microsoft.Quantum.Random;
|
|
|
|
open Quantum.Kata.Utils;
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// "Framework" operation for testing single-qubit tasks for distinguishing states of one qubit
|
|
// with Bool return
|
|
operation DistinguishTwoStates_OneQubit (statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Bool), stateName : String[]) : Unit {
|
|
let nTotal = 100;
|
|
let nStates = 2;
|
|
mutable misclassifications = [0, size = nStates];
|
|
|
|
use q = Qubit();
|
|
for i in 1 .. nTotal {
|
|
// get a random bit to define whether qubit will be in a state corresponding to true return (1) or to false one (0)
|
|
// state = 0 false return
|
|
// state = 1 true return
|
|
let state = DrawRandomInt(0, 1);
|
|
|
|
// do state prep: convert |0⟩ to outcome with false return or to outcome with true return depending on state
|
|
statePrep(q, state);
|
|
|
|
// get the solution's answer and verify if NOT a match, then differentiate what kind of mismatch
|
|
let ans = testImpl(q);
|
|
if ans != (state == 1) {
|
|
set misclassifications w/= state <- misclassifications[state] + 1;
|
|
}
|
|
|
|
// we're not checking the state of the qubit after the operation
|
|
Reset(q);
|
|
}
|
|
|
|
mutable totalMisclassifications = 0;
|
|
for i in 0 .. nStates - 1 {
|
|
if misclassifications[i] != 0 {
|
|
set totalMisclassifications += misclassifications[i];
|
|
Message($"Misclassified {stateName[i]} as {stateName[1 - i]} in {misclassifications[i]} test runs.");
|
|
}
|
|
}
|
|
|
|
// This check will tell the total number of failed classifications
|
|
Fact(totalMisclassifications == 0, $"{totalMisclassifications} test runs out of {nTotal} returned incorrect state (see output for details).");
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_IsQubitOne (q : Qubit, state : Int) : Unit {
|
|
if state != 0 {
|
|
// convert |0⟩ to |1⟩
|
|
X(q);
|
|
}
|
|
}
|
|
|
|
@Test("QuantumSimulator")
|
|
operation T101_IsQubitOne () : Unit {
|
|
DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitOne, ["|0⟩", "|1⟩"]);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
@Test("QuantumSimulator")
|
|
operation T102_InitializeQubit () : Unit {
|
|
use q = Qubit();
|
|
for i in 0 .. 36 {
|
|
let alpha = ((2.0 * PI()) * IntAsDouble(i)) / 36.0;
|
|
Ry(2.0 * alpha, q);
|
|
|
|
// Test Task 1
|
|
InitializeQubit(q);
|
|
|
|
// Confirm that the state is |0⟩.
|
|
AssertQubit(Zero, q);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_IsQubitPlus (q : Qubit, state : Int) : Unit {
|
|
if state == 0 {
|
|
// convert |0⟩ to |-⟩
|
|
X(q);
|
|
H(q);
|
|
} else {
|
|
// convert |0⟩ to |+⟩
|
|
H(q);
|
|
}
|
|
}
|
|
|
|
@Test("QuantumSimulator")
|
|
operation T103_IsQubitPlus () : Unit {
|
|
DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitPlus, ["|-⟩", "|+⟩"]);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
// |A⟩ = cos(alpha) * |0⟩ + sin(alpha) * |1⟩,
|
|
// |B⟩ = - sin(alpha) * |0⟩ + cos(alpha) * |1⟩.
|
|
operation StatePrep_IsQubitA (alpha : Double, q : Qubit, state : Int) : Unit {
|
|
if state == 0 {
|
|
// convert |0⟩ to |B⟩
|
|
X(q);
|
|
Ry(2.0 * alpha, q);
|
|
} else {
|
|
// convert |0⟩ to |A⟩
|
|
Ry(2.0 * alpha, q);
|
|
}
|
|
}
|
|
|
|
@Test("QuantumSimulator")
|
|
operation T104_IsQubitA () : Unit {
|
|
// cross-test
|
|
// alpha = 0.0 or PI() => !isQubitOne
|
|
// alpha = PI() / 2.0 => isQubitOne
|
|
DistinguishTwoStates_OneQubit(StatePrep_IsQubitOne, IsQubitA(PI() / 2.0, _),
|
|
[$"|B⟩=(-sin(π/2)|0⟩ + cos(π/2)|1⟩)", $"|A⟩=(cos(π/2)|0⟩ + sin(π/2)|1⟩)"]);
|
|
|
|
// alpha = PI() / 4.0 => isQubitPlus
|
|
DistinguishTwoStates_OneQubit(StatePrep_IsQubitPlus, IsQubitA(PI() / 4.0, _),
|
|
[$"|B⟩=(-sin(π/4)|0⟩ + cos(π/4)|1⟩)", $"|A⟩=(cos(π/4)|0⟩ + sin(π/4)|1⟩)"]);
|
|
|
|
for i in 0 .. 10 {
|
|
let alpha = (PI() * IntAsDouble(i)) / 10.0;
|
|
DistinguishTwoStates_OneQubit(StatePrep_IsQubitA(alpha, _, _), IsQubitA(alpha, _),
|
|
[$"|B⟩=(-sin({i}π/10)|0⟩ + cos({i}π/10)|1⟩)", $"|A⟩=(cos({i}π/10)|0⟩ + sin({i}π/10)|1⟩)"]);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
|
|
// "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits
|
|
// with Int return
|
|
operation DistinguishStates_MultiQubit (nQubits : Int,
|
|
nStates : Int,
|
|
statePrep : ((Qubit[], Int) => Unit),
|
|
testImpl : (Qubit[] => Int),
|
|
measurementsPerRun : Int,
|
|
stateNames : String[]) : Unit {
|
|
let nTotal = 100;
|
|
// misclassifications will store the number of times state i has been classified as state j (dimension nStates^2)
|
|
mutable misclassifications = [0, size = nStates * nStates];
|
|
// unknownClassifications will store the number of times state i has been classified as some invalid state (index < 0 or >= nStates)
|
|
mutable unknownClassifications = [0, size = nStates];
|
|
|
|
use qs = Qubit[nQubits];
|
|
for _ in 1 .. nTotal {
|
|
// get a random integer to define the state of the qubits
|
|
let state = DrawRandomInt(0, nStates - 1);
|
|
|
|
// do state prep: convert |0...0⟩ to outcome with return equal to state
|
|
statePrep(qs, state);
|
|
|
|
if measurementsPerRun > 0 {
|
|
ResetOracleCallsCount();
|
|
}
|
|
// get the solution's answer and verify that it's a match, if not, increase the exact mismatch count
|
|
let ans = testImpl(qs);
|
|
if ((ans >= 0) and (ans < nStates)) {
|
|
// classification result is a valid state index - check if is it correct
|
|
if ans != state {
|
|
set misclassifications w/= ((state * nStates) + ans) <- (misclassifications[(state * nStates) + ans] + 1);
|
|
}
|
|
}
|
|
else {
|
|
// classification result is an invalid state index - file it separately
|
|
set unknownClassifications w/= state <- (unknownClassifications[state] + 1);
|
|
}
|
|
// if we have a max number of measurements per solution run specified, check that it is not exceeded
|
|
if measurementsPerRun > 0 {
|
|
let nm = GetOracleCallsCount(Measure);
|
|
EqualityFactB(nm <= 1, true, $"You are allowed to do at most one measurement, and you did {nm}");
|
|
}
|
|
|
|
// we're not checking the state of the qubit after the operation
|
|
ResetAll(qs);
|
|
}
|
|
|
|
mutable totalMisclassifications = 0;
|
|
for i in 0 .. nStates - 1 {
|
|
for j in 0 .. nStates - 1 {
|
|
if misclassifications[(i * nStates) + j] != 0 {
|
|
set totalMisclassifications += misclassifications[i * nStates + j];
|
|
Message($"Misclassified {stateNames[i]} as {stateNames[j]} in {misclassifications[(i * nStates) + j]} test runs.");
|
|
}
|
|
}
|
|
if unknownClassifications[i] != 0 {
|
|
set totalMisclassifications += unknownClassifications[i];
|
|
Message($"Misclassified {stateNames[i]} as Unknown State in {unknownClassifications[i]} test runs.");
|
|
}
|
|
}
|
|
// This check will tell the total number of failed classifications
|
|
Fact(totalMisclassifications == 0, $"{totalMisclassifications} test runs out of {nTotal} returned incorrect state (see output for details).");
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_ZeroZeroOrOneOne (qs : Qubit[], state : Int) : Unit {
|
|
if state == 1 {
|
|
// |11⟩
|
|
X(qs[0]);
|
|
X(qs[1]);
|
|
}
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T105_ZeroZeroOrOneOne () : Unit {
|
|
DistinguishStates_MultiQubit(2, 2, StatePrep_ZeroZeroOrOneOne, ZeroZeroOrOneOne, 0, ["|00⟩", "|11⟩"]);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_BasisStateMeasurement (qs : Qubit[], state : Int) : Unit {
|
|
|
|
if state / 2 == 1 {
|
|
// |10⟩ or |11⟩
|
|
X(qs[0]);
|
|
}
|
|
|
|
if state % 2 == 1 {
|
|
// |01⟩ or |11⟩
|
|
X(qs[1]);
|
|
}
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T106_BasisStateMeasurement () : Unit {
|
|
DistinguishStates_MultiQubit(2, 4, StatePrep_BasisStateMeasurement, BasisStateMeasurement, 0, ["|00⟩", "|01⟩", "|10⟩", "|11⟩"]);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_Bitstring (qs : Qubit[], bits : Bool[]) : Unit {
|
|
for i in 0 .. Length(qs) - 1 {
|
|
if bits[i] {
|
|
X(qs[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
operation StatePrep_TwoBitstringsMeasurement (qs : Qubit[], bits1 : Bool[], bits2 : Bool[], state : Int) : Unit {
|
|
let bits = state == 0 ? bits1 | bits2;
|
|
StatePrep_Bitstring(qs, bits);
|
|
}
|
|
|
|
|
|
// Helper function to convert a boolean array to its ket state representation
|
|
function BoolArrayAsKetState (bits : Bool[]) : String {
|
|
mutable stateName = "|";
|
|
for i in 0 .. Length(bits) - 1 {
|
|
set stateName += (bits[i] ? "1" | "0");
|
|
}
|
|
return stateName + "⟩";
|
|
}
|
|
|
|
|
|
operation CheckTwoBitstringsMeasurement (b1 : Bool[], b2 : Bool[]) : Unit {
|
|
DistinguishStates_MultiQubit(Length(b1), 2, StatePrep_TwoBitstringsMeasurement(_, b1, b2, _),
|
|
TwoBitstringsMeasurement(_, b1, b2), 1,
|
|
[BoolArrayAsKetState(b1), BoolArrayAsKetState(b2)]);
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T107_TwoBitstringsMeasurement () : Unit {
|
|
mutable b1 = [false, true];
|
|
mutable b2 = [true, false];
|
|
CheckTwoBitstringsMeasurement(b1, b2);
|
|
|
|
set b1 = [true, true, false];
|
|
set b2 = [false, true, true];
|
|
CheckTwoBitstringsMeasurement(b1, b2);
|
|
|
|
set b1 = [false, true, true, false];
|
|
set b2 = [false, true, true, true];
|
|
CheckTwoBitstringsMeasurement(b1, b2);
|
|
|
|
set b1 = [true, false, false, false];
|
|
set b2 = [true, false, true, true];
|
|
CheckTwoBitstringsMeasurement(b1, b2);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_FindFirstDiff (bits1 : Bool[], bits2 : Bool[]) : Int {
|
|
for i in 0 .. Length(bits1) - 1 {
|
|
if bits1[i] != bits2[i] {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
// a combination of tasks 14 and 15 from the Superposition kata
|
|
operation StatePrep_BitstringSuperposition (qs : Qubit[], bits : Bool[][]) : Unit {
|
|
let L = Length(bits);
|
|
Fact(L == 1 or L == 2 or L == 4, "State preparation only supports arrays of 1, 2 or 4 bit strings");
|
|
if L == 1 {
|
|
for i in 0 .. Length(qs) - 1 {
|
|
if bits[0][i] {
|
|
X(qs[i]);
|
|
}
|
|
}
|
|
}
|
|
if L == 2 {
|
|
// find the index of the first bit at which the bit strings are different
|
|
let firstDiff = StatePrep_FindFirstDiff(bits[0], bits[1]);
|
|
|
|
// Hadamard corresponding qubit to create superposition
|
|
H(qs[firstDiff]);
|
|
|
|
// iterate through the bit strings again setting the final state of qubits
|
|
for i in 0 .. Length(qs) - 1 {
|
|
if bits[0][i] == bits[1][i] {
|
|
// if two bits are the same, apply X or nothing
|
|
if bits[0][i] {
|
|
X(qs[i]);
|
|
}
|
|
} else {
|
|
// if two bits are different, set their difference using CNOT
|
|
if i > firstDiff {
|
|
CNOT(qs[firstDiff], qs[i]);
|
|
if bits[0][i] != bits[0][firstDiff] {
|
|
X(qs[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if L == 4 {
|
|
let N = Length(qs);
|
|
|
|
use anc = Qubit[2];
|
|
// Put two ancillas into equal superposition of 2-qubit basis states
|
|
ApplyToEachA(H, anc);
|
|
|
|
// Set up the right pattern on the main qubits with control on ancillas
|
|
for i in 0 .. 3 {
|
|
for j in 0 .. N - 1 {
|
|
if bits[i][j] {
|
|
(ControlledOnInt(i, X))(anc, qs[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Uncompute the ancillas, using patterns on main qubits as control
|
|
for i in 0 .. 3 {
|
|
if i % 2 == 1 {
|
|
(ControlledOnBitString(bits[i], X))(qs, anc[0]);
|
|
}
|
|
if i / 2 == 1 {
|
|
(ControlledOnBitString(bits[i], X))(qs, anc[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
operation StatePrep_SuperpositionMeasurement (qs : Qubit[], bits1 : Bool[][], bits2 : Bool[][], state : Int) : Unit {
|
|
let bits = state == 0 ? bits1 | bits2;
|
|
StatePrep_BitstringSuperposition(qs, bits);
|
|
}
|
|
|
|
|
|
// Helper function to convert an array of bit strings to its ket state representation
|
|
function IntArrayAsStateName (qubits : Int, bitStrings : Bool[][]) : String {
|
|
mutable statename = "";
|
|
for i in 0 .. Length(bitStrings) - 1 {
|
|
if i > 0 {
|
|
set statename += " + ";
|
|
}
|
|
set statename += BoolArrayAsKetState(bitStrings[i]);
|
|
}
|
|
return statename;
|
|
}
|
|
|
|
|
|
operation CheckSuperpositionBitstringsOneMeasurement (nQubits : Int, ints1 : Int[], ints2 : Int[]): Unit {
|
|
let bits1 = Mapped(IntAsBoolArray(_, nQubits), ints1);
|
|
let bits2 = Mapped(IntAsBoolArray(_, nQubits), ints2);
|
|
|
|
DistinguishStates_MultiQubit(nQubits, 2, StatePrep_SuperpositionMeasurement(_, bits1, bits2, _),
|
|
SuperpositionOneMeasurement(_, bits1, bits2), 1,
|
|
[IntArrayAsStateName(nQubits, bits1), IntArrayAsStateName(nQubits, bits2)]);
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T108_SuperpositionOneMeasurement () : Unit {
|
|
// note that bit strings in the comments (big endian) are the reverse of the bit strings passed to the solutions (little endian)
|
|
CheckSuperpositionBitstringsOneMeasurement(2, [2], // [10]
|
|
[1]); // [01]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(2, [2,3], // [10,11]
|
|
[1,0]); // [01,00]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(2, [2], // [10]
|
|
[1,0]); // [01,00]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(4, [15,7], // [1111,0111]
|
|
[0,8]); // [0000,1000]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(4, [15,7], // [1111,0111]
|
|
[0,8,10,12]); // [0000,1000,1010,1100]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(5, [30,14,10,6], // [11110,01110,01010,00110]
|
|
[1,17,21,25]); // [00001,10001,10101,11001]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(2, [0,2], // [00,10]
|
|
[3]); // [11]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(3, [5,7], // [101,111]
|
|
[2]); // [010]
|
|
|
|
CheckSuperpositionBitstringsOneMeasurement(4, [13,11,7,3], // [1101,1011,0111,0011]
|
|
[2,4]); // [0010,0100]
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation CheckSuperpositionBitstringsMeasurement (nQubits : Int, ints1 : Int[], ints2 : Int[]): Unit {
|
|
let bits1 = Mapped(IntAsBoolArray(_, nQubits), ints1);
|
|
let bits2 = Mapped(IntAsBoolArray(_, nQubits), ints2);
|
|
|
|
DistinguishStates_MultiQubit(nQubits, 2, StatePrep_SuperpositionMeasurement(_, bits1, bits2, _),
|
|
SuperpositionMeasurement(_, bits1, bits2), 0,
|
|
[IntArrayAsStateName(nQubits, bits1), IntArrayAsStateName(nQubits, bits2)]);
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T109_SuperpositionMeasurement () : Unit {
|
|
// note that bit strings in the comments (big endian) are the reverse of the bit strings passed to the solutions (little endian)
|
|
CheckSuperpositionBitstringsMeasurement(2, [2], // [10]
|
|
[1]); // [01]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(2, [2,1], // [10,01]
|
|
[3,0]); // [11,00]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(2, [2], // [10]
|
|
[3,0]); // [11,00]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(4, [15,6], // [1111,0110]
|
|
[0,14]); // [0000,1110]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(4, [15,7], // [1111,0111]
|
|
[0,8,10,13]); // [0000,1000,1010,1101]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(5, [30,14,10,7], // [11110,01110,01010,00111]
|
|
[1,17,21,27]); // [00001,10001,10101,11011]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(2, [2,1], // [10,01]
|
|
[3]); // [11]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(3, [7,5], // [111,101]
|
|
[2]); // [010]
|
|
|
|
CheckSuperpositionBitstringsMeasurement(4, [13,11,7,3], // [1101,1011,0111,0011]
|
|
[5,2]); // [0101,0010]
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit is Adj + Ctl {
|
|
let N = Length(qs);
|
|
|
|
if N == 1 {
|
|
// base case of recursion: |1⟩
|
|
X(qs[0]);
|
|
} else {
|
|
// |W_N⟩ = |0⟩|W_(N-1)⟩ + |1⟩|0...0⟩
|
|
// do a rotation on the first qubit to split it into |0⟩ and |1⟩ with proper weights
|
|
// |0⟩ -> sqrt((N-1)/N) |0⟩ + 1/sqrt(N) |1⟩
|
|
let theta = ArcSin(1.0 / Sqrt(IntAsDouble(N)));
|
|
Ry(2.0 * theta, qs[0]);
|
|
|
|
// do a zero-controlled W-state generation for qubits 1..N-1
|
|
X(qs[0]);
|
|
Controlled WState_Arbitrary_Reference(qs[0 .. 0], qs[1 .. N - 1]);
|
|
X(qs[0]);
|
|
}
|
|
}
|
|
|
|
operation StatePrep_AllZerosOrWState (qs : Qubit[], state : Int) : Unit {
|
|
|
|
if state == 1 {
|
|
// prep W state
|
|
WState_Arbitrary_Reference(qs);
|
|
}
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T110_AllZerosOrWState () : Unit {
|
|
|
|
for i in 2 .. 6 {
|
|
DistinguishStates_MultiQubit(i, 2, StatePrep_AllZerosOrWState, AllZerosOrWState, 0, ["|0...0⟩", "|W⟩"]);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation GHZ_State_Reference (qs : Qubit[]) : Unit is Adj {
|
|
H(Head(qs));
|
|
for q in Rest(qs) {
|
|
CNOT(Head(qs), q);
|
|
}
|
|
}
|
|
|
|
|
|
operation StatePrep_GHZOrWState (qs : Qubit[], state : Int) : Unit {
|
|
|
|
if state == 0 {
|
|
// prep GHZ state
|
|
GHZ_State_Reference(qs);
|
|
} else {
|
|
// prep W state
|
|
WState_Arbitrary_Reference(qs);
|
|
}
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T111_GHZOrWState () : Unit {
|
|
for i in 2 .. 6 {
|
|
DistinguishStates_MultiQubit(i, 2, StatePrep_GHZOrWState, GHZOrWState, 0, ["|GHZ⟩", "|W⟩"]);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
// 0 - |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2)
|
|
// 1 - |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2)
|
|
// 2 - |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2)
|
|
// 3 - |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2)
|
|
operation StatePrep_BellState (qs : Qubit[], state : Int) : Unit {
|
|
H(qs[0]);
|
|
CNOT(qs[0], qs[1]);
|
|
|
|
// now we have |00⟩ + |11⟩ - modify it based on state arg
|
|
if state % 2 == 1 {
|
|
// negative phase
|
|
Z(qs[1]);
|
|
}
|
|
if state / 2 == 1 {
|
|
X(qs[1]);
|
|
}
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T112_BellState () : Unit {
|
|
DistinguishStates_MultiQubit(2, 4, StatePrep_BellState, BellState, 0, ["|Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2)",
|
|
"|Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2)",
|
|
"|Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2)",
|
|
"|Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2)"]);
|
|
}
|
|
|
|
// ------------------------------------------------------
|
|
// 0 - (|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2
|
|
// 1 - (|00⟩ - |01⟩ + |10⟩ - |11⟩) / 2
|
|
// 2 - (|00⟩ + |01⟩ - |10⟩ - |11⟩) / 2
|
|
// 3 - (|00⟩ - |01⟩ - |10⟩ + |11⟩) / 2
|
|
operation StatePrep_TwoQubitState (qs : Qubit[], state : Int) : Unit {
|
|
// start with state prep of basis vectors
|
|
StatePrep_BasisStateMeasurement(qs, state);
|
|
H(qs[0]);
|
|
H(qs[1]);
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T113_TwoQubitState () : Unit {
|
|
DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitState, TwoQubitState, 0, ["(|00⟩ + |01⟩ + |10⟩ + |11⟩) / 2",
|
|
"(|00⟩ - |01⟩ + |10⟩ - |11⟩) / 2",
|
|
"(|00⟩ + |01⟩ - |10⟩ - |11⟩) / 2",
|
|
"(|00⟩ - |01⟩ - |10⟩ + |11⟩) / 2"]);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
// 0 - ( |00⟩ - |01⟩ - |10⟩ - |11⟩) / 2
|
|
// 1 - (-|00⟩ + |01⟩ - |10⟩ - |11⟩) / 2
|
|
// 2 - (-|00⟩ - |01⟩ + |10⟩ - |11⟩) / 2
|
|
// 3 - (-|00⟩ - |01⟩ - |10⟩ + |11⟩) / 2
|
|
operation StatePrep_TwoQubitStatePartTwo (qs : Qubit[], state : Int) : Unit {
|
|
|
|
// start with state prep of basis vectors
|
|
StatePrep_BasisStateMeasurement(qs, state);
|
|
|
|
// now apply all gates for unitary in reference impl (in reverse + adjoint)
|
|
within {
|
|
ApplyToEachA(X, qs);
|
|
Controlled Z([qs[0]], qs[1]);
|
|
ApplyToEachA(X, qs);
|
|
} apply {
|
|
ApplyToEach(H, qs);
|
|
}
|
|
SWAP(qs[0], qs[1]);
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T114_TwoQubitStatePartTwo () : Unit {
|
|
DistinguishStates_MultiQubit(2, 4, StatePrep_TwoQubitStatePartTwo, TwoQubitStatePartTwo, 0, ["(+|00⟩ - |01⟩ - |10⟩ - |11⟩) / 2",
|
|
"(-|00⟩ + |01⟩ - |10⟩ - |11⟩) / 2",
|
|
"(-|00⟩ - |01⟩ + |10⟩ - |11⟩) / 2",
|
|
"(-|00⟩ - |01⟩ - |10⟩ + |11⟩) / 2"]);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_ThreeQubitMeasurement (qs : Qubit[], state : Int) : Unit is Adj {
|
|
|
|
WState_Arbitrary_Reference(qs);
|
|
|
|
if state == 0 {
|
|
// prep 1/sqrt(3) ( |100⟩ + ω |010⟩ + ω² |001⟩ )
|
|
R1(2.0 * PI() / 3.0, qs[1]);
|
|
R1(4.0 * PI() / 3.0, qs[2]);
|
|
} else {
|
|
// prep 1/sqrt(3) ( |100⟩ + ω² |010⟩ + ω |001⟩ )
|
|
R1(4.0 * PI() / 3.0, qs[1]);
|
|
R1(2.0 * PI() / 3.0, qs[2]);
|
|
}
|
|
}
|
|
|
|
@Test("Microsoft.Quantum.Katas.CounterSimulator")
|
|
operation T115_ThreeQubitMeasurement () : Unit {
|
|
DistinguishStates_MultiQubit(3, 2, StatePrep_ThreeQubitMeasurement, ThreeQubitMeasurement, 0,
|
|
["1/sqrt(3) (|100⟩ + ω |010⟩ + ω² |001⟩)",
|
|
"1/sqrt(3) (|100⟩ + ω² |010⟩ + ω |001⟩)"]);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// Part II*. Discriminating Nonorthogonal States
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
operation StatePrep_IsQubitZeroOrPlus (q : Qubit, state : Int) : Unit {
|
|
|
|
if state != 0 {
|
|
// convert |0⟩ to |+⟩
|
|
H(q);
|
|
}
|
|
}
|
|
|
|
|
|
// "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits
|
|
// with Int return. Framework tests against a threshold parameter for the fraction of runs that must succeed.
|
|
operation DistinguishStates_MultiQubit_Threshold (Nqubit : Int, Nstate : Int, threshold : Double, statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Bool)) : Unit {
|
|
let nTotal = 1000;
|
|
mutable nOk = 0;
|
|
|
|
use qs = Qubit[Nqubit];
|
|
for i in 1 .. nTotal {
|
|
// get a random integer to define the state of the qubits
|
|
let state = DrawRandomInt(0, Nstate - 1);
|
|
|
|
// do state prep: convert |0⟩ to outcome with return equal to state
|
|
statePrep(qs[0], state);
|
|
|
|
// get the solution's answer and verify that it's a match
|
|
let ans = testImpl(qs[0]);
|
|
if ans == (state == 0) {
|
|
set nOk += 1;
|
|
}
|
|
|
|
// we're not checking the state of the qubit after the operation
|
|
ResetAll(qs);
|
|
}
|
|
|
|
if IntAsDouble(nOk) < threshold * IntAsDouble(nTotal) {
|
|
fail $"{nTotal - nOk} test runs out of {nTotal} returned incorrect state which does not meet the required threshold of at least {threshold * 100.0}%.";
|
|
}
|
|
}
|
|
|
|
@Test("QuantumSimulator")
|
|
operation T201_IsQubitZeroOrPlus () : Unit {
|
|
DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, StatePrep_IsQubitZeroOrPlus, IsQubitPlusOrZero);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
// "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits
|
|
// with Int return. Framework tests against a threshold parameter for the fraction of runs that must succeed.
|
|
// Framework tests in the USD scenario, i.e., it is allowed to respond "inconclusive" (with some probability)
|
|
// up to given threshold, but it is never allowed to err if an actual conclusive response is given.
|
|
operation USD_DistinguishStates_MultiQubit_Threshold (Nqubit : Int, Nstate : Int, thresholdInconcl : Double, thresholdConcl : Double, statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Int)) : Unit {
|
|
let nTotal = 10000;
|
|
|
|
// counts total inconclusive answers
|
|
mutable nInconc = 0;
|
|
|
|
// counts total conclusive |0⟩ state identifications
|
|
mutable nConclOne = 0;
|
|
|
|
// counts total conclusive |+> state identifications
|
|
mutable nConclPlus = 0;
|
|
|
|
use qs = Qubit[Nqubit];
|
|
for i in 1 .. nTotal {
|
|
|
|
// get a random integer to define the state of the qubits
|
|
let state = DrawRandomInt(0, Nstate - 1);
|
|
|
|
// do state prep: convert |0⟩ to outcome with return equal to state
|
|
statePrep(qs[0], state);
|
|
|
|
// get the solution's answer and verify that it's a match
|
|
let ans = testImpl(qs[0]);
|
|
|
|
// check that the answer is actually in allowed range
|
|
if (ans < -1 or ans > 1) {
|
|
fail $"state {state} led to invalid response {ans}.";
|
|
}
|
|
|
|
// keep track of the number of inconclusive answers given
|
|
if ans == -1 {
|
|
set nInconc += 1;
|
|
}
|
|
|
|
if (ans == 0 and state == 0) {
|
|
set nConclOne += 1;
|
|
}
|
|
|
|
if (ans == 1 and state == 1) {
|
|
set nConclPlus += 1;
|
|
}
|
|
|
|
// check if upon conclusive result the answer is actually correct
|
|
if (ans == 0 and state == 1 or ans == 1 and state == 0) {
|
|
fail $"state {state} led to incorrect conclusive response {ans}.";
|
|
}
|
|
|
|
// we're not checking the state of the qubit after the operation
|
|
ResetAll(qs);
|
|
}
|
|
|
|
if IntAsDouble(nInconc) > thresholdInconcl * IntAsDouble(nTotal) {
|
|
fail $"{nInconc} test runs out of {nTotal} returned inconclusive which does not meet the required threshold of at most {thresholdInconcl * 100.0}%.";
|
|
}
|
|
|
|
if IntAsDouble(nConclOne) < thresholdConcl * IntAsDouble(nTotal) {
|
|
fail $"Only {nConclOne} test runs out of {nTotal} returned conclusive |0⟩ which does not meet the required threshold of at least {thresholdConcl * 100.0}%.";
|
|
}
|
|
|
|
if IntAsDouble(nConclPlus) < thresholdConcl * IntAsDouble(nTotal) {
|
|
fail $"Only {nConclPlus} test runs out of {nTotal} returned conclusive |+> which does not meet the required threshold of at least {thresholdConcl * 100.0}%.";
|
|
}
|
|
}
|
|
|
|
@Test("QuantumSimulator")
|
|
operation T202_IsQubitZeroOrPlusSimpleUSD () : Unit {
|
|
USD_DistinguishStates_MultiQubit_Threshold(1, 2, 0.8, 0.1, StatePrep_IsQubitZeroOrPlus, IsQubitPlusZeroOrInconclusiveSimpleUSD);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------
|
|
operation StatePrep_IsQubitNotInABC (q : Qubit, state : Int) : Unit {
|
|
let alpha = (2.0 * PI()) / 3.0;
|
|
H(q);
|
|
|
|
if state == 0 {
|
|
// convert |0⟩ to 1/sqrt(2) (|0⟩ + |1⟩)
|
|
}
|
|
elif state == 1 {
|
|
// convert |0⟩ to 1/sqrt(2) (|0⟩ + ω |1⟩), where ω = exp(2iπ/3)
|
|
R1(alpha, q);
|
|
}
|
|
else {
|
|
// convert |0⟩ to 1/sqrt(2) (|0⟩ + ω² |1⟩), where ω = exp(2iπ/3)
|
|
R1(2.0 * alpha, q);
|
|
}
|
|
}
|
|
|
|
|
|
// "Framework" operation for testing multi-qubit tasks for distinguishing states of an array of qubits
|
|
// with Int return. Framework tests instances of the Peres/Wootters game. In this game, one of three
|
|
// "trine" states is presented and the algorithm must answer with a label that does not correspond
|
|
// to one of the label of the input state.
|
|
operation ABC_DistinguishStates_MultiQubit_Threshold (Nqubit : Int, Nstate : Int, statePrep : ((Qubit, Int) => Unit), testImpl : (Qubit => Int)) : Unit {
|
|
|
|
let nTotal = 1000;
|
|
|
|
use qs = Qubit[Nqubit];
|
|
|
|
for i in 1 .. nTotal {
|
|
// get a random integer to define the state of the qubits
|
|
let state = DrawRandomInt(0, Nstate - 1);
|
|
|
|
// do state prep: convert |0⟩ to outcome with return equal to state
|
|
statePrep(qs[0], state);
|
|
|
|
// get the solution's answer and verify that it's a match
|
|
let ans = testImpl(qs[0]);
|
|
|
|
// check that the value of ans is 0, 1 or 2
|
|
if (ans < 0 or ans > 2) {
|
|
fail "You can not return any value other than 0, 1 or 2.";
|
|
}
|
|
|
|
// check if upon conclusive result the answer is actually correct
|
|
if ans == state {
|
|
fail $"State {state} led to incorrect conclusive response {ans}.";
|
|
}
|
|
|
|
// we're not checking the state of the qubit after the operation
|
|
ResetAll(qs);
|
|
}
|
|
}
|
|
|
|
@Test("QuantumSimulator")
|
|
operation T203_IsQubitNotInABC () : Unit {
|
|
ABC_DistinguishStates_MultiQubit_Threshold(1, 3, StatePrep_IsQubitNotInABC, IsQubitNotInABC);
|
|
}
|
|
}
|