Skip to content

Commit

Permalink
Start migration of Phase Estimation kata (microsoft#1824)
Browse files Browse the repository at this point in the history
* First two exercises match the last two exercises from classic Linear
Algebra kata, and follow a similar approach for migrating it to Q#,
replacing Python code with a hardcoded exercise
* Third exercise is 1.3 from classic PhaseEstimation kata, rewritten to
work with Boolean checks rather than assertions.

Part 2 will include exercises 2.1 and 1.4 from classic PhaseEstimation
kata and new (to katas) QPE algorithm theory and demo of its success
probability

---------

Co-authored-by: Scott Carda <[email protected]>
  • Loading branch information
tcNickolas and ScottCarda-MS authored Aug 12, 2024
1 parent d64d3a8 commit a8b00c8
Show file tree
Hide file tree
Showing 16 changed files with 337 additions and 0 deletions.
9 changes: 9 additions & 0 deletions katas/content/phase_estimation/eigenvalues_s/Placeholder.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Kata {
open Microsoft.Quantum.Math;

function EigenvaluesS() : Complex[] {
// Replace the return value with correct answer.
return [Complex(0.0, 0.0),
Complex(0.0, 0.0)];
}
}
8 changes: 8 additions & 0 deletions katas/content/phase_estimation/eigenvalues_s/Solution.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Kata {
open Microsoft.Quantum.Math;

function EigenvaluesS() : Complex[] {
return [Complex(1.0, 0.0),
Complex(0.0, 1.0)];
}
}
26 changes: 26 additions & 0 deletions katas/content/phase_estimation/eigenvalues_s/Verification.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Kata.Verification {
open Microsoft.Quantum.Math;

function ComplexEqual(x : Complex, y : Complex) : Bool {
// Tests two complex numbers for equality.
AbsD(x::Real - y::Real) <= 0.001 and AbsD(x::Imag - y::Imag) <= 0.001
}


@EntryPoint()
operation CheckSolution() : Bool {
let actual = Kata.EigenvaluesS();
let expected = [Complex(1.0, 0.0), Complex(0.0, 1.0)];
if Length(actual) != 2 {
Message("The array of eigenvalues should have exactly two elements.");
return false;
}
if ComplexEqual(actual[0], expected[0]) and ComplexEqual(actual[1], expected[1]) or
ComplexEqual(actual[0], expected[1]) and ComplexEqual(actual[1], expected[0]) {
Message("Correct!");
return true;
}
Message("Incorrect value for one of the eigenvalues.");
return false;
}
}
9 changes: 9 additions & 0 deletions katas/content/phase_estimation/eigenvalues_s/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
**Input:** None.

**Output:** Return an array of two eigenvalues of the $S$ gate.

$$S = \begin{bmatrix} 1 & 0 \\ 0 & i \end{bmatrix}$$

Sort the eigenvalues in decreasing order of their real parts.

> In this task, the eigenvalues are represented as complex numbers of Q# `Complex` type.
10 changes: 10 additions & 0 deletions katas/content/phase_estimation/eigenvalues_s/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Since the $S$ gate is diagonal, it's easy to realize that its eigenvectors are the basis vectors $\ket{0}$ and $\ket{1}$.

To find the corresponding eigenvalues, you need to solve the two equations:
- $S\ket{0} = \lambda \ket{0}$, which gives you $\lambda = 1$.
- $S\ket{1} = \lambda \ket{1}$, which gives you $\lambda = i$.

@[solution]({
"id": "phase_estimation__eigenvalues_s_solution",
"codePath": "Solution.qs"
})
9 changes: 9 additions & 0 deletions katas/content/phase_estimation/eigenvectors_x/Placeholder.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Kata {
open Microsoft.Quantum.Math;

function EigenvectorsX() : Double[][] {
// Replace the return value with correct answer.
return [[0.0, 0.0],
[0.0, 0.0]];
}
}
8 changes: 8 additions & 0 deletions katas/content/phase_estimation/eigenvectors_x/Solution.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Kata {
open Microsoft.Quantum.Math;

function EigenvectorsX() : Double[][] {
return [[1.0, 1.0],
[1.0, -1.0]];
}
}
32 changes: 32 additions & 0 deletions katas/content/phase_estimation/eigenvectors_x/Verification.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Kata.Verification {
open Microsoft.Quantum.Math;

@EntryPoint()
operation CheckSolution() : Bool {
let actual = Kata.EigenvectorsX();
if Length(actual) != 2 {
Message("The array of eigenvectors should have exactly two elements.");
return false;
}
for i in 0 .. 1 {
if Length(actual[i]) != 2 {
Message("Each eigenvector should have exactly two elements.");
return false;
}
if AbsD(actual[i][0]) + AbsD(actual[i][1]) < 1E-9 {
Message("Each eigenvector should be non-zero.");
return false;
}
}

// One eigenvector has to have equal components, the other one - opposite ones
if AbsD(actual[0][0] - actual[0][1]) < 1e-9 and AbsD(actual[1][0] + actual[1][1]) < 1e-9 or
AbsD(actual[0][0] + actual[0][1]) < 1e-9 and AbsD(actual[1][0] - actual[1][1]) < 1e-9 {
Message("Correct!");
return true;
}

Message("Incorrect value for one of the eigenvectors.");
return false;
}
}
10 changes: 10 additions & 0 deletions katas/content/phase_estimation/eigenvectors_x/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
**Input:** None.

**Output:** Return an array of two eigenvectors of the $X$ gate that correspond to different eigenvalues.

$$X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}$$

