Training
Module
Create your first Q# program by using the Azure Quantum Development Kit - Training
Get started with Q# programming by building a quantum random number generator.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In this tutorial, you implement Grover's algorithm in Q# to solve search-based problems. For an in-depth explanation of the theory behind Grover's algorithm, see the Theory of Grover's algorithm.
In this tutorial, you:
Tip
If you want to accelerate your quantum computing journey, check out Code with Azure Quantum, a unique feature of the Azure Quantum website. Here, you can run built-in Q# samples or your own Q# programs, generate new Q# code from your prompts, open and run your code in VS Code for the Web with one click, and ask Copilot any questions about quantum computing.
To run the code sample in the Copilot in Azure Quantum:
To develop and run the code sample in Visual Studio Code:
Grover's algorithm is one of the most famous algorithms in quantum computing. The type of problem it solves is often referred to as "searching a database", but it's more accurate to think of it in terms of the search problem.
Any search problem can be mathematically formulated with an abstract function
Thus, you can formulate the any search problem as: given a classical function
To implement Grover's algorithm to solve a search problem, you need to:
Suppose there are
This section discusses how to implement the algorithm in Q#. There are few things to consider when implementing Grover's algorithm. You need to define what is your marked state, how to reflect about it, and how many iterations to run the algorithm for. You also need to define the oracle that implements the function of the Grover's task.
First, you define what input you are trying to find in the search. To do so, write an operation that applies the steps b, c and d from the Grover's algorithm.
Together, these steps are also known as the Grover'S diffusion operator
operation ReflectAboutMarked(inputQubits : Qubit[]) : Unit {
Message("Reflecting about marked state...");
use outputQubit = Qubit();
within {
// We initialize the outputQubit to (|0⟩ - |1⟩) / √2, so that
// toggling it results in a (-1) phase.
X(outputQubit);
H(outputQubit);
// Flip the outputQubit for marked states.
// Here, we get the state with alternating 0s and 1s by using the X
// operation on every other qubit.
for q in inputQubits[...2...] {
X(q);
}
} apply {
Controlled X(inputQubits, outputQubit);
}
}
The ReflectAboutMarked
operation reflects about the basis state marked by alternating zeros and ones. It does so by applying the Grover's diffusion operator to the input qubits. The operation uses an auxiliary qubit, outputQubit
, which is initialized in the state
Grover's search has an optimal number of iterations that yields the highest probability of measuring a valid output. If the problem has
Continuing to iterate past the optimal number of iterations starts reducing that probability until you reach nearly-zero success probability on iteration
In practical applications, you don't usually know how many solutions your problem has before you solve it. An efficient strategy to handle this issue is to "guess" the number of solutions
The following Q# function calculates the optimal number of iterations for a given number of qubits in a register.
function CalculateOptimalIterations(nQubits : Int) : Int {
if nQubits > 63 {
fail "This sample supports at most 63 qubits.";
}
let nItems = 1 <<< nQubits; // 2^nQubits
let angle = ArcSin(1. / Sqrt(IntAsDouble(nItems)));
let iterations = Round(0.25 * PI() / angle - 0.5);
return iterations;
}
The CalculateOptimalIterations
function uses the formula above to calculate the number of iterations, and then rounds it to the nearest integer.
The Q# operation for Grover's search algorithm has three inputs:
nQubits : Int
, in the qubit register. This register will encode the tentative solution to the search problem. After the operation, it will be measured.iterations : Int
.phaseOracle : Qubit[] => Unit) : Result[]
, that represents the phase oracle for the Grover's task. This operation applies an unitary transformation over a generic qubit register.operation GroverSearch( nQubits : Int, iterations : Int, phaseOracle : Qubit[] => Unit) : Result[] {
use qubits = Qubit[nQubits];
PrepareUniform(qubits);
for _ in 1..iterations {
phaseOracle(qubits);
ReflectAboutUniform(qubits);
}
// Measure and return the answer.
return MResetEachZ(qubits);
}
The GroverSearch
operation initializes a register of
The code makes use of three helper operations: PrepareUniform
, ReflectAboutUniform
, and ReflectAboutAllOnes
.
Given a register in the all-zeros state, the PrepareUniform
operation prepares a uniform superposition over all basis states.
operation PrepareUniform(inputQubits : Qubit[]) : Unit is Adj + Ctl {
for q in inputQubits {
H(q);
}
}
The ``ReflectAboutAllOnes` operation reflects about the all-ones state.
operation ReflectAboutAllOnes(inputQubits : Qubit[]) : Unit {
Controlled Z(Most(inputQubits), Tail(inputQubits));
}
The operation ReflectAboutUniform
reflects about the uniform superposition state. First, it transforms the uniform superposition to all-zero. Then, it transforms the all-zero state to all-ones. Finally, it reflects about the all-ones state. The operation is called ReflectAboutUniform
because it can be geometrically interpreted as a reflection in the vector space about the uniform superposition state.
operation ReflectAboutUniform(inputQubits : Qubit[]) : Unit {
within {
Adjoint PrepareUniform(inputQubits);
// Transform the all-zero state to all-ones
for q in inputQubits {
X(q);
}
} apply {
ReflectAboutAllOnes(inputQubits);
}
}
Now you have all the ingredients to implement a particular instance of Grover's search algorithm and solve the factoring problem. To finish, the Main
operation sets up the problem by specifying the number of qubits and the number of iterations
operation Main() : Result[] {
let nQubits = 5;
let iterations = CalculateOptimalIterations(nQubits);
Message($"Number of iterations: {iterations}");
// Use Grover's algorithm to find a particular marked state.
let results = GroverSearch(nQubits, iterations, ReflectAboutMarked);
return results;
}
Select the desired platform to run your program.
You can test your Q# code with the Copilot in Azure Quantum free of charge - all you need is a Microsoft (MSA) email account. For more information about the Copilot in Azure Quantum, see Explore Azure Quantum.
Open the Copilot in Azure Quantum in your browser.
Copy and paste the following code into the code editor.
import Microsoft.Quantum.Convert.*;
import Microsoft.Quantum.Math.*;
import Microsoft.Quantum.Arrays.*;
import Microsoft.Quantum.Measurement.*;
import Microsoft.Quantum.Diagnostics.*;
operation Main() : Result[] {
let nQubits = 5;
let iterations = CalculateOptimalIterations(nQubits);
Message($"Number of iterations: {iterations}");
// Use Grover's algorithm to find a particular marked state.
let results = GroverSearch(nQubits, iterations, ReflectAboutMarked);
return results;
}
operation GroverSearch(
nQubits : Int,
iterations : Int,
phaseOracle : Qubit[] => Unit) : Result[] {
use qubits = Qubit[nQubits];
PrepareUniform(qubits);
for _ in 1..iterations {
phaseOracle(qubits);
ReflectAboutUniform(qubits);
}
// Measure and return the answer.
return MResetEachZ(qubits);
}
function CalculateOptimalIterations(nQubits : Int) : Int {
if nQubits > 63 {
fail "This sample supports at most 63 qubits.";
}
let nItems = 1 <<< nQubits; // 2^nQubits
let angle = ArcSin(1. / Sqrt(IntAsDouble(nItems)));
let iterations = Round(0.25 * PI() / angle - 0.5);
return iterations;
}
operation ReflectAboutMarked(inputQubits : Qubit[]) : Unit {
Message("Reflecting about marked state...");
use outputQubit = Qubit();
within {
// We initialize the outputQubit to (|0⟩ - |1⟩) / √2, so that
// toggling it results in a (-1) phase.
X(outputQubit);
H(outputQubit);
// Flip the outputQubit for marked states.
// Here, we get the state with alternating 0s and 1s by using the X
// operation on every other qubit.
for q in inputQubits[...2...] {
X(q);
}
} apply {
Controlled X(inputQubits, outputQubit);
}
}
operation PrepareUniform(inputQubits : Qubit[]) : Unit is Adj + Ctl {
for q in inputQubits {
H(q);
}
}
operation ReflectAboutAllOnes(inputQubits : Qubit[]) : Unit {
Controlled Z(Most(inputQubits), Tail(inputQubits));
}
operation ReflectAboutUniform(inputQubits : Qubit[]) : Unit {
within {
// Transform the uniform superposition to all-zero.
Adjoint PrepareUniform(inputQubits);
// Transform the all-zero state to all-ones
for q in inputQubits {
X(q);
}
} apply {
// Now that we've transformed the uniform superposition to the
// all-ones state, reflect about the all-ones state, then let the
// within/apply block transform us back.
ReflectAboutAllOnes(inputQubits);
}
}
Tip
From Copilot in Azure Quantum, you can open your program in VS Code for the Web by selecting the VS Code logo button in the right-hand corner of the code editor.
You can also submit your program to the free Quantinuum Emulator. The emulator simulates a quantum computer with 20 qubits.
Explore other Q# tutorials:
Training
Module
Create your first Q# program by using the Azure Quantum Development Kit - Training
Get started with Q# programming by building a quantum random number generator.