Learn Quantum Computing with Python and IBM Quantum Experience
上QQ阅读APP看书,第一时间看更新

Understanding superposition

Superposition is something we generally can't see with the naked eye. This is typically the case when discussing the superposition of an electron. Since an electron is very small and there are so many of them, it is hard to distinguish one with even a powerful microscope. There are, however, some analogies in the classical world that we can use to illustrate what superposition is. For example, a spinning coin is what most texts use to describe superposition. While it is spinning, we can say that it is in the state of both heads and tails. It isn't until the coin collapses that we see what the final state of the coin is.

In this chapter, we're going to use this spinning coin analogy just to help you understand the general principle of superposition. However, once we start working on our quantum circuits, you will see some of the differences between superposition and its probabilistic behavior in the classical world versus its behavior in the quantum world. Let's start by reviewing the random effects in the classical world.

Learning about classical randomness

Previously, we discussed the randomness of a spinning coin as an example. However, the spinning coin and its results are not as random as we think. Just because we cannot guess the correct answer when a coin is spun on a table or flipped in the air does not make it random. What leads us to believe that it's random is the fact that we don't have all the information necessary to know or predict or, in fact, determine that the coin will land on either heads or tails.

All the relevant information, such as the weight of the coin, its shape, the amount of force required to spin the coin, the air resistance, the friction of the platform the coin is rolling on, and so on, all of this information, and the information of the environment itself, is not known to us in order for us to determine what the outcome would be after spinning a coin. It's because of this lack of information that we assume the spinning of the coin is random. If we had some function that could calculate all this information, then we would always successfully determine the outcome of the spinning coin.

The same can be said about random number generators. As an example, when we trigger a computer to generate a random number, the computer uses a variety of information to calculate and generate a random number. These parameters can include information such as the current daytime that the request was triggered, information about the user or the system itself, and so on.

These types of random number generators are often referred to as pseudorandom number generators (PSRN) or deterministic random bit generators (DRBT). They are only as random as the calculation or seed values provided that is allowed. For example, if we knew the parameters used and how they were used to generate this random number, then we would be able to determine the generated random number each and every time.

Now, that being said, I don't want you to worry about anyone determining the calculations or cryptic keys that you may have generated. We use these pseudorandom number generators because of the precision and granularity that they encompass to generate this number, which is such that any deviation can drastically alter the results.

So, why bother reviewing the probabilistic and random nature of a spinning coin? One, it's to explain the difference between randomness, or what we believe is random, in the classical world versus the randomness in the quantum world.

In the classic world, we learned that if we had all the information available, we can more than likely determine an outcome. However, in the previous section, where we described the double-slit experiment, we saw that we couldn't determine where in the screen the electron was going to hit. We understood the probabilities of where it would land based on our experiment. But even then, we could not deterministically identify where precisely the electron was going to land on the screen. You'll see an example of this when we create our superposition circuit in the next section.

For those who wish to learn a little more about this phenomenon, I would suggest reading the book by the famous physicist Richard Feynman titled QED: The Strange Theory of Light and Matter.

Preparing a qubit in a superposition state

In this section, we are going to create a circuit with a single qubit and set an operator on the qubit to set it in a superposition state. But before we do that, let's quickly define what a superposition state is.

We define the qubit as having two basis energy states, one of which is the ground (0) state and the second of which is the excited (1) state, as illustrated in Figure 4.3. The state value name of each basis state could be anything we choose, but since the results from our circuit will be fed back to a classic system, we will use binary values to define our states – in this case, the binary values 0 and 1. To say that the superposition of two states is being in both 0 and 1 at the same time is incorrect. The proper way to state a qubit is in a superposition state is to say that it is in a complex linear combination of states where in this case, the states are 0 and 1.

The following screenshot is referred to as a Bloch sphere, which represents a single qubit and its two basis states, which are located on opposite poles. On the north pole, we have the basis state 0, while the south pole, we have the basis state 1. The symbols surrounding the basis state values are the commonly used notations in most quantum computing text. This is called Dirac notation, which was named after the English theoretical physicist Paul Dirac, who first conceived the notation, which he called the Bra-Ket notation. Both Bra-Ket and Dirac notation are generally used interchangeably as they refer to the same thing, as we'll see later.

Figure 4.3 - Two basis states of a qubit on a Bloch sphere

Ok, so let's stop talking and let's start coding. We're going to create a quantum circuit with a single qubit. We will then execute the circuit so that we can obtain the same result we can see in the preceding screenshot, which is the initial state of the qubit, state .

