An introduction to Quantum Programming with Q# – Part 2

In the previous part of this tutorial, we learned about how a quantum computer works, and we created our first simple quantum application. Now it’s time to complicate things!

To start with the second part of this tutorial, you need to check out this ready-to-use code repository, which contains the Q# application that we are going to analyse in this article. Once cloned, open the Visual Studio solution inside.

Now let’s start analysing the Driver.cs file.
The Main function allows the running of the following procedures:

  • Run_Measurement
  • Run_Superposition
  • Run_Entanglement

Each of these functions allows us to execute a different quantum operation: the first one sets a qubit in a well-known state, then tries to read (measure) it. In the same way, the second one sets a qubit in a superposition state, then tries to read it. The third one activates the entanglement between two different qubits, also sets the first one in a superposition state, and checks that every time a measurement is taken, the second one has the same state.

By taking a look at the Operations.qs file, we can see the mentioned quantum operations, plus one more named Set:

operation Set(desired: Result, q: Qubit) : Unit
{
    // Read the qubit state
    if (desired != M(q)) 
    {
        // Flip the qubit state if it isn't the desired one
        X(q);
    }
}

This method allows us to set a qubit in a particular state by using the X gate. This logic operator flips the qubit state from Zero to One and vice-versa. So, after reading the current qubit state by using the M (Measurement) operator, we can flip its state if it isn’t the desired one. Let’s now analyse the second quantum operation, Measurement:

operation Measurement(count: Int, initial: Result) : (Int, Int)
{
    // Variables are represented by the 'mutable' type
    mutable numOnes = 0;
    using (qubit = Qubit()) 
    {
        for (test in 1..count) 
        {
            // Set the qubit in a well-known initial state
            Set (initial, qubit);
            let res = M (qubit);
            // Count the number of |1>
            if (res == One) 
            {
                // The 'set' operation allows updating the 'mutable' type value
                set numOnes += 1;
            }
        }
        // We have to set the used qubit to the Zero state before release it
        Set(Zero, qubit);
    }
    // Return the number of times that we saw a |0> or a |1>
    return (count-numOnes, numOnes);
}

For “count” times, this method sets a qubit in a well-known state, then tries then to read it. At the end of the execution, the process will print out the read number of 0s and 1s. As expected, by running the Run_Measurement function, we can obtain the following result:

Running: Measurement Test
Init:Zero 0s=1000 1s=0
Init:One  0s=0    1s=1000

So far, nothing special. But, by analysing the third quantum operation, named Superposition, we can see the following line of code:

// Enable the qubit superposition by using the 'Hadamard' gate
H(qubit);

And here is where the magic starts! Thanks to the H (Hadamard) gate, we can set a qubit in a superposition state, so, until the next measurement, its state can be Zero and One simultaneously. By running the Run_Superposition function, we can now obtain the following result:

Running: Superposition Test
Init:Zero 0s=507  1s=493
Init:One  0s=484  1s=516

Mind-blowing! As you can see now, the result of the qubit measurement doesn’t always match the initial state. This situation happens because of superposition. Now, let’s analyse the last quantum operation, Entanglement:

operation Entanglement(count: Int, initial: Result) : (Int, Int, Int)
{
    mutable numOnes = 0;
    mutable agree = 0;
    using ((q0, q1) = (Qubit(), Qubit())) 
    {
        for (test in 1..count)
        {
            Set (initial, q0);
            Set (Zero, q1);
            H(q0);
            // Activate the entanglement between qubits
            CNOT(q0,q1);
            let res = M (q0);
            // Count the number of times that the qubits state is the same for both
            if (M(q1) == res) 
            {
                set agree += 1;
            }
            if (res == One) 
            {
                set numOnes += 1;
            }
        }
        Set(Zero, q0);
        Set(Zero, q1);
    }
    return (count-numOnes, numOnes, agree);
}

For “count” times, this method sets two qubits in a well-known state, then enables the superposition for the first one, and activates the entanglement between both by using the CNOT gate (ControlNOT). This gate works as follow: given two qubits, if the measurement of the first one (the control qubit) is Zero, the state of the second one remains unchanged, or else its state becomes inverted. So, in this way, running the Run_Entanglement function, we can obtain the following result:

Running: Entanglement Test
Init:Zero 0s=514  1s=486  agree=1000
Init:One  0s=497  1s=503  agree=1000

As you can see by the “agree” value, for each cycle, the result of the measurement is the same for both qubits. This situation happens because of entanglement, which flips the state of the second qubit from Zero to One, each time the measurement of the first one is One.

The described quantum algorithms can be represented by a graphical notation, which allows us to intuitively build quantum circuits composed of gates and operations. This type of annotation is also used by the IBM Q Experience web tool, helping to develop and testing quantum applications on a real (physic) quantum computer. Below are the graphical notations of the Superposition and Entanglement quantum operations, described before:

Let’s take a look at the mathematics behind

Before we conclude this tutorial, it will be useful to take a look at the mathematical aspect of the gates operators that we have seen. Recalling the definition of qubit state, we can see both the Zero and the One states in a vectorial way, as follow:

Applying a logic gate to a qubit essentially means computing the product between its current state vector and the matrix associated with the logic gate. For example, if we take a look at the X gate matrix, we can apply it to the Zero state, like this:

Consequently, the resulting state is exactly the inverse of the first one (One). Below are the matrices associated with the H and the CNOT gates:

Our tutorial ends here!
In the future, I will write more articles about quantum programming, through the implementation of some of the most popular algorithms. Don’t miss them!

Happy coding!