The eigenvectors don't have to be normalized, that is, they don't have to describe valid quantum states.
Both eigenvectors have to be non-zero.

> In this task, the eigenvectors are represented as arrays of Q# `Double` type of length $2$. Your return should be an array of two such arrays.
12 changes: 12 additions & 0 deletions katas/content/phase_estimation/eigenvectors_x/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Since the $X$ gate is self-adjoint, you know that its eigenvalues can only be $+1$ and $-1$.
Now, you need to find the eigenvectors that correspond to these eigenvalues.
To do this, you need to solve the two equations:
- $X \begin{bmatrix} v_0 \\ v_1 \end{bmatrix} = \begin{bmatrix} v_0 \\ v_1 \end{bmatrix}$, which gives you $v_0 = v_1$.
- $X \begin{bmatrix} v_0 \\ v_1 \end{bmatrix} = -\begin{bmatrix} v_0 \\ v_1 \end{bmatrix}$, which gives you $v_0 = -v_1$.

One of the eigenvectors should consist of two equal elements, and the other - of two elements with equal absolute values but opposite signs.

@[solution]({
"id": "phase_estimation__eigenvectors_x_solution",
"codePath": "Solution.qs"
})
123 changes: 123 additions & 0 deletions katas/content/phase_estimation/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Phase Estimation

@[section]({
"id": "phase_estimation__overview",
"title": "Overview"
})

This kata introduces you to the phase estimation algorithm - an important building block in more advanced quantum algorithms such as integer factoring.

**This kata covers the following topics:**

- The definition of eigenvalues and eigenvectors
- The phase estimation problem
- The quantum phase estimation algorithm based on quantum Fourier transform

**What you should know to start working on this kata:**

- Basic quantum gates and measurements.
- Quantum Fourier transform.

@[section]({
"id": "phase_estimation__eigen",
"title": "Eigenvectors, Eigenvalues, and Eigenphases"
})

An *eigenvector* of a matrix $A$ is a non-zero vector that, when multiplied by that matrix, changes by a scalar factor:

$$A \ket{v} = \lambda \ket{v}$$

The number $\lambda$ is called an *eigenvalue* that corresponds to this eigenvector. In general, eigenvalues of matrices can be complex numbers.