Open a new Qiskit Notebook and enter the following code into the next empty cell:

from qiskit.visualization import plot_bloch_multivector

qc = QuantumCircuit(1)

# execute the quantum circuit

backend = Aer.get_backend('statevector_simulator')

result = execute(qc, backend).result()

stateVectorResult = result.get_statevector(qc)

#Display the Bloch sphere

plot_bloch_statevector(stateVectorResult)

The first line imports the Bloch sphere library so that we can plot our vector. The next line creates the circuit so that it includes 1 qubit, and in the next three lines, we are setting up our backend to execute the circuit to the simulator. And finally, we display the results on our Bloch sphere, which should display the same as what you can see in the preceding diagram.

So, you might be wondering what all this talk about vectors and statevector simulators is about. Good! This is what we will discuss now. The reason I wanted to run the experiment first as opposed to explaining what the vector states are and what the statevector simulator does is so that you can see it first and then hopefully the description will be a bit clearer. Let's start with the vector explanation.

Each qubit, as mentioned earlier, is made up of two basis states, which in this example reside on opposite poles of the Bloch sphere. These two basis states are what we would submit back to the classical system as our result – either one or the other. The vector representing these two points originates from the origin of the Bloch sphere, as you can see in the previous diagram, or the result from your experiment. If we were to notate this as a vector, we would write the following:

Since the opposite would apply to the opposite pole, we would notate it as follows:

