Having introduced all the constructs of QIO in an example driven manner, it is useful to have a brief recap of all the contructors available. This section aims to list all the QIO contructors, along with a brief description, and their relation to the categorical model of circuits introduced in chapter 3. In fact, it is mainly the choice of unitary operators that is influenced by the categorical model, as the circuits we introduced didn’t model the initialisation, or explicit measurement of qubits. As such we shall first recap the contructs of the monadic QIO type, and then move onto the constructs of the pure monoidal U type of unitary operations.
• Qbit :: ∗
The type Qbit is a reference to a physical qubit within the system. • QIO :: ∗ → ∗
instanceMonad QIO
QIO is a monadic type constructor, that contains the following primitive operations for defining quantum computations.
• mkQbit :: Bool → QIO Qbit
The mkQbit construct is used to initialise a qubit into the base state given by the argument Bool value.
• applyU :: U → QIO ()
The applyU construct is used to apply a unitary operation (of type U which is introduced below) to the current quantum state of a system.
• measQbit :: Qbit → QIO Bool
The measQbit construct is used to measure qubits, and return a Boolean result depending on the measurement outcome. It is this measurement con- struct that can be effectful, causing side effects to the quantum state of the rest of the system, and gives rise to the monadic structure of QIO.
• U :: ∗
instanceMonoid U
The U data type defines the unitary operations that can be defined for use in QIO computations. Its monoidal structure gives rise to an operation that sequences operations, and a • element that corresponds to the idenity operator. The following primitives can be used to define unitary operations (of type U ).
• swap :: Qbit → Qbit → U
The swap operation is used to swap the states of two physical qubits within the overall quantum state. This corresponds exactly to one of the wires constructs as introduced in the categorical model, and any other wires con- struct can be defined in terms of multiple swap operations. A brief discussion on the inclusion of the swap operation is included at the end of this section.
• cond :: Qbit → (Bool → U ) → U
The cond operation allows conditional operations to be defined, whereby the unitary that is applied depends on the value of a control qubit. The cond operation corresponds to the controlled operations introduced in the categorical model, whereby the special cases in which one of the branches is • correspond exactly to the two control structures in the categorical model.
• rot :: Qbit → ((Bool, Bool) → C) → U
The rot operation applies a single qubit rotation to the given argument qubit. Instances of the rot operation correspond exactly to the one qubit rotations introduced in the categorical model. The second argument (Bool , Bool )→ C defines exactly the unitary matrix that represents the rotation, with in- stances of rot including the Hadamard rotation, Not rotation (pauli X), and a Phase rotation. Although any single qubit rotation can be defined. The classical subset of QIO is only restricted by the instances of rot which can be used (E.g. only Not and Id rotations are classical).
• ulet :: Bool → (Qbit → U ) → U
ulet allows the introduction of ancilliary quibts into a QIO computation. The programmer must ensure unitarity by only using the ancilliary qubit in such a way as to return it to its original value.
• urev :: U → U
As all members of the U data type are unitary, the urev function is able to calculate the inverse (or reverse) of the given argument unitary. This is achieved at the syntactic level.
• Prob :: ∗ → ∗
instanceMonad Prob
The Prob type contrsuctor is defined to model the probablistic nature of running quantum computations. As such it has a monadic structure, that is used to create probability distributions instead of a pure return value.
• run :: QIO a → IO a
The run function embeds QIO programs into the IO monad, using the random number generator to simulate the running of a QIO computation.
The sim function embeds QIO programs into the Prob monad, creating a probability distribution for the results of running a QIO computation.
• runC :: QIO a → a
The runC function is able to run QIO programs that only use the classical subset of QIO, and can return a pure value as the classical subset doesn’t give rise to an effectful model of computation.
• class Qdata a qa | a → qa, qa → a where mkQ :: a → QIO qa
measQ :: qa → QIO a
condQ :: qa → (a → U ) → U letU :: a → (qa → U ) → U
The type class Qdata introduces a way of extending the quantum data types for which a QIO computation can have access. mkQ initialises a quantum data structure, from its classical counterpart, and measQ measures a quan- tum data structure, collapsing it to a single element of the classical coun- terpart. condQ allows a quantum data structure to be used as the control in a conditional unitary, and letU allows the quantum data structure to be introduced as an ancilliary structure in a unitary.
• instance Qdata BoolQbit. . .
instance(Qdata a qa, Qdata b qb)⇒ Qdata (a, b) (qa, qb) ... instanceQdata a qa ⇒ Qdata [a ] [qa ] ...
Example instances of the Qdata class include the correspondence between Booleans and qubits, and any quantum data structures can be extended over pairs and lists.
The Qdata class allows us to overload the operations for each member of the class. As Bool and Qbit combine to form a member of this class, this leaves the underlying mkQbit, measQbit, cond , and ulet constructors looking somewhat
redundant. However, it is quite important to introduce these constructs to show that at the lowest level, any member of the Qdata class must be defined in terms of the individual qubits that make up the quantum data structure.
It has also been noted that the swap operation on qubits looks a little redunant, as unitaries can explicitly reference the qubits on which they act. However, it is left as a construct in QIO as it can be useful for moving qubits about within a larger quantum data structure, without having to then explicitly reference the individual qubits within the data structure when you want to pass the whole thing as an argument.
The following chapter looks at some of the most famous quantum algorithms, as introduced in section 2.6, developed in QIO.
Chapter 6
Quantum algorithms in QIO
This section goes over the implementation of a few of the famous quantum algo- rithms we have seen previously in section 2.6. The algorithms we present imple- mented in QIO include Deutsch’s algorithm, Quantum teleportation, Quantum (or reversible) arithmetic functions, and the Quantum Fourier Transform (QFT). The final section goes on to look at the development in QIO of an implementation of Shor’s algorithm, which makes use of some of the previously defined algorithms.
6.1
Deutsch’s algorithm
Deutsch’s Algorithm ([Deu85]), as we have seen in section 2.6.1, was presented as one of the first and simplest quantum algorithms that could be shown to provide a more efficient solution than its classical counterpart. In Haskell, we can think of the problem as being given a function f :: Bool → Bool, and being asked to calculate whether the given function is balanced or constant.
In the QIO monad the algorithm can easily be modelled: we initialise two qubits in the |+i and |−i states, and then conditionally negate the second qubit depending on the outcome of applying f to the first qubit. The resulting entan- glement describes the properties of f which we wish to find out. Applying the Hadamard rotation to the first qubit, and then measuring it, enables us to extract this information.
deutsch :: (Bool → Bool) → QIO Bool deutsch f = do x ← |+i
y ← |−i
applyU (cond x (λb → if f b then unot y else •)) applyU (uhad x )
measQ x
We can see from the code that the function f only appears to be called once, in the argument to a conditional unitary, and this is indeed the case. The quantum control aspects of the cond construct allow us to have effectively run the function f over a quantum state. In either of the cases where f was a constant function then the measurement will yield False (with probability 1), and in the cases where f is a balanced function the measurement will yield True (again with probability 1).
Evaluating, sim (deutsch ¬) gives [(True, 1.0)], sim (deutsch id) also gives [(True, 1.0)]. sim (deutsch (λx → False)) gives [(False, 1.0)], and sim (deutsch (λx → True)) also gives [(False, 1.0)].
6.2
Quantum Teleportation
Quantum teleportation has been introduced in section 2.6.7, and can be thought of as the transfer of an arbitrary quantum state (in this case a single qubit) using a pair of entangled quantum states (or qubits) and the communication of two classical bits. With-in the QIO monad, we can think of this in three stages. First, before any communication has occurred we must initialise the entangled pair of qubits. Secondly, Alice uses one of these qubits to entangle it with the qubit she wishes to teleport, and produces the classical data from measuring the two qubits now in her possession. The third stage involves Bob receiving the classical mea- surements, and performing the necessary unitary on his qubit from the entangled pair. To model the requirement that Alice and Bob share an entangled pair of
qubits means that the whole process must take part in a single quantum system. In terms of the QIO monad, this means that the whole process must take part with-in a single instance of the QIO monadic structure.
Alice has her initial qubit (aq) and one of the entangled pair of qubits (eq). All she has to do is apply a controlled not between these two qubits, and then perform the Hadamard rotation on the first one. The application of the controlled not rotation is very similar to the share operation previously defined, however, we cannot simply use the share function as we require that the state of the input qubit qa is somehow shared with the already entangled state of the eq qubit. Finally she has to measure these two qubits and send the results of this measurement to Bob. The results of the measurement of these two qubits corresponds exactly to the requirement that two classical bits of data must be sent to Bob, and without this classical information Bob is unable to determine anything about the state of the qubit he has. We implement Alice’s part of the teleportation protocol by the function alice.
alice :: Qbit → Qbit → QIO (Bool, Bool) alice aq eq = do applyU (ifQ aq (unot eq))
applyU (uhad aq) measQ (aq, eq)
Bob will also have a qubit from the entangled pair (eq). He will receive the classical data (cd ) from Alice, and depending upon this data, has to apply the correct unitary (or unitaries) to his entangled qubit eq. We model Bob’s part of the teleportation protocol by the function bob.
uZ :: Qbit → U
uZ qb = (uphase qb 0.5)
bob :: Qbit → (Bool, Bool) → QIO Qbit
bob eq (a, b) = do applyU (if b then (unot eq) else•) applyU (if a then (uZ eq) else•) return eq
In order to actually run the teleportation protocol, we have the requirement that Alice and Bob share an entangled pair of qubits. This entangled pair corre- sponds exactly to the Bell state we defined in QIO previously (bell ::QIO (Qbit, Qbit)). We can now combine the three stages of the teleportation protocol as follows:
teleportation :: Qbit → QIO Qbit teleportation iq = do (eq1 , eq2 )← bell
cd ← alice iq eq1 tq ← bob eq2 cd return tq
The teleportation function takes a qubit as an argument, which is the qubit whose state we wish to teleport. The protocol first initialises the entangled pair, and then Alice uses one of the pair along with the input qubit to create the classical data cd . Bob can then use this classical data, along with the other qubit from the entangled pair to reconstruct the state of the original input qubit.