QuantumKatas/Superposition/ReferenceImplementation.qs

498 строки
18 KiB
Plaintext

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
//////////////////////////////////////////////////////////////////////
// This file contains reference solutions to all tasks.
// The tasks themselves can be found in Tasks.qs file.
// We recommend that you try to solve the tasks yourself first,
// but feel free to look up the solution if you get stuck.
//////////////////////////////////////////////////////////////////////
namespace Quantum.Kata.Superposition {
open Microsoft.Quantum.Primitive;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Extensions.Convert;
open Microsoft.Quantum.Extensions.Math;
// Task 1. Plus state
// Input: a qubit in |0⟩ state (stored in an array of length 1).
// Goal: create a |+⟩ state on this qubit (|+⟩ = (|0⟩ + |1⟩) / sqrt(2)).
operation PlusState_Reference (qs : Qubit[]) : Unit {
body (...) {
H(qs[0]);
}
adjoint invert;
}
// Task 2. Minus state
// Input: a qubit in |0⟩ state (stored in an array of length 1).
// Goal: create a |-⟩ state on this qubit (|-⟩ = (|0⟩ - |1⟩) / sqrt(2)).
operation MinusState_Reference (qs : Qubit[]) : Unit {
body (...) {
X(qs[0]);
H(qs[0]);
}
adjoint invert;
}
// Task 3. Unequal superposition
// Inputs:
// 1) a qubit in |0⟩ state (stored in an array of length 1).
// 2) angle alpha, in radians, represented as Double
// Goal: create a cos(alpha) * |0⟩ + sin(alpha) * |1⟩ state on this qubit.
operation UnequalSuperposition_Reference (qs : Qubit[], alpha : Double) : Unit {
body (...) {
// Hint: Experiment with rotation gates from Microsoft.Quantum.Primitive
Ry(2.0 * alpha, qs[0]);
}
adjoint invert;
}
// Task 4. Superposition of all basis vectors on two qubits
operation AllBasisVectors_TwoQubits_Reference (qs : Qubit[]) : Unit {
body (...) {
// Since a Hadamard gate will change |0⟩ into |+⟩ = (|0⟩ + |1⟩)/sqrt(2)
// And the desired state is just a tensor product |+⟩|+⟩, we can apply
// a Hadamard transformation to each qubit.
H(qs[0]);
H(qs[1]);
}
adjoint invert;
}
// Task 5. Superposition of basis vectors with phases
operation AllBasisVectorsWithPhases_TwoQubits_Reference (qs : Qubit[]) : Unit {
body (...) {
// Question:
// Is this state separable?
// Answer:
// Yes. It is. We can see that:
// ((|0⟩ - |1⟩) / sqrt(2)) ⊗ ((|0⟩ + i*|1⟩) / sqrt(2)) is equal to the desired
// state, so we can create it by doing operations on each qubit independently.
// We can see that the first qubit is in state |-⟩ and the second in state |i⟩,
// so the transformations that we need are:
// Qubit 0 is taken into |+⟩ and then z-rotated into |-⟩.
H(qs[0]);
Z(qs[0]);
// Qubit 1 is taken into |+⟩ and then z-rotated into |i⟩.
H(qs[1]);
S(qs[1]);
}
adjoint invert;
}
// Task 6. Bell state
// Input: two qubits in |00⟩ state (stored in an array of length 2).
// Goal: create a Bell state |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2) on these qubits.
operation BellState_Reference (qs : Qubit[]) : Unit {
body (...) {
H(qs[0]);
CNOT(qs[0], qs[1]);
}
adjoint invert;
}
// Task 7. All Bell states
// Inputs:
// 1) two qubits in |00⟩ state (stored in an array of length 2)
// 2) an integer index
// Goal: create one of the Bell states based on the value of index:
// 0: |Φ⁺⟩ = (|00⟩ + |11⟩) / sqrt(2)
// 1: |Φ⁻⟩ = (|00⟩ - |11⟩) / sqrt(2)
// 2: |Ψ⁺⟩ = (|01⟩ + |10⟩) / sqrt(2)
// 3: |Ψ⁻⟩ = (|01⟩ - |10⟩) / sqrt(2)
operation AllBellStates_Reference (qs : Qubit[], index : Int) : Unit {
body (...) {
H(qs[0]);
CNOT(qs[0], qs[1]);
// now we have |00⟩ + |11⟩ - modify it based on index arg
if (index % 2 == 1) {
// negative phase
Z(qs[1]);
}
if (index / 2 == 1) {
X(qs[1]);
}
}
adjoint invert;
}
// Task 8. Greenberger–Horne–Zeilinger state
// Input: N qubits in |0...0⟩ state.
// Goal: create a GHZ state (|0...0⟩ + |1...1⟩) / sqrt(2) on these qubits.
operation GHZ_State_Reference (qs : Qubit[]) : Unit {
body (...) {
H(qs[0]);
for (i in 1 .. Length(qs) - 1) {
CNOT(qs[0], qs[i]);
}
}
adjoint invert;
}
// Task 9. Superposition of all basis vectors
// Input: N qubits in |0...0⟩ state.
// Goal: create an equal superposition of all basis vectors from |0...0⟩ to |1...1⟩
// (i.e. state (|0...0⟩ + ... + |1...1⟩) / sqrt(2^N) ).
operation AllBasisVectorsSuperposition_Reference (qs : Qubit[]) : Unit {
body (...) {
for (i in 0 .. Length(qs) - 1) {
H(qs[i]);
}
}
adjoint invert;
}
// ------------------------------------------------------
// Task 10. |00⟩ + |01⟩ + |10⟩ state
// Input: 2 qubits in |00⟩ state.
// Goal: create the state (|00⟩ + |01⟩ + |10⟩) / sqrt(3) on these qubits.
operation ThreeStates_TwoQubits_Reference (qs : Qubit[]) : Unit {
body (...) {
// Follow Niel's answer at https://quantumcomputing.stackexchange.com/a/2313/
// Rotate first qubit to (sqrt(2) |0⟩ + |1⟩) / sqrt(3) (task 1.4 from BasicGates kata)
let theta = ArcSin(1.0 / Sqrt(3.0));
Ry(2.0 * theta, qs[0]);
// Split the state sqrt(2) |0⟩ ⊗ |0⟩ into |00⟩ + |01⟩
(ControlledOnInt(0, H))([qs[0]], qs[1]);
}
adjoint auto;
}
// Alternative solution, based on post-selection
operation ThreeStates_TwoQubits_Postselection (qs : Qubit[]) : Unit {
using (ancilla = Qubit()) {
repeat {
// Create |00⟩ + |01⟩ + |10⟩ + |11⟩ state
ApplyToEach(H, qs);
// Create (|00⟩ + |01⟩ + |10⟩) ⊗ |0⟩ + |11⟩ ⊗ |1⟩
Controlled X(qs, ancilla);
let res = MResetZ(ancilla);
}
until (res == Zero)
fixup {
ResetAll(qs);
}
}
}
// ------------------------------------------------------
// Task 11. Superposition of |0...0⟩ and given bit string
// Inputs:
// 1) N qubits in |0...0⟩ state
// 2) bit string represented as Bool[]
// Goal: create an equal superposition of |0...0⟩ and basis state given by the second bit string.
// Bit values false and true correspond to |0⟩ and |1⟩ states.
// You are guaranteed that the qubit array and the bit string have the same length.
// You are guaranteed that the first bit of the bit string is true.
// Example: for bit string = [true, false] the qubit state required is (|00⟩ + |10⟩) / sqrt(2).
operation ZeroAndBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[]) : Unit {
body (...) {
AssertIntEqual(Length(bits), Length(qs), "Arrays should have the same length");
AssertBoolEqual(bits[0], true, "First bit of the input bit string should be set to true");
// Hadamard first qubit
H(qs[0]);
// iterate through the bit string and CNOT to qubits corresponding to true bits
for (i in 1 .. Length(qs) - 1) {
if (bits[i]) {
CNOT(qs[0], qs[i]);
}
}
}
adjoint invert;
}
// ------------------------------------------------------
// Task 12. Superposition of two bit strings
// Inputs:
// 1) N qubits in |0...0⟩ state
// 2) two bit string represented as Bool[]s
// Goal: create an equal superposition of two basis states given by the bit strings.
// Bit values false and true correspond to |0⟩ and |1⟩ states.
// Example: for bit strings [false, true, false] and [false, false, true]
// the qubit state required is (|010⟩ + |001⟩) / sqrt(2).
// You are guaranteed that the two bit strings will be different.
// helper function for TwoBitstringSuperposition_Reference
function FindFirstDiff_Reference (bits1 : Bool[], bits2 : Bool[]) : Int {
mutable firstDiff = -1;
for (i in 0 .. Length(bits1) - 1) {
if (bits1[i] != bits2[i] and firstDiff == -1) {
set firstDiff = i;
}
}
return firstDiff;
}
operation TwoBitstringSuperposition_Reference (qs : Qubit[], bits1 : Bool[], bits2 : Bool[]) : Unit {
body (...) {
// find the index of the first bit at which the bit strings are different
let firstDiff = FindFirstDiff_Reference(bits1, bits2);
// 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 (bits1[i] == bits2[i]) {
// if two bits are the same apply X or nothing
if (bits1[i]) {
X(qs[i]);
}
} else {
// if two bits are different, set their difference using CNOT
if (i > firstDiff) {
CNOT(qs[firstDiff], qs[i]);
if (bits1[i] != bits1[firstDiff]) {
X(qs[i]);
}
}
}
}
}
adjoint invert;
}
// ------------------------------------------------------
// Task 13*. Superposition of four bit strings
// Inputs:
// 1) N qubits in |0...0⟩ state
// 2) four bit string represented as Bool[][] bits
// bits is an array of size 4 x N which describes the bit strings as follows:
// bits[i] describes the i-th bit string and has N elements;
// bit values false and true correspond to |0⟩ and |1⟩ states.
//
// Goal: create an equal superposition of the four basis states given by the bit strings.
operation FourBitstringSuperposition_Reference (qs : Qubit[], bits : Bool[][]) : Unit {
body (...) {
let N = Length(qs);
using (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]);
}
}
}
}
adjoint auto;
}
// ------------------------------------------------------
// Task 14. W state on 2ᵏ qubits
// Input: N = 2ᵏ qubits in |0...0⟩ state.
// Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits.
// W state is an equal superposition of all basis states on N qubits of Hamming weight 1.
// Example: for N = 4, W state is (|1000⟩ + |0100⟩ + |0010⟩ + |0001⟩) / 2.
operation WState_PowerOfTwo_Reference (qs : Qubit[]) : Unit {
body (...) {
let N = Length(qs);
if (N == 1) {
// base of recursion: |1⟩
X(qs[0]);
} else {
let K = N / 2;
// create W state on the first K qubits
WState_PowerOfTwo_Reference(qs[0 .. K - 1]);
// the next K qubits are in |0...0⟩ state
// allocate ancilla in |+⟩ state
using (anc = Qubit[1]) {
H(anc[0]);
for (i in 0 .. K - 1) {
Controlled SWAP(anc, (qs[i], qs[i + K]));
}
for (i in K .. N - 1) {
CNOT(qs[i], anc[0]);
}
}
}
}
adjoint invert;
}
// ------------------------------------------------------
// Task 15**. W state on arbitrary number of qubits
// Input: N qubits in |0...0⟩ state (N is not necessarily a power of 2).
// Goal: create a W state (https://en.wikipedia.org/wiki/W_state) on these qubits.
// W state is an equal superposition of all basis states on N qubits of Hamming weight 1.
// Example: for N = 3, W state is (|100⟩ + |010⟩ + |001⟩) / sqrt(3).
// general solution based on rotations and recursive application of controlled generation routine
operation WState_Arbitrary_Reference (qs : Qubit[]) : Unit {
body (...) {
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(ToDouble(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]);
}
}
adjoint invert;
controlled distribute;
controlled adjoint distribute;
}
// Iterative solution (equivalent to the WState_Arbitrary_Reference, but with the recursion unrolled)
// Circuit for N=4: https://algassert.com/quirk#circuit={%22cols%22:[[1,1,1,%22~95cq%22],[1,1,%22~erlf%22,%22%E2%97%A6%22],[1,%22~809j%22,%22%E2%97%A6%22,%22%E2%97%A6%22],[%22X%22,%22%E2%97%A6%22,%22%E2%97%A6%22,%22%E2%97%A6%22]],%22gates%22:[{%22id%22:%22~809j%22,%22name%22:%22FS_2%22,%22matrix%22:%22{{%E2%88%9A%C2%BD,-%E2%88%9A%C2%BD},{%E2%88%9A%C2%BD,%E2%88%9A%C2%BD}}%22},{%22id%22:%22~erlf%22,%22name%22:%22FS_3%22,%22matrix%22:%22{{%E2%88%9A%E2%85%94,-%E2%88%9A%E2%85%93},{%E2%88%9A%E2%85%93,%E2%88%9A%E2%85%94}}%22},{%22id%22:%22~95cq%22,%22name%22:%22FS_4%22,%22matrix%22:%22{{%E2%88%9A%C2%BE,-%C2%BD},{%C2%BD,%E2%88%9A%C2%BE}}%22}]}
operation WState_Arbitrary_Iterative (qs : Qubit[]) : Unit {
let N = Length(qs);
FractionSuperposition(N, qs[0]);
for (i in 1 .. N - 1) {
(ControlledOnInt(0, FractionSuperposition))(qs[0..i-1], (N-i, qs[i]));
}
}
// Given a qubit in |0⟩ state and a denominator N,
// transform the qubit to state sqrt((N-1) / N) |0⟩ + sqrt(1/N) |1⟩.
operation FractionSuperposition(denominator : Int, q : Qubit) : Unit {
body (...) {
if (denominator == 1) {
X(q);
} else {
// represent the target state as cos(theta) * |0⟩ + sin(theta) * |1⟩, as in task 1.3
let denom = ToDouble(denominator);
let num = denom - 1.0;
let theta = ArcCos(Sqrt(num / denom));
Ry(2.0 * theta, q);
}
}
adjoint invert;
controlled distribute;
controlled adjoint distribute;
}
// solution based on generation for 2ᵏ and post-selection using measurements
operation WState_Arbitrary_Postselect (qs : Qubit[]) : Unit {
let N = Length(qs);
if (N == 1) {
// base case of recursion: |1⟩
X(qs[0]);
} else {
// find the smallest power of 2 which is greater than or equal to N
// as a hack, we know we're not doing it on more than 64 qubits
mutable P = 1;
for (i in 1 .. 6) {
if (P < N) {
set P = P * 2;
}
}
if (P == N) {
// prepare as a power of 2 (previous task)
WState_PowerOfTwo_Reference(qs);
} else {
// allocate extra qubits
using (anc = Qubit[P - N]) {
let all_qubits = qs + anc;
repeat {
// prepare state W_P on original + ancilla qubits
WState_PowerOfTwo_Reference(all_qubits);
// measure ancilla qubits; if all of the results are Zero, we get the right state on main qubits
mutable allZeros = true;
for (i in 0 .. (P - N) - 1) {
set allZeros = allZeros and IsResultZero(M(anc[i]));
}
}
until (allZeros)
fixup {
ResetAll(anc);
}
}
}
}
}
}