From observing the vector values, you can see that flipping the values of the vector is similar to a classical bit flip. Now that we understand the vector representation of a qubit, let's continue and set the qubit in a superposition state:

  1. Insert a new cell at the bottom of the current notebook and enter the following code:

    #Place the qubit in a superposition state by adding a #Hadamard (H)gate

    qc.h(0)

    #Draw the circuit

    qc.draw()

    The previous code places a Hadamard (H) gate onto the first qubit, identified by the qubit's index value (0). It then calls the draw function, which will draw the circuit diagram.

    After running the previous cell, you should see the following circuit image, which represents adding the Hadamard gate to the qubit:

    Figure 4.4 – Circuit with a Hadamard (H) gate added to a qubit

    The Hadamard gate is an operational gate that places the qubit in a superposition state, or, more specifically, a complex linear combination of the basis states, which means that when we measure the qubit, it will have an equal probability result of measuring a 0 or 1. Or in other words, it would collapse to the basis state value or .

    Mathematically, the superposition state is represented in the following two superposition equations, which, as you can see, depends on which of the two basis states it was in prior to applying the Hadamard gate. The first superposition equation is as follows and originates from the state:

    The second superposition equation, originating from the state, is as follows:

    This is equal to a π⁄2 rotation about the X and Z axes of the Bloch sphere. These rotations are Cartesian rotations, which rotate counter-clockwise.

  2. Now, let's execute our circuit, see what this looks like, and where the state vector lands on the Bloch sphere. In the following code, you will execute the same circuit again, the results of which will not differ in that the qubit will appear in a superposition state, which you will see in the resulting Bloch sphere's output:

    #Execute the circuit again and plot the result in the #Bloch sphere

    result = execute(qc, backend).result()

    #Get the state vector results of the circuit

    stateVectorResult = result.get_statevector(qc)

    #Display the Bloch sphere

    plot_bloch_multivector(stateVectorResult)

    Once the circuit has completed executing, the results will be plotted on the Bloch sphere in a superposition between and , as illustrated in the following screenshot:

    Figure 4.5 - Superposition of a qubit after 90° rotation around the X and Z axes

    As you can see in the preceding screenshot, this has placed the vector on the positive X axis, as described previously when adding a H gate from the basis state.

  3. Now, let's clear the circuit. This time, we will initialize the qubit to the state first and then apply a Hadamard gate to see what happens to the vector this time. Initialize qubit to the state and place it in a superposition. Clear the circuit and initialize qubit to 1 before applying Hadamard gate:

    #Reset the circuit

    qc = QuantumCircuit(1)

    #Rotate the qubit from 0 to 1 using the X (NOT) gate

    qc.x(0)

    #Add a Hadamard gate

    qc.h(0)

    #Draw the circuit

    qc.draw()

    You should now see the following circuit:

    Figure 4.6 - Applying an H gate superposition from an opposite base state

  4. Now, let's execute the circuit and plot the result on the Bloch sphere:

    Figure 4.7 - Superposition of a qubit after 90° rotation around the X and Z axes from the state

    Do you see the difference between adding a Hadamard gate to a qubit in the state (Figure 4.5) and adding it to a qubit in the state in the preceding screenshot?

    Of course, the difference is where it lands on the X axis! Because the vector falls onto the positive X axis when applying a Hadamard gate to the state, this is commonly notated as . This logically means that the vector falls onto the negative X axis when applying a Hadamard gate to the state. This is commonly notated as .

    Now, take a look at the superposition equations and note the initial basis state on the left-hand side of the equation, which represents the state of the qubit before it was placed in superposition, and notice the right-hand side of the equation. Pay close attention to the signs in-between.

    Notice that the signs match the direction of where the vector lands after the Hadamard gate is applied. From the state, it moves toward the positive (+) direction of the X axis, and from the state, it moves toward the negative (-) direction of the X axis.

    This difference is referred to as a phase difference between the two results. This will be very important later on in this and subsequent chapters as phase difference plays an important role in many quantum algorithms and blends itself into the topic of interference, as we will learn shortly.

    One last thing that we will discuss before moving on is to now loop back to our earlier discussion on probabilities. Now that we've learned what superposition looks like in a circuit and on a Bloch sphere, let's execute and see what the probabilities are when we measure the qubit after it is in superposition. As you may recall from our first analogy of flipping or spinning a coin, we said that once the coin is spinning, it is in a superposition of heads or tails, or in this example, 0 or 1.

    Once we collapse and observe, the result of the coin will be one or the other. However, classically, this is pseudorandom, as we learned. But in quantum computing, electron detection is truly random as there is no way to determine its outcome without disturbing it. This is the same as measuring a qubit; we are, in essence, measuring it, and therefore forcing it to collapse into one of two basis states.

  5. Then, measure the qubit after it is in superposition and reset the circuit. Let's start from the state and apply a Hadamard gate, as we did earlier:

    #Reset the circuit

    qc = QuantumCircuit(1,1)

    #Add a Hadamard gate

    qc.h(0)

  6. Now, let's include a measurement operator so that we can measure the qubit, which should collapse it into one of two states, as follows:

    #Create a measurement circuit with 1 qubit and 1 bit

    measurement_circuit = QuantumCircuit(1,1)

    #Measure function used to map the qubit and bit by their #index value on the circuit, respectively

    measurement_circuit.measure(0,0)

    #Concatenate the circuits together

    full_circuit = qc+measurement_circuit

    #Draw the full circuit

    full_circuit.draw()

    In the previous code, we created a measurement circuit that includes a measurement operation that basically collapses the qubit from its current state to that of either 0 or 1.

    You will obtain the following circuit:

    Figure 4.8 - Full circuit with rotation and measurement from qubit (q) to classic bit (c)

    The previous diagram illustrates our circuit, which you can see now includes two new components, the first of which is the classic register below the quantum register. The second component is the measurement operator, which will extract the result of the qubit and pass it onto the classic bit. The result will collapse the state of the qubit to either 1 or 0.

  7. Now, let's execute and run a few shots and see the results. shots refers to running through the experiment a few times and aggregating its results:

    #Execute the circuit again and print the results

    backend = Aer.get_backend('qasm_simulator')

    result = execute(full_circuit, backend, shots=1000).    result()

    counts = result.get_counts(full_circuit)

    print(counts)

    The previous code will now use qasm simulator rather than the state vector simulator, which will allow us to obtain the measured results of the circuit. In this case, we will extract counts, which stores the number of times the measurement resulted in either a 0 or 1 out of 1000 shots.

    The result of the previous code is as follows:

    {'1': 491, '0': 509}

    Notice that the results are almost 50%, which illustrates that you can have an equal probability of 0 and 1!

    Important Note

    Note that your actual value results might be different than what was shown previously, but the probability should be pretty close to 50%. Retry running the code a few times and play around with the number of shots to see if you get any differences. The limitation for shots at the time of writing was around 8,000.

The reason why we run so many shots of a circuit is because the near-term quantum devices used these days are not fault-tolerant yet. Fault-tolerant devices are those that exhibit very low error rates and large quantum volumes, which we will cover in Chapter 9, Understanding Qiskit Aer. Current near-term devices need to run multiple shots to provide your quantum algorithm with good probability results. However, once these devices reach fault-tolerant status, you can expect the probabilities to be closer to 1; that is, they are highly accurate with fewer shots.

Now that we have covered superposition, we will move onto the second quantum computing principle, which is entanglement.