We left off in the last post after having discussed the Hadamard gate - our first quantum gate - and how it can be used to crate a uniform superposiiton for a single qubit. We are going to continue today by exploring other single qubit gates, discussing the underlying mathematics and, of course, testing it all out with some Q# code.
Pauli matrices in quantum mechanics 🔗
Let’s start our today’s journey by looking at the famous Pauli spin matrices, as they are central to quantum computational transformations.
In quantum mechanics, the three Pauli gates are represented by the greek letter
The presence of
Pauli matrices are also of fundamental importance in quantum computing too, as they represent three of the most useful single qubit gates, and will be our main focus in this part 3 of the series.
Circuits 🔗
Similarly to classical computer science, where gates can be used to assemble circuits, in quantum computing, a sequence of quantum gates is usually referred to as a “quantum circuit”. This in turn allows visualization of the logic flow of the program at the most basic information level.
In this part 3 of the series, we are going to focus on single qubit gates only - we’ll be looking at multi qubit gates next time. In classical computing, single bit gates - at least in boolean circuits - can only exist in two variants: identity gate, which leaves the bit value intact, and a
In quantum computing, however, as we already mentioned in previous parts, we can come up with infinitely many single qubit gates, as there are infinitely many ways of manipulating a quantum system that the qubit represents. The reason is
quantum transformations are mathematically described by matrices - more specifically,
Single qubit quantum gates are often referred to as elementary quantum gates. Most general equation we can write here for quantum gates is transforming one quantum state
Quantum circuits can be visualized in a similar way to how classical circuits are visualized, with simple diagrams representing ordered operations on qubits. My favorite basic tool for quantum circuits is called Bono, and it can be cloned from Github and run locally; however, there are many other useful circuit building tools, one of the most popular - albeit a little overwhelming at first - being Quiirk.
An example is shown below:
In the circuit above, we start with a single qubit in a basis state
Identity gate 🔗
The simplest possible quantum gate is the 2 x 2 identity gate and it semantically corresponds to the behavior of the identity gate in classical computing. Given that it is two dimensional, in linear algebra it is often denoted with
We can also say that the linear transformation behind the identity is an identity function - a function that always returns the same value as was used as its argument. We can write it in the following way:
Let’s see what would happen when we apply
As expected, nothing really happens - when the identity gate is applied, the qubit state is left unchanged.
Pauli matrices in quantum computing 🔗
In quantum computing, when representing Pauli matrices, it is common to skip the quantum mechanical
As already mentioned in part 1 of this series, we are going to try to avoid imaginary and complex numbers wherever we can (it won’t always be possible) - and in this case we can simplify
And that’s going to be approach we will take here.
Each of the matrices can also be written using Dirac notation, as the outer product of the base vectors with their complex conjugates. This might seem somewhat confusing at first but quickly becomes very intuitive:
We shall now dive deeper into the mathematical consequences of applying each of the three Pauli gates to such a quantum system, starting with
The Pauli
p
Since we already know that we can write the state of our qubit as the linear compination of the base vectors, with
we can now express the
All of this, especially the theoretical symmetry to the classical
Pauli
In other words, we started with a state of
Finally, the
The three Pauli gates are all its own inverses, meaning the following holds:
Pauli gates and the identity gate have fundamental meaning in quantum information theory in a sense that they can be used as building blocks to make up any other transformation. We could algebraically express any quantum computational linear transformation in the two dimensional complex vector space as a product of a complex unit and a linear combination of the three Pauli matrices and the identity matrix. It means that we can always find
where
Pauli gates and the identity gate in Q 🔗
We’ve spent quite some time looking at the theory, but now we are ready to go back to Q# programming. All three Pauli gates are available as functions in the
Identity gate is also available - mainly for completeness, but also because it is sometimes useful when an algorithm requires a no-effect action to be performed on a qubit. It is probably of no surprise to anyone that the function signature for identity is
A sample Q# identity operation is shown below.
operation Identity(count : Int) : Int {
mutable resultsTotal = 0;
using (qubit = Qubit()) {
for (idx in 1..count) {
I(qubit);
let result = MResetZ(qubit);
set resultsTotal += result == One ? 1 | 0;
}
return resultsTotal;
}
}
The operation, similarly to the previous examples in earlier parts of this series, takes in an integer input indicating the amount of time the operation should be repeated. A single qubit is allocated within the loop, then the
Below is the following C# driver to orchestrate this Q# operation. We will use this driver for all the snippets that come, so I will not repeat it anymore (only the invoked operation name would differ). The quantum operation would be run 1000 times to give us a better statistical result set.
class Driver
{
static async Task Main(string[] args)
{
using var qsim = new QuantumSimulator();
var iterations = 1000;
Console.WriteLine($"Running qubit measurement {iterations} times.");
var results = await Identity.Run(qsim, iterations);
Console.WriteLine($"Received {results} ones.");
Console.WriteLine($"Received {iterations - results} zeros.");
}
}
We should get the following output when running this program:
Running qubit measurement 1000 times.
Received 0 ones.
Received 1000 zeros.
This is of course hardly surprising. The default state of the qubit is
We will now turn our attention to the bit flip. The Q# code is identical to the one above, except we will use
operation Bitflip(count : Int) : Int {
mutable resultsTotal = 0;
using (qubit = Qubit()) {
for (idx in 1..count) {
X(qubit);
let result = MResetZ(qubit);
set resultsTotal += result == One ? 1 | 0;
}
return resultsTotal;
}
}
Running this, using the same type of C# driver, would produce the following result:
Running qubit measurement 1000 times.
Received 1000 ones.
Received 0 zeros.
This is also very much in line with our expectations. The default state of the qubit is
The next thing worth experimenting with is to try to chain several gates together. For example, we already made a claim - not supported by any algebraic calculation though - that the following relation holds:
We could verify this easily using Q#. Our operation would look as follows:
operation HZH(count : Int) : Int {
mutable resultsTotal = 0;
using (qubit = Qubit()) {
for (idx in 1..count) {
H(qubit);
Z(qubit);
H(qubit);
let result = MResetZ(qubit);
set resultsTotal += result == One ? 1 | 0;
}
return resultsTotal;
}
}
This code is yet again similar to the previous snippets, except this time we apply a chain of gates on the same qubit -
Running qubit measurement 1000 times.
Received 1000 ones.
Received 0 zeros.
The result is identical to running the bit flip
Rotation gates 🔗
As we already mentioned, there are infinitely many possibilities for constructing single qubit gates. For the purpose of this series, the 5 we already discussed - Hadamard gate
We mentioned that they are generializations of the Pauli gates, and looking at the matrices closely, the relation between the rotation gates and Pauli gates should become a little bit more apparent. For example, the
Similarly to Pauli gates, all three of these rotation gates are available in Q# using the
Let’s try invoking
operation Rx45(count : Int) : Int {
mutable resultsTotal = 0;
using (qubit = Qubit()) {
for (idx in 1..count) {
Rx(45.0, qubit);
let result = MResetZ(qubit);
set resultsTotal += result == One ? 1 | 0;
}
return resultsTotal;
}
}
Executing this code produces the following result:
Running qubit measurement 1000 times.
Received 251 ones.
Received 749 zeros.
So a rotation by 45 degrees along the X axis, distributes the probabilities for obtaining one or zero 0.25-0.75 - when measuring in the computational basis along the Z axis.
Summary 🔗
In this post, we discussed in depth several important quantum computing gates - Pauli gates
All of this is still quite basic in terms of what we can do at the Q# code level, but we are slowly building up the necessary knowledge and amassing building blocks that will be extremely helpful when putting together quantum algorithms.
Equipped in this knowledge we should be are ready to have a look at multi qubit gates and the algebraic foundations behind them - which we will do in the next part. We are also going to look at one of the more bizarre quantum phenomena - entanglement. After that, we will be ready to start putting it all to good use by exploring some quantum algorithms.