QuantumKatas/MagicSquareGame/Tests.qs

318 строки
13 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.MagicSquareGame {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Random;
function SignFromBool (input : Bool) : Int {
return input ? 1 | -1;
}
@Test("QuantumSimulator")
operation T111_ValidAliceMove () : Unit {
// Try all moves with +1 and -1.
for i in 0..1 <<< 3 - 1 {
let cells = Mapped(SignFromBool, IntAsBoolArray(i, 3));
Fact(ValidAliceMove(cells) == ValidAliceMove_Reference(cells),
$"Incorrect Alice move validity for {cells}");
}
// Moves with numbers other than +1 and -1 should be rejected.
for cellsOutOfRange in [[1, -2, 10], [-3, 0, -2], [-1, 2, 1], [2, 3, 4]] {
Fact(ValidAliceMove(cellsOutOfRange) == false,
$"Invalid Alice move judged valid for {cellsOutOfRange}");
}
}
@Test("QuantumSimulator")
operation T112_ValidBobMove () : Unit {
// Try all moves with +1 and -1.
for i in 0..1 <<< 3 - 1 {
let cells = Mapped(SignFromBool, IntAsBoolArray(i, 3));
Fact(ValidBobMove(cells) == ValidBobMove_Reference(cells),
$"Incorrect Bob move validity for {cells}");
}
// Moves with numbers other than +1 and -1 should be rejected.
for cellsOutOfRange in [[1, -2, 10], [-3, 0, -2], [-1, 2, 1], [2, 3, 4]] {
Fact(ValidBobMove(cellsOutOfRange) == false,
$"Invalid Bob move judged valid for {cellsOutOfRange}");
}
}
// ------------------------------------------------------
@Test("QuantumSimulator")
operation T12_WinCondition () : Unit {
// Try all moves with +1 and -1.
for i in 0..1 <<< 3 - 1 {
for j in 0..1 <<< 3 - 1 {
for rowIndex in 0..2 {
for columnIndex in 0..2 {
let row = Mapped(SignFromBool, IntAsBoolArray(i, 3));
let column = Mapped(SignFromBool, IntAsBoolArray(j, 3));
Fact(
WinCondition(rowIndex, columnIndex, row, column) ==
WinCondition_Reference(rowIndex, columnIndex, row, column),
$"Incorrect win condition for rowIndex={rowIndex}, columnIndex={columnIndex}, " +
$" row={row}, column={column}");
}
}
}
}
// Moves with numbers other than +1 and -1 should be rejected.
let aliceOutOfRange = [-1, -1, 7];
let bobInRange = [-1, 1, 1];
Fact(not WinCondition(0, 0, aliceOutOfRange, bobInRange),
$"Win condition is wrong for Alice={aliceOutOfRange}, row=0, Bob={bobInRange}, column=0");
}
// ------------------------------------------------------
operation RunTrials (n : Int, moves : ((Int, Int) => (Int[], Int[]))) : Int {
mutable wins = 0;
for i in 1..n {
let rowIndex = DrawRandomInt(0, 2);
let columnIndex = DrawRandomInt(0, 2);
let (alice, bob) = moves(rowIndex, columnIndex);
if (WinCondition_Reference(rowIndex, columnIndex, alice, bob)) {
set wins += 1;
}
}
return wins;
}
operation ClassicalRunner (rowIndex : Int, columnIndex : Int) : (Int[], Int[]) {
return (AliceClassical(rowIndex), BobClassical(columnIndex));
}
@Test("QuantumSimulator")
operation T13_ClassicalStrategy() : Unit {
let wins = RunTrials(1000, ClassicalRunner);
Fact(wins >= 850, $"The classical strategy implemented is not optimal: win rate {IntAsDouble(wins) / 1000.}");
}
// ------------------------------------------------------
operation AssertEqualOnZeroState (N : Int, taskImpl : (Qubit[] => Unit),
refImpl : (Qubit[] => Unit is Adj)) : Unit {
use qs = Qubit[N];
// apply operation that needs to be tested
taskImpl(qs);
// apply adjoint reference operation and check that the result is |0^N⟩
Adjoint refImpl(qs);
// assert that all qubits end up in |0⟩ state
AssertAllZero(qs);
}
@Test("QuantumSimulator")
operation T21_CreateEntangledState () : Unit {
AssertEqualOnZeroState(4, CreateEntangledState, CreateEntangledState_Reference);
}
// ------------------------------------------------------
function Pairs<'T> (array : 'T[]) : ('T, 'T)[] {
let N = Length(array);
mutable pairs = [];
for j in 0..N - 1 {
for k in j + 1..N - 1 {
set pairs += [(array[j], array[k])];
}
}
return pairs;
}
operation ControlledWrapper (op : (Qubit[] => Unit is Adj+Ctl), qs : Qubit[]) : Unit is Adj+Ctl {
Controlled op([Head(qs)], Rest(qs));
}
operation AssertOperationsEqualWithPhase (N : Int, op1 : (Qubit[] => Unit is Adj+Ctl), op2 : (Qubit[] => Unit is Adj+Ctl)) : Unit {
// To check that the operations don't introduce a phase, compare their controlled versions
AssertOperationsEqualReferenced(N + 1, ControlledWrapper(op1, _), ControlledWrapper(op2, _));
}
// Helper function to checks that each pair of operations in the array commutes (i.e., AB = BA)
operation AssertOperationsMutuallyCommute (operations : (Qubit[] => Unit is Adj+Ctl)[]) : Unit {
for (a, b) in Pairs(operations) {
AssertOperationsEqualWithPhase(2, BoundCA([a, b]), BoundCA([b, a]));
}
}
operation MinusI (qs : Qubit[]) : Unit is Adj+Ctl {
Z(qs[0]);
X(qs[0]);
Z(qs[0]);
X(qs[0]);
// An alternative approach is to use R(PauliI, 2 * PI(), _)
}
function ApplyObservablesImpl (row : Int, column : Int) : (Qubit[] => Unit is Adj+Ctl) {
return ApplyMagicObservables_Reference(GetMagicObservables(row, column), _);
}
@Test("QuantumSimulator")
operation T22_GetMagicObservables () : Unit {
// Since there can be multiple magic squares with different observables,
// the test checks the listed properties of the return values rather than the values themselves.
for row in 0..2 {
let observables = Mapped(ApplyObservablesImpl(row, _), RangeAsIntArray(0..2));
// The product of observables in each row should be I.
AssertOperationsEqualWithPhase(2, BoundCA(observables), ApplyToEachCA(I, _));
AssertOperationsMutuallyCommute(observables);
}
for column in 0..2 {
let observables = Mapped(ApplyObservablesImpl(_, column), RangeAsIntArray(0..2));
// The product of observables in each column should be -I.
AssertOperationsEqualWithPhase(2, BoundCA(observables), MinusI);
AssertOperationsMutuallyCommute(observables);
}
}
// ------------------------------------------------------
@Test("QuantumSimulator")
operation T23_ApplyMagicObservables () : Unit {
// Try all pairs of observables and all signs, and check the unitary equality
for sign in [-1, 1] {
for obs1 in [PauliI, PauliX, PauliY, PauliZ] {
for obs2 in [PauliI, PauliX, PauliY, PauliZ] {
let obs = (sign, [obs1, obs2]);
AssertOperationsEqualWithPhase(2, ApplyMagicObservables(obs, _), ApplyMagicObservables_Reference(obs, _));
}
}
}
}
// ------------------------------------------------------
@Test("QuantumSimulator")
operation T24_MeasureObservables () : Unit {
use qs = Qubit[2];
for sign in [-1, 1] {
for obs1 in [PauliI, PauliX, PauliY, PauliZ] {
for obs2 in [PauliI, PauliX, PauliY, PauliZ] {
for i in 1..100 {
// Start by preparing the qubits in a uniform superposition with some signs
if ((i % 4) / 2 == 1) {
X(qs[0]);
}
if ((i % 4) % 2 == 1) {
X(qs[1]);
}
ApplyToEach(H, qs);
// Use the reference implementation of observable measurement to project the register into an eigenstate of the operator
let observable = (sign, [obs1, obs2]);
let result = MeasureObservable_Reference(observable, qs);
// Make sure the task implementation gets the same result.
// If the implementation is wrong, it could pass accidentally,
// but with multiple operators and multiple attempts on each this should fail at some point.
Fact(MeasureObservable(observable, qs) == result,
$"Observable measurement result differs from the reference result for observable {observable}");
ResetAll(qs);
}
}
}
}
}
// ------------------------------------------------------
@Test("QuantumSimulator")
operation T25_MeasureOperator () : Unit {
use qs = Qubit[2];
for sign in [-1, 1] {
for obs1 in [PauliI, PauliX, PauliY, PauliZ] {
for obs2 in [PauliI, PauliX, PauliY, PauliZ] {
for i in 1..100 {
// Start by preparing the qubits in a uniform superposition with some signs
if ((i % 4) / 2 == 1) {
X(qs[0]);
}
if ((i % 4) % 2 == 1) {
X(qs[1]);
}
ApplyToEach(H, qs);
// Use the reference implementation of operator measurement to project the register into an eigenstate of the operator
let observable = (sign, [obs1, obs2]);
let op = ApplyMagicObservables_Reference(observable, _);
let result = MeasureOperator_Reference(op, qs);
// Make sure the task implementation gets the same result.
// If the implementation is wrong, it could pass accidentally,
// but with multiple operators and multiple attempts on each this should fail at some point.
Fact(MeasureOperator(op, qs) == result,
$"Operator measurement result differs from the reference result for observable {observable}");
ResetAll(qs);
}
}
}
}
}
// ------------------------------------------------------
operation QuantumRunner (referee : (((Qubit[] => Int[]), (Qubit[] => Int[])) => (Int[], Int[])),
rowIndex : Int,
columnIndex : Int) : (Int[], Int[]) {
return referee(AliceQuantum(rowIndex, _), BobQuantum(columnIndex, _));
}
@Test("QuantumSimulator")
operation T26_QuantumStrategy () : Unit {
let N = 1000;
let wins = RunTrials(N, QuantumRunner(PlayQuantumMagicSquare_Reference, _, _));
Fact(wins == N, $"Alice and Bob's quantum strategy is not optimal: win rate {IntAsDouble(wins) / IntAsDouble(N)}");
}
// ------------------------------------------------------
@Test("QuantumSimulator")
operation T27_PlayQuantumMagicSquare () : Unit {
let N = 1000;
let wins = RunTrials(N, QuantumRunner(PlayQuantumMagicSquare, _, _));
Message($"Win rate {IntAsDouble(wins) / IntAsDouble(N)}");
Fact(wins == N, $"Alice and Bob's quantum strategy is not optimal: win rate {IntAsDouble(wins) / IntAsDouble(N)}");
}
// ------------------------------------------------------
function DrawMagicSquare (alice : Int[], row : Int, bob : Int[], column : Int) : Unit {
for i in 0..2 {
mutable line = ["", size = 3];
for j in 0..2 {
if (i == row and j == column and alice[j] != bob[i]) {
set line w/= j <- "±";
} elif (i == row) {
set line w/= j <- alice[j] == 1 ? "+" | (alice[j] == -1 ? "-" | "?");
} elif (j == column) {
set line w/= j <- bob[i] == 1 ? "+" | (bob[i] == -1 ? "-" | "?");
} else {
set line w/= j <- " ";
}
}
Message("-------");
Message($"|{line[0]}|{line[1]}|{line[2]}|");
}
Message("-------");
}
}