Recall that all quantum gates are unitary matrices, for which their inverse equals their adjoint ($U^{-1} = U^\dagger$). This means that the eigenvalues of their eigenvectors have the property that their modulus equals $1$:

$$|\lambda| = 1$$

Thus, they can be written in the following form:
$$\lambda = e^{i\theta}$$

The value $\theta$ is called an *eigenphase* that corresponds to this eigenvector.

> How can you prove that the modulus of an eigenvalue of a unitary matrix equals $1$?
>
> On one hand, by definition of an eigenvalue,
> $$U \ket{v} = \lambda \ket{v}$$
> $$|U \ket{v}| = |\lambda| \cdot |\ket{v}|$$
> On the other hand, using the properties of the unitary matrix, you can write the following equation:
> $$|U \ket{v}|^2 = \bra{v} U^\dagger U \ket{v} = \bra{v} U^{-1} U \ket{v} = \bra{v} I \ket{v} = \braket{v|v} = |\ket{v}|^2$$
>
> From these two equations, you get the following equality:
> $$(|\lambda| \cdot |\ket{v}|)^2 = |\ket{v}|^2$$
> And then, finally:
> $$|\lambda| = 1$$
If the quantum gate is self-adjoint, that is, its matrix equals its inverse $U^{-1} = U$, the eigenvalues of this matrix can only be $+1$ and $-1$, with eigenphases $0$ and $\pi$, respectively.

> You can prove this in a similar manner, using the defintion of an eigenvalue:
> $$U^2 \ket{v} = U(U \ket{v}) = U(\lambda \ket{v}) = \lambda U \ket{v} = \lambda^2 \ket{v}$$
> At the same time,
> $$U^2 \ket{v} = UU \ket{v} = U^{-1}U \ket{v} = I \ket{v} = \ket{v}$$
> So you can conclude that $\lambda^2 = 1$.
For example, the $Z$ gate has two eigenvctors:
- $\ket{0}$, with eigenvalue $1$
- $\ket{1}$, with eigenvalue $-1$


@[exercise]({
"id": "phase_estimation__eigenvalues_s",
"title": "Find Eigenvalues of the S Gate",
"path": "./eigenvalues_s/"
})

@[exercise]({
"id": "phase_estimation__eigenvectors_x",
"title": "Find Eigenvectors of the X Gate",
"path": "./eigenvectors_x/"
})

@[exercise]({
"id": "phase_estimation__state_eigenvector",
"title": "Is Given State an Eigenvector of the Gate?",
"path": "./state_eigenvector/"
})


@[section]({
"id": "phase_estimation__problem",
"title": "Phase Estimation Problem"
})

The phase estimation problem is formulated as follows.

You are given a unitary operator $U$ and its eigenvector $\ket{\psi}$. The eigenvector is given as a unitary operator $P$ that, when applied to $\ket{0}$, results in the state $\ket{\psi}$.

Your goal is to find the eigenvalue $\lambda$ associated with this eigenvector, or, in a more common formulation, the corresponding eigenphase $\theta$:

$$U\ket{\psi} = e^{2 \pi i \theta} \ket{\psi}, \theta = ?$$

The value of $\theta$ is defined to be between $0$ and $1$, since any value outside of this range has an equivalent value within it. Instead of representing $\theta$ as a decimal, sometimes it is represented as a binary fraction with $n$ digits:

$$\theta = 0.\theta_1 \theta_2... \theta_n = \frac{\theta_1}{2^1}+ \frac{\theta_2}{2^2}+...\frac{\theta_n}{2^n}$$

Let's consider a simplified variant of the phase estimation problem, in which you are guaranteed that the phase $\theta$ has exactly one binary digit, that is, it's either $0$ or $\frac12$.

- exercise: solve for one bit eigenphase


@[section]({
"id": "phase_estimation__qpe",
"title": "Quantum Phase Estimation Algorithm"
})

