Skip to content

Commit

Permalink
Clean up QRNG kata (#1190)
Browse files Browse the repository at this point in the history
Also adds task 1.5 to Superposition kata and updates docs on
`@[exercise]` macro
  • Loading branch information
tcNickolas authored Feb 22, 2024
1 parent 7b56bdf commit 8369a7d
Show file tree
Hide file tree
Showing 32 changed files with 191 additions and 147 deletions.
12 changes: 8 additions & 4 deletions katas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ Macros are meant to insert interactive elements into the content defined through
The following macros are available for katas composition:
- @[exercise]: Used to create Q# code exercises that we can be automatically verified.
- id: Unique identifier for the exercise.
- descriptionPath: Path to a markdown file that contains the description of the exercise.
- placeholderSourcePath: Path to a Q# file. It contains Q# code which is used as template code that helps the user to start implementing a solution.
- codePaths: Q# file paths. This code is not shown to the user but is built with the user code. Verification code in one of these files. The @EntryPoint operation is called to check the solution (eventually, for the convention is to call Kata.Verification.CheckSolution).
- solutionPath: Path to a markdown file that contains a “reference solution” – text with at least one code solution that solves the exercise. It can have more than one code solution.
- title: Title that will be displayed for the exercise.
- path: Path to a folder that contains the description of the exercise. This folder should contain the following files:
- `index.md`: the Markdown description of the exercise.
- `Placeholder.qs`: the Q# code that is given to the learner to start with.
- `Verification.qs`: the Q# code that checks whether the learner's solution is correct.
- `solution.md`: the Markdown description of the solution(s) to the exercise.
- `Solution.qs`: the Q# code that contains a "reference solution" described in `solution.md`.
- qsDependencies: Q# file paths used in addition to `Verification.qs`. This code is not shown to the learner but is used to build the learner's code. The @EntryPoint operation is called to check the solution (eventually, for the convention is to call Kata.Verification.CheckSolution).
- @[question]: Used to create theoretical/analytical questions that are not automatically verified.
- id: Unique identifier for the question.
- descriptionPath: Path a markdown file that contains the description of the question.
Expand Down
4 changes: 3 additions & 1 deletion katas/content/random_numbers/Common.qs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ namespace Kata.Verification {
repeat {
set result = verifier();
set attemptResults += [result];
} until (result == 0 or Length(attemptResults) >= maxAttempts);
// If the result is 0x1, the generator returned an invalid result.
// That's different from "not random enough" verdicts, so we break right away.
} until result == 0 or result == 0x1 or Length(attemptResults) >= maxAttempts;

attemptResults
}
Expand Down
18 changes: 7 additions & 11 deletions katas/content/random_numbers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

True random number generation is a notoriously difficult problem. Many "random" generators today are actually pseudo-random, using a starting seed to spawn seemingly-random numbers that are actually a repeatable function of that seed. Most true random number generators are based on measurements of some natural phenomenon, such as atmospheric noise or atomic decay. You can read more about it <a href="https://en.wikipedia.org/wiki/Random_number_generation" target="_blank">here</a>.

Quantum random number generators (QRNGs) are truly random. The quantum algorithm for random number generation is one of the simplest applications of quantum computing principles, requiring very few qubits to run.
Quantum random number generators (QRNGs) are truly random. Of course, this only applies to the case when they run on a real quantum device, relying on the randomness of the quantum state collapse during measurement to produce the random numbers. When QRNGs run on a simulator, the source of randomness is the same as for other classical programs, so the generated numbers are pseudo-random.

The quantum algorithm for random number generation is one of the simplest applications of quantum computing principles, requiring very few qubits to run.

**This kata covers the following topics:**

Expand All @@ -19,7 +21,7 @@ Quantum random number generators (QRNGs) are truly random. The quantum algorithm

@[section]({"id": "random_numbers__introduction", "title": "Introduction"})

Recall from the Qubit kata that a qubit state $|\psi\rangle$ is defined via the basis states $|0\rangle$ and $|1\rangle$ as $|\psi\rangle = \begin{bmatrix} \alpha \\ \beta \end{bmatrix} = \alpha|0\rangle + \beta|1\rangle$, where $|\alpha|^2 + |\beta|^2 = 1$
Recall from the Qubit kata that a qubit state $|\psi\rangle$ is defined via the basis states $|0\rangle$ and $|1\rangle$ as $|\psi\rangle = \begin{bmatrix} \alpha \\\ \beta \end{bmatrix} = \alpha|0\rangle + \beta|1\rangle$, where $|\alpha|^2 + |\beta|^2 = 1$.

