Explore superposition with Q#
In the previous units, you learned about superposition and Dirac notation. That's enough theory for now! Let's explore superposition in Q# by writing some code.
In this unit, you'll create quantum superposition and dive into probabilities with Q# by using the DumpMachine
function. The DumpMachine
function dumps information about the current status of the quantum system at the point where it's called.
Create a new Q# file
- Open Visual Studio Code.
- In Visual Studio Code, select File > New Text File and save the file as Main.qs.
- Select View -> Command Palette and type Q#: Set the Azure Quantum QIR target profile. Press Enter.
- Select Q#: Unrestricted.
Get started with superposition
Let's start with a simple program that generates a random bit using a qubit in superposition. You'll use the DumpMachine
function to see the state of the qubit at different points in the program.
Add the following code to the Main.qs file:
import Microsoft.Quantum.Diagnostics.*; operation Main() : Result { use q = Qubit(); Message("Initialized qubit:"); DumpMachine(); // First dump Message(" "); H(q); Message("Qubit after applying H:"); DumpMachine(); // Second dump Message(" "); let randomBit = M(q); Message("Qubit after the measurement:"); DumpMachine(); // Third dump Message(" "); Reset(q); Message("Qubit after resetting:"); DumpMachine(); // Fourth dump Message(" "); return randomBit; }
Here, you call
DumpMachine
four times:- After the qubit is allocated.
- After you place the qubit in superposition.
- After you measure the qubit's state.
- After you reset the qubit.
You split the operation
MResetZ
into two operations:M
andReset
. You do so because you want to inspect the state after the measurement.To run your program on the built-in simulator, click Run above the
Main
operation or press Ctrl+F5. Your output will appear in the debug console.The
DumpMachine
function creates a table of information that describes the state of the qubit register. Specifically, it gives the probability amplitude, the probability, and the phase in radians for each basis state.At the end of the program, you get a result of
Zero
orOne
. Let's look at each step.Initialized qubit: Every qubit that's allocated with the
use
statement starts in the state $|0\rangle$. SoDumpMachine
produces the information that corresponds to a single-qubit register in the state $|0\rangle$.Initialized qubit: DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |0⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Qubit after applying H: After applying
H
, we prepare the qubit in the superposition state $|\psi\rangle=\frac1{\sqrt2} |0\rangle + \frac1{\sqrt2} |1\rangle$.Qubit after applying H: DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |0⟩ | 0.7071+0.0000𝑖 | 50.0000% | 0.0000 |1⟩ | 0.7071+0.0000𝑖 | 50.0000% | 0.0000
Qubit after the measurement: After we measure and store the outcome, which can be a
Zero
orOne
. For example, if the resulting state isOne
, the state of the registers collapses to $|1\rangle$ and is no longer in superposition.Qubit after the measurement: DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |1⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Qubit after resetting: The operation
Reset
resets the qubit to the state $|0\rangle$. Remember that for any Q# operation, you always need to leave the qubits you use in the state $|0\rangle$ so that other operations can use it.Qubit after resetting: DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |0⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Note
Your outputs might differ because the random number generator is probabilistic. The probabilities of the outcomes are not deterministic.
Explore other types of superposition states
Now that you know how to inspect the state of a register, you can see more operations that modify the state of your qubits and place them into a superposition.
The current random number generator produces either Zero
or One
with a 50% probability. Let's look at a second example that generates random numbers with a different probability.
Skewed random bit generator
Suppose you want to create a random bit generator that's skewed, that is, the probability of getting Zero
is different from the probability of getting One
.
For example, you want the outcome Zero
with probability $P$ and the outcome One
with probability $1-P$. Here's a valid qubit state that produces such a random bit generator:
$$|\psi\rangle=\sqrt{P}|0\rangle+\sqrt{1-P}|1\rangle$$
Here, $\alpha=\sqrt{P}$ and $\beta=\sqrt{1-P}$ are the amplitudes of the basis states $|0\rangle$ and $|1\rangle$, respectively.
This state can be obtained by sequentially applying the operator $R_y(2\arccos\sqrt{P})$ to a qubit in the state $|0\rangle.$ You can achieve this result in Q# by using the operation Ry in the Standard library.
Tip
To learn more about the math behind single-qubit operations, check out the Single-Qubit Gates tutorial in Quantum Katas.
Modify Main.qs like the following example, and then save the file. This example chooses $\alpha$ to be about $\frac13$.
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; operation Main() : Result { use q = Qubit(); let P = 0.333333; // P is 1/3 Ry(2.0 * ArcCos(Sqrt(P)), q); Message("The qubit is in the desired state."); Message(""); DumpMachine(); // Dump the state of the qubit Message(""); Message("Your skewed random bit is:"); let skewedrandomBit = M(q); Reset(q); return skewedrandomBit; }
To run your program on the built-in simulator, click Run above the
Main
operation or press Ctrl+F5. Your output will appear in the debug console.You can see how
DumpMachine
displays the expected state after it applies the operations and displays the associated probabilities. Notice that the probability of gettingZero
is about 33.33% and the probability of gettingOne
is about 66.67%. Thus, the random bit generator is skewed.The qubit is in the desired state. DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |0⟩ | 0.5773+0.0000𝑖 | 33.3333% | 0.0000 |1⟩ | 0.8165+0.0000𝑖 | 66.6667% | 0.0000 Your skewed random bit is: Result: "One"
Note
Your output might differ because the random number generator is probabilistic. The probabilities of the outcomes are not deterministic.
Multiple-qubit superposition
Now let's explore superpositions of a register that includes many qubits. For example, if your register consists of three qubits, then you have eight basis states:
$$|000\rangle,|001\rangle,|010\rangle,|011\rangle,|100\rangle,|101\rangle, |110\rangle,|111\rangle $$
So you can express an arbitrary three-qubit state as:
$$|\psi\rangle=\alpha_0|000\rangle+\alpha_1|001\rangle+\alpha_2|010\rangle+\alpha_3|011\rangle+\alpha_4|100\rangle+\alpha_5|101\rangle+\alpha_6 |110\rangle+\alpha_7|111\rangle$$
Here, $\alpha_i$ are complex numbers that satisfy $\sum|\alpha_i|^2=1$.
For example, you can place qubits in a uniform superposition by applying H
to each qubit. You can use this uniform superposition to create a different version of the quantum random number generator that generates three-bit numbers by measuring three qubits in superposition
instead of measuring one qubit three times.
Basis | Number |
---|---|
$\ket{000}$ | 0 |
$\ket{001}$ | 1 |
$\ket{010}$ | 2 |
$\ket{011}$ | 3 |
$\ket{100}$ | 4 |
$\ket{101}$ | 5 |
$\ket{110}$ | 6 |
$\ket{111}$ | 7 |
Modify Main.qs like the following example, and then save the file.
import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Convert.*; import Microsoft.Quantum.Arrays.*; operation Main() : Int { use qubits = Qubit[3]; ApplyToEach(H, qubits); Message("The qubit register in a uniform superposition: "); DumpMachine(); let result = ForEach(M, qubits); Message("Measuring the qubits collapses the superposition to a basis state."); DumpMachine(); ResetAll(qubits); return BoolArrayAsInt(ResultArrayAsBoolArray(result)); }
Here, you see three concepts:
- The
qubits
variable now represents aQubit
array that has a length of three. - The operations
ApplyToEach
andForEach
are useful to measure and act on multiple qubits, and they use less code. Q# libraries offer many kinds of operations and functions that make writing quantum programs more efficient. - The
BoolArrayAsInt
andResultArrayAsBoolArray
functions from theMicrosoft.Quantum.Convert
library transform the binaryResult
array that's returned byForEach(M, qubits)
into an integer.
- The
To run the program, click Run above the
Main
operation or press Ctrl+F5. Your output will appear in the debug console.By using
DumpMachine
, you see how the act of measuring the three qubits collapses the state of the register to one of the eight possible basis states. For example, if you get the result3
, it means that the state of the register collapsed to $|110\rangle$.The qubit register in a uniform superposition: DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 Measuring the qubits collapses the superposition to a basis state. DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |110⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000 Result: "3"
Note
Your output might differ because the random number generator is probabilistic. The probabilities of the outcomes are not deterministic.
The
ForEach(M, qubit)
operation measures each qubit in turn, gradually collapsing the state. You can also dump the intermediary states after each measurement. To do so, modify Main.qs like the following example, and then save the file.import Microsoft.Quantum.Diagnostics.*; import Microsoft.Quantum.Measurement.*; import Microsoft.Quantum.Math.*; import Microsoft.Quantum.Convert.*; operation Main() : Int { use qubits = Qubit[3]; ApplyToEach(H, qubits); Message("The qubit register in a uniform superposition: "); DumpMachine(); mutable results = []; for q in qubits { Message(" "); set results += [M(q)]; DumpMachine(); } Message(" "); Message("Your random number is: "); ResetAll(qubits); return BoolArrayAsInt(ResultArrayAsBoolArray(results)); }
Here, you use a
for
loop to act on each qubit sequentially. Q# has classical flow control capabilities, such asfor
loops, andif
statements, that you can use to control the flow of your program.To run the program, click on Run from the list of commands above the
Main
operation or press Ctrl+F5.You can see how each consecutive measurement changes the quantum state and therefore the probabilities of obtaining each outcome. For example, if your result is number five, you'll get the following output. Let's look briefly at each step:
State preparation: After applying
H
to each qubit of the register, we obtain a uniform superposition.The qubit register in a uniform superposition: DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000 |111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
First measurement: In the first measurement, the result was
One
. Therefore, all of the amplitudes of the states whose rightmost qubit isZero
are no longer present. The amplitudes are $|0\rangle=|000\rangle, |2\rangle=|010\rangle, |4\rangle=|100\rangle$, and $|6\rangle= |110\rangle$. The rest of the amplitudes increase to fulfill the normalization condition.DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |001⟩ | 0.5000+0.0000𝑖 | 25.0000% | 0.0000 |011⟩ | 0.5000+0.0000𝑖 | 25.0000% | 0.0000 |101⟩ | 0.5000+0.0000𝑖 | 25.0000% | 0.0000 |111⟩ | 0.5000+0.0000𝑖 | 25.0000% | 0.0000
Second measurement: In the second measurement, the result was
Zero
. Therefore, all of the amplitudes of the states whose second rightmost (middle) qubit isOne
vanish. The amplitudes are $|3\rangle=|011\rangle$ and $|7\rangle=|111\rangle$. The rest of the amplitudes increase to fulfill the normalization condition.DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |001⟩ | 0.7071+0.0000𝑖 | 50.0000% | 0.0000 |101⟩ | 0.7071+0.0000𝑖 | 50.0000% | 0.0000
Third measurement: In the third measurement, the result was
One
. Therefore, all of the amplitudes of the states whose leftmost qubit isZero
clear out. The only compatible state is $|5\rangle=|101\rangle$. This state gets an amplitude probability of $1$.DumpMachine: Basis | Amplitude | Probability | Phase ----------------------------------------------- |101⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000 Your random number is: Result: "5"
Note
Your output might differ because the random number generator is probabilistic. The probabilities of the outcomes are not deterministic.