- theory
- exercise: task 1.4 to implement QPE
- demo of end-to-end probabilistic behavior in case of lower precision (use R1 gate)


@[section]({
"id": "phase_estimation__conclusion",
"title": "Conclusion"
})

Congratulations! In this kata you learned about the phase estimation problem and its solution using the quantum phase estimation algorithm.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Kata {
operation IsEigenvector(U : Qubit => Unit, P : Qubit => Unit is Adj) : Bool {
// Implement your solution here...

return false;
}
}
12 changes: 12 additions & 0 deletions katas/content/phase_estimation/state_eigenvector/Solution.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Kata {
import Microsoft.Quantum.Diagnostics.CheckZero;
operation IsEigenvector(U : Qubit => Unit, P : Qubit => Unit is Adj) : Bool {
use q = Qubit();
P(q);
U(q);
Adjoint P(q);
let ret = CheckZero(q);
Reset(q);
return ret;
}
}
36 changes: 36 additions & 0 deletions katas/content/phase_estimation/state_eigenvector/Verification.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Kata.Verification {
open Microsoft.Quantum.Unstable.StatePreparation;

@EntryPoint()
operation CheckSolution() : Bool {
let eigenvectors = [
(Z, I, "Z, |0⟩"),
(Z, X, "Z, |1⟩"),
(S, I, "S, |0⟩"),
(S, X, "S, |1⟩"),
(X, H, "X, |+⟩"),
(X, q => PreparePureStateD([1., -1.], [q]), "X, |-⟩")];
for (U, P, msg) in eigenvectors {
if not Kata.IsEigenvector(U, P) {
Message($"Incorrect for (U, P) = ({msg}): expected true");
return false;
}
}

let notEigenvectors = [
(Z, H, "Z, |+⟩"),
(X, X, "X, |1⟩"),
(X, Z, "X, |0⟩"),
(Y, H, "Y, |+⟩"),
(Y, X, "Y, |1⟩")];
for (U, P, msg) in notEigenvectors {
if Kata.IsEigenvector(U, P) {
Message($"Incorrect for (U, |ψ⟩) = ({msg}): expected false");
return false;
}
}

Message("Correct!");
return true;
}
}
14 changes: 14 additions & 0 deletions katas/content/phase_estimation/state_eigenvector/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
**Inputs:**

1. A single-qubit unitary $U$.
2. A single-qubit state $\ket{\psi}$, given as a unitary $P$ that prepares it from the $\ket{0}$ state. In other words, the result of applying the unitary $P$ to the state $\ket{0}$ is the $\ket{\psi}$ state:
$$P\ket{0} = \ket{\psi}$$

**Output:** Return true if the given state is an eigenstate of the given unitary, and false otherwise.

<details>
<summary><b>Need a hint?</b></summary>

The library operation <code>CheckZero</code> allows you to check whether the state of the given qubit is $\ket{0}$.

</details>
12 changes: 12 additions & 0 deletions katas/content/phase_estimation/state_eigenvector/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
A quantum state is an eigenstate of a quantum gate if applying that gate to that state doesn't change it, other than multiply it by a global phase. This means that your solution should probably start by preparing the state $\ket{\psi}$ and applying the unitary $U$ to it. How can you check that the state after that is still $\ket{\psi}$ (up to a global phase)?

Let's consider what happens if you apply the adjoint of $P$ to the state $\ket{\psi}$:

$$P^\dagger \ket{\psi} = P^\dagger P\ket{0} = I\ket{0} = \ket{0}$$

You can use this to finish the solution: apply `Adjoint P` to the state you obtained after applying $U$ and check whether the result is $\ket{0}$ using the library operation `CheckZero`.

@[solution]({
"id": "phase_estimation__state_eigenvector_solution",
"codePath": "Solution.qs"
})

0 comments on commit a8b00c8

Please sign in to comment.