We call $\alpha$ and $\beta$ the probability amplitudes of states $|0\rangle$ and $|1\rangle$, respectively. When $|\psi\rangle$ is measured in the $\\{|0\rangle, |1\rangle\\}$ basis (the computational basis), the probabilities of the outcomes are defined based on the state amplitudes: there is a $|\alpha|^2$ probability that the measurement result will be $0$, and a $|\beta|^2$ probability that the measurement result will be $1$.

Expand Down Expand Up @@ -84,12 +86,6 @@ This knowledge is sufficient to implement a simple random number generator!
Congratulations! In this kata you have created a random number generator. Here are a few key concepts to keep in mind:

- This code will generate truly random numbers when executed on a true quantum computer. Random numbers obtained when executing on a simulator are only as good as the source of randomness used by the simulator.
- You can generate a random bit by applying a Hadamard gate to a state $\ket{0}$, and then measuring the resulting qubit in the computational basis.
- The Q# <a href="https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.math.bitsizei" target="_blank">BitSizeI function</a> returns the number of bits needed to write an integer in binary.

**Next Steps**

We hope you enjoyed this kata on quantum random number generation! If you're looking to learn more about quantum computing and Q#, here are some suggestions:

- To learn about superposition, interference and entanglement by using Q#, you can check the <a href="https://learn.microsoft.com/en-us/training/modules/qsharp-explore-key-concepts-quantum-computing/" target="_blank">Microsoft Learn module "Explore the key concepts of quantum computing by using Q#"</a>.
- For another look at quantum random number generation, you can check out the <a href="https://docs.microsoft.com/learn/modules/qsharp-create-first-quantum-development-kit/1-introduction" target="_blank">Microsoft Learn module "Create your first Q# program by using the Quantum Development Kit"</a>.
- You can generate a random bit by preparing a qubit in superposition and then measuring it in the computational basis.
The amplitudes of the basis states will define the probability distribution of the generated bits.
- The Q# library function `BitSizeI` returns the number of bits in the binary representation of an integer.
8 changes: 4 additions & 4 deletions katas/content/random_numbers/random_bit/Solution.qs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace Kata {
open Microsoft.Quantum.Measurement;
operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
// Convert the qubit state to |+>.
H(q);

// Measuring the qubit and reset.
let result = M(q);
Reset(q);
// Measure the qubit and reset it to |0>.
let result = MResetZ(q);

// Return integer value of result.
if result == One {
Expand Down
3 changes: 1 addition & 2 deletions katas/content/random_numbers/random_bit/Verification.qs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
namespace Kata.Verification {
@EntryPoint()
operation CheckSolution() : Bool {
Message("Testing one random bit generation...");
let randomnessVerifier = () => CheckUniformDistribution(Kata.RandomBit, 0, 1, 1000);
let isCorrect = IsSufficientlyRandom(randomnessVerifier);
if isCorrect {
Message("All tests passed.");
Message("Correct!");
}
isCorrect
}
Expand Down
4 changes: 2 additions & 2 deletions katas/content/random_numbers/random_bit/solution.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
The state of single qubit can be represented as a two-dimensional column vector $\begin{bmatrix} \alpha\\ \beta \end{bmatrix}$, where $\alpha$ and $\beta$ are complex numbers that satisfy $|\alpha|^2 + |\beta|^2 = 1$. When we measure the qubit, we get either 0 with probability $|\alpha|^2$ or 1 with probability $|\beta|^2$. Essentially we can control probablity of measurement outcome by setting the right amplitudes of basis states.
The state of single qubit can be represented as a two-dimensional column vector $\begin{bmatrix} \alpha \\\ \beta \end{bmatrix}$, where $\alpha$ and $\beta$ are complex numbers that satisfy $|\alpha|^2 + |\beta|^2 = 1$. When we measure the qubit, we get either 0 with probability $|\alpha|^2$ or 1 with probability $|\beta|^2$. Essentially we can control probablity of measurement outcome by setting the right amplitudes of basis states.

When we allocate the qubit in Q#, amplitudes $\alpha$ and $\beta$ are 1 and 0, respectively. Now our goal is set equal amplitudes for $\alpha$ and $\beta$ for absolute randomness. We can achieve that by simply applying Hadamard gate to the initial state $|0\rangle$:

Expand All @@ -15,7 +15,7 @@ $$

Now, both 0 and 1 measurement outcomes occur with equal probablity of $|\frac{1}{\sqrt{2}}|^2 = \frac{1}{2}$.

> Note: Since probability is the square of the absolute value of amplitude, we will get the same randomness by applying a Hadamard gate on base state $|1\rangle$. Try it out as an exercise!
> Since the outcome probability is the square of the absolute value of amplitude, we will get the same distribution of outcomes by measuring the $|-\rangle$ state, which we can prepare by applying the Hadamard gate to the basis state $|1\rangle$. Try it out to achieve the stretch goal!
@[solution]({
"id": "random_numbers__random_bit_solution",
Expand Down
17 changes: 3 additions & 14 deletions katas/content/random_numbers/random_n_bits/Placeholder.qs
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
namespace Kata {
open Microsoft.Quantum.Measurement;
operation RandomNBits(N : Int) : Int {
// Implement your solution here...

return -1;
}

// You can use this operation to implement your solution.
// You can use the operation defined in the first exercise to implement your solution.
operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
H(q);

// Measuring the qubit and reset.
let result = M(q);
Reset(q);

// Return integer value of result.
if result == One {
return 1;
}
return 0;
return MResetZ(q) == Zero ? 0 | 1;
}
}
18 changes: 4 additions & 14 deletions katas/content/random_numbers/random_n_bits/Solution.qs
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
namespace Kata {
open Microsoft.Quantum.Measurement;
operation RandomNBits(N : Int) : Int {
mutable result = 0;
for i in 0..(N - 1) {
for i in 0 .. N - 1 {
set result = result * 2 + RandomBit();
}
return result;
}

// You can use the operation defined in the first exercise to implement your solution.
operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
H(q);

// Measuring the qubit and reset.
let result = M(q);
Reset(q);

// Return integer value of result.
if result == One {
return 1;
}
return 0;
return MResetZ(q) == Zero ? 0 | 1;
}
}
2 changes: 1 addition & 1 deletion katas/content/random_numbers/random_n_bits/Verification.qs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Kata.Verification {
}
Message($"Test passed for N = {N}");
}
Message("All tests passed.");
Message("Correct!");
true
}

Expand Down
1 change: 0 additions & 1 deletion katas/content/random_numbers/random_n_bits/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Let's take it a step further and generate an $N$-bit number.
>
> * <a href="https://docs.microsoft.com/azure/quantum/user-guide/language/statements/iterations" target="_blank">for loops</a>
> * <a href="https://docs.microsoft.com/azure/quantum/user-guide/language/typesystem/immutability" target="_blank">mutable variables</a>
> * <a href="https://docs.microsoft.com/qsharp/api/qsharp/microsoft.quantum.math.powi" target="_blank">exponents</a>
<details>
<summary><b>Need a hint?</b></summary>
Expand Down
4 changes: 2 additions & 2 deletions katas/content/random_numbers/random_n_bits/solution.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Let's reuse the `RandomBit` operation from the "Generate A Single Random Bit" exercise again.
We'll generate N random bits by calling `RandomBit` operation N times, and treat the result as a binary notation to convert it into an integer.
Since the maximum value of the number written with N bits is $2^N - 1$, we don't need to do any extra checks to ensure that the result is within the given range.
We'll generate $N$ random bits by calling `RandomBit` operation $N$ times, and treat the result as a binary notation of the integer we're looking for.
Since the maximum value of the number written with $N$ bits is $2^N - 1$, we don't need to do any extra checks to ensure that the result is within the given range.

@[solution]({
"id": "random_numbers__random_n_bits_solution",
Expand Down
22 changes: 10 additions & 12 deletions katas/content/random_numbers/random_number/Placeholder.qs
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
namespace Kata {
open Microsoft.Quantum.Measurement;

operation RandomNumberInRange(min : Int, max : Int) : Int {
// Implement your solution here...

return -1;
}

// You can use this operation to implement your solution.
// You can use the operations defined in the earlier exercises to implement your solution.
operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
H(q);
return MResetZ(q) == Zero ? 0 | 1;
}

// Measuring the qubit and reset.
let result = M(q);
Reset(q);

// Return integer value of result.
if result == One {
return 1;
operation RandomNBits(N : Int) : Int {
mutable result = 0;
for i in 0 .. N - 1 {
set result = result * 2 + RandomBit();
}
return 0;
return result;
}
}
17 changes: 3 additions & 14 deletions katas/content/random_numbers/random_number/Solution.qs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Kata {
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;

operation RandomNumberInRange(min : Int, max : Int) : Int {
let nBits = BitSizeI(max - min);
Expand All @@ -12,27 +13,15 @@ namespace Kata {

operation RandomNBits(N : Int) : Int {
mutable result = 0;
for i in 0..(N - 1) {
for i in 0 .. N - 1 {
set result = result * 2 + RandomBit();
}
return result;
}

operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
H(q);

// Measuring the qubit and reset.
let result = M(q);
Reset(q);

// Return integer value of result.
if result == One {
return 1;
}
return 0;
return MResetZ(q) == Zero ? 0 | 1;
}
}
4 changes: 1 addition & 3 deletions katas/content/random_numbers/random_number/Verification.qs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ namespace Kata.Verification {
if not isCorrect {
return false;
}

Message($"Test passed for min = {min} and max = {max}");
}
Message("All tests passed.");
Message("Correct!");
true
}
}
4 changes: 1 addition & 3 deletions katas/content/random_numbers/random_number/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,4 @@ Two integers $min$ and $max$ ($0 \leq min \leq max \leq 2^{10}-1$).

**Goal:** Generate a random number in the range $[min, max]$ with an equal probability of getting each of the numbers in this range.

> Useful Q# documentation:
> * <a href="https://docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum.math.bitsizei" target="_blank">`BitSizeI` function</a>
> Q# namespace `Microsoft.Quantum.Math` includes useful function `BitSizeI` that calculates the number of bits in the binary representation of the given number.
4 changes: 2 additions & 2 deletions katas/content/random_numbers/random_number/solution.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
We can reuse the `RandomBit` operation from the "Generate A Single Random Bit" exercise.
We can reuse the `RandomBit` and `RandomNBits` operations from earlier exercises.

We'll generate an $N$-bit random number by calling the `RandomNBits` operation, where N is the bitsize of $max - min$. We can repeat this process until the result is less than or equal than $max - min$, and return that number plus $min$.
We'll generate an $N$-bit random number by calling the `RandomNBits` operation, where $N$ is the bitsize of $max - min$. We can repeat this process until the result is less than or equal than $max - min$, and return that number plus $min$.

@[solution]({
"id": "random_numbers__random_number_solution",
Expand Down
17 changes: 3 additions & 14 deletions katas/content/random_numbers/random_two_bits/Placeholder.qs
Original file line number Diff line number Diff line change
@@ -1,26 +1,15 @@
namespace Kata {
open Microsoft.Quantum.Measurement;
operation RandomTwoBits() : Int {
// Implement your solution here...

return -1;
}

// You can use this operation to implement your solution.
// You can use the operation defined in the previous exercise to implement your solution.
operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
H(q);

// Measuring the qubit and reset.
let result = M(q);
Reset(q);

// Return integer value of result.
if result == One {
return 1;
}
return 0;
return MResetZ(q) == Zero ? 0 | 1;
}
}
15 changes: 2 additions & 13 deletions katas/content/random_numbers/random_two_bits/Solution.qs
Original file line number Diff line number Diff line change
@@ -1,23 +1,12 @@
namespace Kata {
open Microsoft.Quantum.Measurement;
operation RandomTwoBits() : Int {
return 2 * RandomBit() + RandomBit();
}

operation RandomBit() : Int {
// Allocate single qubit.
use q = Qubit();

// Set qubit in superposition state.
H(q);

// Measuring the qubit and reset.
let result = M(q);
Reset(q);

// Return integer value of result.
if result == One {
return 1;
}
return 0;
return MResetZ(q) == Zero ? 0 | 1;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
namespace Kata.Verification {
@EntryPoint()
operation CheckSolution() : Bool {
Message("Testing two random bits generation...");
let randomnessVerifier = () => CheckUniformDistribution(Kata.RandomTwoBits, 0, 3, 1000);
let isCorrect = IsSufficientlyRandom(randomnessVerifier);
if isCorrect {
Expand Down
2 changes: 1 addition & 1 deletion katas/content/random_numbers/random_two_bits/solution.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Let's reuse the `RandomBit` operation from the "Generate A Single Random Bit" exercise.
We can generate two random bits by calling the `RandomBit` operation twice, multiply the most significant bit by 2 and add the second random bit to generate a random two-bit number.
We can generate two random bits by calling the `RandomBit` operation twice, multiply the most significant bit by 2 and add the least significant bit to generate a random two-bit number.

@[solution]({
"id": "random_numbers__random_two_bits_solution",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ namespace Kata {

return -1;
}

}
Loading

0 comments on commit 8369a7d

Please sign in to comment.