<<

DEGREE PROJECT IN TECHNOLOGY, FIRST CYCLE, 15 CREDITS STOCKHOLM, SWEDEN 2020

Quantum programming and simulation of the Hubbard model using Q#

HENRIC HINTZE

KTH ROYAL INSTITUTE OF TECHNOLOGY SCHOOL OF ENGINEERING SCIENCES Theoretical Physics

Quantum programming and simulation of the Hubbard model using Q#

Henric Hintze SA114X Degree Project in Engineering Physics, First Level Department of Theoretical Physics Royal Institute of Technology (KTH)

1st June 2020 Abstract

This paper gives a brief introduction to the basic concepts of quantum computing and simulation as well as quantum programming using Q#. These concepts are then applied by implementing the 2D Hubbard model for fermions interacting on a lattice. Eigenstate energies of the model are obtained for small lattices by running the program on a simulated quantum computer and the resulting values are, when verifiable, accurate. Besides this, the implementation process demonstrates the utility of a framework such as Microsoft’s Quantum Development Kit (QDK) when writing quantum algorithms.

Denna uppsats ger en kort introduktion till kvantdatorer, kvantsimulering och kvant- programmering med Q#. Dessa koncept appliceras sedan genom att implementera Hub- bard modellen för fermioner som interagerar i ett gitter i 2D. Modellens egentillståndsen- ergier beräknas genom att köra programmet på en simulerad kvantdator och resultaten stämmer i de fall de går att verifiera. Utöver detta demonstrerar implementationspro- cessen hur användbart ett ramverk såsom Microsoft’s Quantum Development Kit (QDK) är vid skrivandet av kvantalgoritmer. Contents

1 Introduction 2

2 Background 3 2.1 ...... 3 2.2 Quantum gates ...... 4 2.3 Quantum simulation ...... 7 2.4 The Hubbard model ...... 8

3 Investigation 9 3.1 A brief introduction to Q# ...... 9 3.2 Implementation of the Hubbard model using Q# ...... 11 3.3 Results and discussion ...... 12

4 Summary and conclusions 15

Bibliography 16

A Hubbard simulation code 18

1 Chapter 1

Introduction

As with many fields of study, it is difficult to define exactly what started the field of quantum computing, but one significant event in the early stages of its development was the First Conference on the Physics of Computation, held at MIT in May 1981, at which Paul Benioff [1] and Richard Feynman [2] gave talks on quantum computing. Benioff’s talk focused on his earlier work in 1980 which showed that a computer can operate under the laws of quantum mechanics, while Feynman argued that it is impossible to efficiently simulate a quantum system on a classical computer, which led him to formulate a basic model for a quantum computer that could, in theory, be used for this purpose. In the following decades the field slowly started taking off with some significant milestones being the proof of the no-cloning theorem, stating that it is impossible to construct a copy of an unknown quantum state, in 1982 by William Wootters and Wojciech Zurek [3] and independently by Dennis Dieks [4], the first description of a universal quantum computer by David Deutsch in 1985 [5], the formulations of Shor [6] and Grover’s [7] algorithms in 1994 and 1996 respectively and more recently the realisations of quantum computers with upwards of 50 qubits by companies like Google and IBM [8]. With the advent of increasingly more effective implementations of quantum com- puters, a need for the tools necessary to efficiently program them as well as people capable of using these tools arises. One effort to make quantum programming accessible and to engage people in the field is Microsoft’s Quantum Development Kit (QDK) [9], which comprises tools to build quantum programs with the programming language Q#, designed specifically to program quantum computers. This paper aims to explore the basics of quantum programming using the QDK, first exploring some of the theoretical background of quantum computing and simulation, then implementing a simple example quantum algorithm and finally simulating the 2D Hubbard model for fermions interacting on a lattice.

2 Chapter 2

Background

To begin, let us briefly summarise what quantum computers are, how they work and why they are worth developing in the first place. The fundamental concept of a quantum computer is the so-called . Whereas a classical computer uses classical bits which take on binary values of either 0 or 1 to store information, a quantum computer instead uses qubits which draw on the quantum mechanical principles of superposition and en- tanglement to create states which are in superposition of 0 and 1, meaning that instead of strictly having the value 0 or 1 they instead have a certain probability to yield either 0 or 1 when measured. These qubits can be physically implemented in a variety of differ- ent ways using for example electron spin, trapped ions, superconductivity or topological qubits; however, the details of this are beyond the scope of this paper. The reason quantum computers are deemed worth the effort of overcoming the dif- ficulties of physically realizing them, is that there are certain types of problems which would take millennia to solve using our most powerful classical computers, but which a reasonably powerful quantum computer could solve in mere minutes. Broadly speaking, there are three classes of problems which a quantum computer is better equipped to solve than a classical one. The first of these is the class of problems which can be solved using the quantum version of the Fourier transform. Most famous in this class is the problem of factoring large numbers, which can be done efficiently using Shor’s algorithm, yielding an exponential speed-up over classical computers. Secondly, there are search problems, most famously approached by Grover’s algorithm, which gives a quadratic speed-up over classical computers. Finally there are simulation problems dealing with the simulation of quantum mechanical systems. Throughout this paper, the main sources used were the Microsoft Quantum Docu- mentation [9], the book ’Quantum Computation and ’ by Nielsen and Chuang [14] and the paper ’Strategies for solving the Fermi-Hubbard model on near- term quantum computer’ by Chris Cade, Lana Mineh, Ashley Montanaro and Stasja Stanisic [15].

2.1 Qubits

Linear algebra is often considered to be the language of quantum mechanics, and since quantum computers function using quantum mechanical systems, that same language can be used to describe operations in quantum computing. The fundamental unit of quantum computing, the qubit, can be represented by a complex vector of size 2 and

3 magnitude 1: α . β Here α and β are the amplitudes of states 0 and 1, meaning |α|2 and |β|2 are the prob- abilities of getting 0 and 1 respectively when measuring the qubit. This can also be written as a linear combination of orthonormal basis states, where a qubit in state 0 is represented by 1 0 and a qubit in state 1 by 0 1 yielding α 1 0 = α + β . β 0 1 Instead of having to write out all of the vectors, it is common to use Dirac notation where the basis states are represented by the following kets:

1 0 |0i = |1i = . 0 1

A qubit in a general state can now be written as

|ψi = α |0i + β |1i .

The below ket symbols have the following generally accepted uses: 1 1 |+i = √ (|0i + |1i) |−i = √ (|0i − |1i) 2 2 1 1 |ii = √ (|0i + i |1i) |−ii = √ (|0i − i |1i). 2 2 2.2 Quantum gates

Now that the basics of a qubit have been introduced, the next step is to examine how its state can be altered. This is done using so-called quantum gates, which are the quantum equivalents of classical logic gates. Mathematically, applying a quantum gate to a qubit is done by multiplying the qubit state vector with a 2 × 2 unitary matrix. The simplest example of a quantum gate is the Pauli-X gate which simply switches the basis state coefficients of the qubit with each other. The matrix representation of the gate is

0 1 X = . 1 0

Applying this to a qubit in a general state would therefore look as follows:

0 1 α β X |ψi = = = β |0i + α |1i . 1 0 β α

4 Quantum gates can also be written using Dirac notation. In the case of the Pauli-X gate one gets 0 1 0 0 1 0 X = + = 0 1 + 1 0 = |0i h1| + |1i h0| . 0 0 1 0 0 1 Application to a qubit now looks as follows: X |ψi = (|0i h1| + |1i h0|)(α |0i + β |1i) = α(|0i h1|0i + |1i h0|0i) + β(|0i h1|1i + |1i h0|1i) = α(|0i · 0 + |1i · 1) + β(|0i · 1 + |1i · 0) = α |1i + β |0i . The other two Pauli gates, the Y and Z gates are given by 0 −i 1 0  Y = Z = . i 0 0 −1 Another important gate is the Hadamard gate, which can be used to put a qubit in the |0i state into an even superposition of |0i and |1i: 1 1 1  1 H = √ H |0i = √ (|0i + |1i) = |+i . 2 1 −1 2 The generalisation from single-qubit systems to multiple-qubit systems is fairly straight- forward. A system of N qubits is represented by a complex vector of size 2N . In the case of a two-qubit system one gets   x0 x1   . x2 x3 As in the case of the single-qubit system, this can also be written in terms of basis states:           x0 1 0 0 0 x1 0 1 0 0   = x0   + x1   + x2   + x3   . x2 0 0 1 0 x3 0 0 0 1 These basis states can be generated by taking the tensor product of pairs of the single- qubit basis states: 1 0 0 0                 0 1 1 1 1 0 0 0 1 0 0 0   = ⊗   = ⊗   = ⊗   = ⊗ . 0 0 0 0 0 1 1 1 0 0 1 1 0 0 0 1 Using this fact, it is once again possible to write everything in Dirac notation:   x0 x1   = x0 |0i ⊗ |0i + x1 |0i ⊗ |1i + x2 |1i ⊗ |0i + x3 |1i ⊗ |1i . x2 x3

5 To simplify further, tensor products of basis states are by convention written as

|i0i ⊗ |i1i ⊗ · · · ⊗ |ini = |i0i1...ini .

This yields   x0 x1   = x0 |00i + x1 |01i + x2 |10i + x3 |11i . x2 x3 In a similar fashion, quantum gates are generalised to multiple-qubit systems as 2N × 2N matrices. When applying single-qubit gates to qubits in a multiple-qubit system, the requisite multiple-qubit gate can be constructed by taking the tensor product of the single-qubit gates which are to be applied to the different qubits in the system. If for example one had a two-qubit system and wanted to apply the Pauli-X gate to the first and do nothing to the second, one could do this by taking the tensor product of the Pauli-X gate and the Identity gate and then multiplying it with the system state vector:

0 0 1 0     0 1 1 0 0 0 0 1 X ⊗ I = ⊗ =   1 0 0 1 1 0 0 0 0 1 0 0

|ψi = x0 |00i + x1 |01i + x2 |10i + x3 |11i       0 0 1 0 x0 x2 0 0 0 1 x1 x3 X ⊗ I |ψi =     =   = x2 |00i + x3 |01i + x0 |10i + x1 |11i . 1 0 0 0 x2 x0 0 1 0 0 x3 x1 The controlled NOT gate, or CNOT gate, is an example of a proper multiple-qubit gate that cannot be separated into single-qubit gates. The CNOT gate is a two-qubit gate where the first qubit is referred to as the control qubit and the second as the target qubit. Its function is to apply the X gate to the target qubit if and only if the control qubit is in state |1i. Its matrix is given by

1 0 0 0 0 1 0 0   . 0 0 0 1 0 0 1 0

Application to a general two-qubit system gives

CNOT |ψi = x0 |00i + x1 |01i + x3 |10i + x2 |11i .

Finally, let us have a brief look at the concept of entanglement. Entanglement is a quantum mechanical phenomenon where two particles, in our case qubits, become linked in an inseparable fashion so that they must always be considered part of a larger system. Mathematically, this corresponds to it not being possible to factor the state vector of the system into a tensor product of individual qubit states. This results in the measurement of one of the qubits in an entangled system altering the state of the other qubits, revealing

6 information about their state. One way to create such a state is by using the CNOT gate. 1 1 |ψi = √ |00i + √ |10i 2 2 1 1 CNOT |ψi = √ |00i + √ |11i 2 2 This state cannot be factored and measuring one of the qubits guarantees that the other qubit will be in the same state as the result of that measurement. This must be so since the probability amplitudes of the mixed states are 0, and therefore the qubits cannot be found to be in different states: 1 1 CNOT |ψi = √ |00i + 0 · |01i + 0 · |10i + √ |11i . 2 2 Entanglement is one of the core concepts utilised in many quantum algorithms.

2.3 Quantum simulation

The simulation of physical systems is generally concerned with examining how an initial state of a system evolves through time and space. This evolution is usually described by a system of differential equations which often is either impossible or impractical to solve analytically. Instead, the equations are discretised, and the initial state is iteratively updated until the desired final conditions are met. In the case of quantum mechanical systems, the equation used to describe their evolution is often the Schrödinger equation: d i |ψi = H |ψi . ~dt Classical computer simulations of quantum mechanical systems are possible, but generally very inefficient, since the number of equations in the differential equation system grows exponentially with the size of the system. Quantum computers overcome this inefficiency through the use of the quantum mechanical properties of qubits. For a time-independent Hamiltonian H, the solution to the Schrödinger equation is given by |ψ(t)i = e−iHt |ψ(0)i . Unfortunately, since H is exponentially large, it is generally infeasible to simply expo- nentiate it, however, for many physical systems the Hamiltonian H can be written as a sum of local Hamiltonians Hk, which are easier to exponentiate since they only act on a −iHt Q −iHkt small subsystem. Since Hk and Hj do not commute in general, e 6= k e and instead the Trotter formula, which gives the following relation for Hermitian operators A and B, must be used: lim (eiAt/neiBt/n)n = ei(A+B)t. n→∞ Discretisation gives ei(A+B)∆t = eiA∆teiB∆t + O(∆t2). It is worth noting that it is also possible to construct higher order approximations. Each iterative update of the system state can therefore be implemented as [14]

P −i Hk∆t Y −iHk∆t |ψj+1i = e k |ψji ≈ e |ψji . k

7 2.4 The Hubbard model

The Hubbard model is a simple model describing fermions interacting on a lattice under a Hamiltonian comprising only two terms. The first is a kinetic hopping term represent- ing tunneling of particles between adjacent sites and the second is an on-site potential term. Since wave functions fall off exponentially with distance, the restriction to hopping between adjacent sites is a reasonable simplification. It is also reasonable to model solids with only one energy band at the Fermi surface as a lattice with only one energy level at each site. Due to the Pauli principle, each site in the lattice can then contain at most two fermions at once, one with spin-up and one with spin-down. The on-site term models the repulsion between the fermions at a site if it is doubly occupied. Since the repulsion force between the fermions in the lattice is screened, only the repulsion between fermions at the same site is accounted for by the model. The Hubbard Hamiltonian can be written as X † † X H = −t (aiσajσ + ajσaiσ) + U nk↑nk↓. i∼j,σ k † † Here aiσ, aiσ are the fermionic creation and annihilation operators, nkσ = akσakσ, i ∼ j indicates that the sites i and j are adjacent in the lattice, σ ∈ {↑, ↓}, t is the tunneling amplitude and U is the Coulomb potential. The Hubbard model is very useful in the study of the insulating, magnetic and superconducting properties of strongly correlated solids. At half-filling, i.e. when the number of fermions equals the number of sites, the ground state is antiferromagnetic, but when doped away from half-filling, the Hubbard model has been proposed to describe superconductivity in the high-Tc cuprates. [15, 16].

8 Chapter 3

Investigation

3.1 A brief introduction to Q#

Q# is a programming language developed and released by Microsoft as part of their Quantum Development Kit (QDK). The language is specifically designed for program- ming quantum computer algorithms and aims to make this process as seamless as pos- sible. In Microsoft’s words, ’it provides quantum programmers a framework that allows you to focus on the algorithms without having to worry about technical details like gate sequence optimization or the physical implementation of a quantum computer.’ [10] Q# works in conjunction with a classical programming language such as Microsoft’s C# or Python. This language is used to create a host program which is executed on a classical computer. The host program then offloads certain operations to the quantum computer, which acts as a co-processor similar to how many programs nowadays offload certain operations to a GPU. The language is related to C# and F# and its syntax is excellently summarised in the Q# Language Quick Reference, provided by Microsoft in their Quantum Documenta- tion [11]. To an individual experienced with classical programming, most features of Q# will seem familiar. Functions, loops and conditionals work very similarly. The first of the main differences is that Q# has two types of functions called functions and operations. Functions execute classical code while quantum code must be contained in an operation. Secondly, Q# introduces a special type called the Qubit, used to allocate physical qubits, as well as a measurement operation M(), which returns either Zero or One when applied to a variable of type Qubit. Finally, it provides a multitude of single and multiple-qubit gates which can be used to manipulate such variables. Besides this, it also comes with a range of libraries to ease the creation of more complex algorithms. Since physical quantum computers are still scarce, Q# comes with the ability to ex- ecute code on a simulated quantum computer. On a typical user machine, this simulator is limited to around 32 qubits, whereas using Microsoft’s cloud computing service Azure allows for programs requiring up to 40 qubits to be run. Probably the simplest application of quantum computing is the creation of truly ran- dom numbers. When random numbers are created in most classical programs, for example using Python’s random module, they are not truly random but pseudo-random. This means that given a certain starting number, called a seed, the sequence of numbers gen- erated is actually completely deterministic, despite appearing random to an unknowing observer. It is surprisingly difficult to create truly random numbers in the deterministic

9 environment of a classical computer; however, using the fundamental quantum random- ness of qubits, it becomes very simple. Below is some basic code implementing a python function called qrandom(), which just like the python function random.random() returns a random float in the range [0.0, 1.0), with the difference being that qrandom() uses quantum computing to create truly random floats whereas random.random() generates pseudo-random floats. The Q# code (code snippet 3.1) is very simple. An operation called RandBit() is defined, which simply allocates a qubit (when allocated, qubits are always in the |0i state), then uses the Hadamard (H) gate to put it into the |+i state. In this state the qubit will have equal probabilities of returning Zero or One when measured. After this the qubit is simply measured and the result is stored in the variable x. At the end of an operation, Q# requires that all qubits are returned to the |0i state, which is easily accomplished using the provided Reset() function. Finally, the x variable is returned. The Python function qrandom() (code snippet 3.2) calls RandBit() repeatedly and then uses the random bits to create a float, which is then returned.

1 namespace QRand { 2 open Microsoft.Quantum.Intrinsic; 3 4 operation RandBit() : Result { 5 using (q= Qubit()){// Allocatesa qubit in state |0> 6 H(q);// Alters qubit state to |+> 7 // Qubit now hasa 50% chance of returning0 or1 when ,→ measured 8 let x = M(q);// Measures the qubit and stores result in variablex 9 Reset (q);// Resets qubit state to |0> 10 return x; 11 } 12 } 13 } Code snippet 3.1: qrandom.qs

1 import qsharp 2 from QRand import RandBit 3 import struct 4 5 def main(): 6 print(qrandom()) 7 8 def qrandom(): 9 """ Returnsa randomly generated float in range [0, 1) using quantum randomness.""" 10 bits = [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]# Generates prefix forIEEE 754 binary64 ,→ in range [0.5, 1) 11 fori in range(52): 12 bits.append(RandBit.simulate())# Generates remaining bits randomly 13 bitString =’’.join(str(i) fori in bits)# Converts array to string 14 rand = bitStrToFloat(bitString)# Converts bit string to float 15 16 if RandBit.simulate() == 1: 17 rand = 0.9999999999999999 - rand# 50% chance of changing range from [0.5, ,→ 1) to [0, 0.5) 18 19 return rand 20

10 21 def bitStrToFloat(bitString): 22 """ Converts bit string to float.""" 23 bytes = int(bitString, 2).to_bytes(8,’big’)# Converts bit string to the8 bytes of ,→ anIEEE 754 binary64 24 return struct.unpack(’>d’, bytes)[0]# Converts bytes to python float 25 26 if __name__ ==’__main__’: 27 main () Code snippet 3.2: qrandom.py

3.2 Implementation of the Hubbard model using Q#

In this section the process of implementing the Hubbard model using Q# is described. The goal was to estimate the energy eigenstates of a half-full 2D lattice with n × m sites. In the initial state of the system, there were spin-up fermions at every second site and spin-down fermions at the others. The lattice was represented using 2 qubits per site, one for spin-up and the other for spin-down. Therefore, an array of 2nm qubits were needed to represent the system as a whole. The sites of the lattice were mapped to the positions in the array of qubits in accordance with the snake-shape shown in figure 3.1. There were two main steps to the process of estimating the energy eigenstates of the system, the first being to encode the fermionic Hubbard Hamiltonian presented above as a qubit Hamiltonian by mapping the creation and annihilation operators to qubit operations. For this, the Jordan-Wigner encoding was used to translate the anticommut- ing fermionic creation and annihilation operators into spin degrees of freedom which for qubits with indices i and j, where i < j denote the sites along the snake-shaped chain, gives 1 a†a + a†a ⇒ (X X + Y Y )Z ...Z i j j i 2 i j i j i+1 j−1 1 n n ⇒ (I − Z )(I − Z ). i j 4 i j These mappings were used to express the Hamiltonian in terms of Pauli operators, the first for the hopping terms and the second for the on-site terms [15]. The second step was to actually estimate the energy eigenstates. In this paper the method used for this

Figure 3.1: A visualisation of how the sites in a 4x3 lattice are mapped to 24 qubits in an array where different colours represent different spins.

11 was robust phase estimation. This method relies on letting the input system evolve under the Schrödinger equation with the Hubbard Hamiltonian, in this case using the Trotter-Suzuki integrator for the actual time evolution:

|ψ(t + ∆t)i = e−iH∆t |ψ(t)i .

When the system reaches an energy eigenstate the energy is calculated as E = φ/∆t, where φ is the global phase of the qubit system. The global phase of a qubit system is of course unobservable, but using robust phase estimation it can be estimated. Since the result of the quantum energy estimation algorithm is probabilistic in nature, the host program had to call it multiple times in order to provide a clear picture. An example python host program, which passes the necessary input parameters to a Q# function, which in turn returns an energy estimate, is provided in the appendix (A.1). This is repeated multiple times after which the resulting energy estimates are plotted in a histogram. The Q# algorithm, which can also be found in the appendix (A.2), relies on the library functions RobustPhaseEstimation [12] and DecomposedIntoTimeStepsCA [13], which implement phase estimation and Trotter-Suzuki integration. The key bits of code pertaining to the Hubbard model specifically are the three operations on lines 60-77 which apply the different terms of the Hamiltonian to the system based on the Jordan-Wigner encoding mentioned above.

3.3 Results and discussion

On an average machine the quantum computer simulator provided with Q# can manage up to around 30 qubits and therefore the largest lattice that could be simulated was a 4 × 3 lattice. Below, eigenstate energy histograms for some inputs are presented in figure 3.2 and some approximate ground state energies are summarised in table 3.1. The histograms display the energies returned from 100 runs of the simulation algorithm for different grid sizes and values of the constants t and U. Each cluster corresponds to an eigenstate energy. In all trials the time-step was set to 0.1, the order of the integrator to 1 and the bit-precision of the phase estimation to 7. The ground state energies in the table were normalised by dividing them by t in order to facilitate comparison. The resulting ground state energies for√ the 2 × 1 grid match the exact solutions one 1 2 2 gets by using the formula E = 2 [U − U + 16t ] [17]. For other grid sizes, it was significantly more difficult to find reliable figures to compare with, however, the results for the 2 × 2 grid match the variational calculation values presented in [18]. Until the program can be run on an actual quantum computer capable of simulating large grids, the simulation results are not of particular interest, however, they do serve as a proof of concept.

12 U/t 2 × 1 4 × 1 2 × 2 3 × 3 0 -2.0 -4.5 -4.0 -4.2 0.5 -1.8 -3.9 -3.9 -5.3 2.5 -1.1 -2.5 -3.6 -5.2 10 -0.4 -0.7 -3.1 -4.7

Table 3.1: Ground state energies E/t

13 Figure 3.2: Eigenstate energy histograms

14 Chapter 4

Summary and conclusions

This paper has given an introduction to quantum computing and simulation, in particular using Microsoft’s QDK. The main part of the paper focused on the implementation of the 2D Hubbard model for simulating fermions interacting on a lattice. After a Q# program implementing the aforementioned model had been constructed, it was run on a simulated quantum computer for some small lattices. Only a few of the eigenstate energies output by the simulations could be compared to values from other sources, yet all seem reasonable, and the implementation process demonstrated the utility of a framework such as Microsoft’s QDK for writing quantum algorithms. Once quantum computers become more efficient and accessible, it will be interesting to run this or similar simulations on them in order to reveal more information about the behaviour of quantum mechanical systems.

15 Bibliography

[1] P. A. Benioff, ’Quantum mechanical Hamiltonian models of discrete processes that erase their own histories: Application to Turing machines,’ Int J Theor Phys, vol. 21, pp. 177–201 (1982).

[2] R. P. Feynman, ’Simulating Physics with Computers,’ Int J Theor Phys, vol. 21, pp. 467–488 (1982).

[3] W. Wootters and W. Zurek, ’A single quantum cannot be cloned,’ Nature, vol. 299, pp. 802-803 (1982).

[4] D. Dieks, ’Communication by EPR devices,’ Phys. Lett. A, vol. 92, pp. 271-272 (1982).

[5] D. Deutsch, ’Quantum theory, the Church-Turing principle and the universal quantum computer,’ Proceedings of the Royal Society, vol. 400, pp. 97-117 (1985).

[6] P. W. Shor, ’Algorithms for quantum computation: discrete logarithms and factor- ing,’ Proceedings 35th Annual Symposium on Foundations of Computer Science, pp. 124-134 (1994).

[7] L. K. Grover, ’A fast quantum mechanical algorithm for database search,’ arXiv e-prints (1996). https://arxiv.org/abs/quant-ph/9605043 (accessed 2020).

[8] E. Pednault, J. Gunnels, D. Maslov and J. Gambetta, ’On "Quantum Su- premacy",’ ibm.com (2019). https://www.ibm.com/blogs/research/2019/10/ on-quantum-supremacy/ (accessed 2020).

[9] Microsoft, ’Microsoft Quantum Documentation,’ Microsoft Docs. https://docs. microsoft.com/en-us/quantum/ (accessed 2020).

[10] Microsoft, ’What are the Q# programming language and QDK?’ Mi- crosoft Docs. https://docs.microsoft.com/en-us/quantum/overview/ what-is-qsharp-and-qdk (accessed 2020).

[11] Microsoft, ’Q# Language Quick Reference,’ Github. https://github.com/ microsoft/QuantumKatas/tree/master/quickref (accessed 2020).

[12] Microsoft, ’RobustPhaseEstimation,’ Microsoft Docs. https://docs.microsoft. com/en-gb/qsharp/api/qsharp/microsoft.quantum.characterization. robustphaseestimation (accessed 2020).

16 [13] Microsoft, ’DecomposedIntoTimeStepsCA,’ Microsoft Docs. https: //docs.microsoft.com/en-us/qsharp/api/qsharp/microsoft.quantum. canon.decomposedintotimestepsca (accessed 2020).

[14] M. A. Nielsen and I. L. Chuang, Quantum Computation and Quantum Information. Cambridge University Press (2000).

[15] C. Cade, L. Mineh, A. Montanaro and S. Stanisic, ’Strategies for solving the Fermi- Hubbard model on near-term quantum computers,’ arXiv e-prints (2019). https: //arxiv.org/abs/1912.06007 (accessed 2020).

[16] R. T. Scalettar, ’An Introduction to the Hubbard Hamiltonian,’ Department of Physics, University of California (2016). https://www.cond-mat.de/events/ correl16/manuscripts/scalettar.pdf (accessed 2020).

[17] M. Berciu, ’Electronic Hamiltonians - Part III,’ Department of Physics and Astronomy, University of British Columbia (2019). https://www.phas.ubc.ca/ ~berciu/TEACHING/PHYS502/NOTES/se.pdf (accessed 2020).

[18] O. R. Okanigbuan, J. O. A. Idiodi, P. N. Okanigbuan, ’Ground State Energy of the Two-dimensional Hubbard Model at Low Filling,’ Journal of Applied Sciences Research, vol. 6, pp. 909-912 (2010).

17 Appendix A

Hubbard simulation code

1 import qsharp 2 from Hubbard import EstimateEigenstateEnergy 3 import matplotlib.pyplot as plt 4 5 show = False 6 7 n = 100 8 bins = 100 9 xDim = 2 10 yDim = 2 11 t = 1.0 12 U = 0.0 13 stepSize = 0.1 14 trotterOrder = 1 15 bitPrecision = 7 16 17 paramStr = str(xDim) +"x"+ str(yDim) +",t="+ str(t) +",U="+ str(U) +", step="+ ,→ str(stepSize) +", order="+ str(trotterOrder) +", bitPrec="+ str(bitPrecision) + ,→ ",n="+ str(n) 18 19 x = [] 20 fori in range(n): 21 energy = EstimateEigenstateEnergy.simulate(xDim=xDim, yDim=yDim, t=t, U=U, stepSize= ,→ stepSize, trotterOrder=trotterOrder, bitPrecision=bitPrecision) 22 x.append(energy) 23 print(i,"/", n," Energy:", energy) 24 25 print("Energy error from phase estimation:", 2**(-bitPrecision)/stepSize) 26 plt.hist(x, bins = bins) 27 plt.title(paramStr) 28 29 if show == True: 30 plt . show () 31 else: 32 plt.savefig(paramStr +".png") Code snippet A.1: hubbard.py

1 namespace Hubbard { 2 open Microsoft.Quantum.Intrinsic; 3 open Microsoft.Quantum.Arrays; 4 open Microsoft.Quantum.Characterization;

18 5 open Microsoft.Quantum.Oracles; 6 open Microsoft.Quantum.Canon; 7 8 function Void() : Unit{ 9 } 10 11 // Estimates the eigenstate energy using the2D Hubbard-Hamiltonian in the half-full ,→ case 12 operation EstimateEigenstateEnergy((xDim : Int, yDim : Int), t : Double, 13 U : Double, stepSize : Double, 14 trotterOrder : Int, 15 bitPrecision : Int) : Double { 16 let nSites = xDim*yDim; 17 18 // Gets the hubbard terms 19 let hubbardTerms = GetHubbardTerm(nSites, xDim, t, U, _, _, _); 20 // Combines hubbard term application into one operation using the Trotter-Suzuki ,→ integrator 21 let trotterIntegrator = DecomposedIntoTimeStepsCA((5*nSites, hubbardTerms), ,→ trotterOrder); 22 // Converts trotterIntegrator unitary operation to DiscreteOracle type 23 let oracle = OracleToDiscrete(trotterIntegrator(stepSize, _)); 24 25 using (qubits = Qubit[2*nSites]) { 26 // Each site is represented bya pair of qubits, one for spin-up 27 // and one for spin-down. The qubits can be imagined as being 28 // arranged ina so called snake-shape, where the first half of 29 // the qubits represents the spin-up modes from site1 ton and the 30 // second half represents the spin-down modes from siten to 1. 31 32 // Initialises the anti-ferromagnetic, half-full state 33 for ((i, qubit) in Enumerated(qubits)) { 34 if (i%2 == 0) {X(qubit);} 35 } 36 37 // Estimates the phase of the operator which equals the energy times the step ,→ size 38 let energy = RobustPhaseEstimation(bitPrecision, oracle, qubits)/stepSize; 39 ResetAll(qubits); 40 return energy; 41 } 42 } 43 44 operation GetHubbardTerm(nSites : Int, xDim : Int, t : Double, U : Double, i : Int, 45 stepSize : Double, qubits : Qubit[]): Unit is Adj + Ctl { 46 // Onsite terms 47 if (i < nSites) { 48 GetHubbardOnsiteTerm(nSites, i, U*stepSize, qubits); 49 } 50 // Array-adjacent hopping terms 51 elif (i < 3*nSites - 1 && i != 2*nSites - 1) { 52 GetHubbardArrayAdjacentHoppingTerm(nSites, xDim, i - nSites, t*stepSize, ,→ qubits ); 53 } 54 // Non-array-adjacent hopping terms 55 elif (i >= 3*nSites && (i - 3*nSites + 1) % xDim != 0 && (i < 5*nSites - xDim && ,→ (i < 4*nSites - xDim || i >= 4*nSites))) {

19 56 GetHubbardNonArrayAdjacentHoppingTerm(nSites, xDim, i - 3*nSites, t*stepSize, ,→ qubits ); 57 } 58 } 59 60 operation GetHubbardOnsiteTerm(nSites : Int, i : Int, c : Double, qubits : Qubit[]): ,→ Unit is Adj + Ctl { 61 Exp([PauliI], 0.25*c, [qubits[i]]); 62 Exp([PauliZ], -0.25*c, [qubits[i]]); 63 Exp([PauliZ], -0.25*c, [qubits[2*nSites - 1 - i]]); 64 Exp([PauliZ, PauliZ], 0.25*c, [qubits[i], qubits[2*nSites - 1 - i]]); 65 } 66 67 operation GetHubbardArrayAdjacentHoppingTerm(nSites : Int, xDim : Int, i : Int, c : ,→ Double, qubits : Qubit[]): Unit is Adj + Ctl { 68 let target = qubits[i .. i + 1]; 69 Exp([PauliX, PauliX], -0.5*c, target); 70 Exp([PauliY, PauliY], -0.5*c, target); 71 } 72 73 operation GetHubbardNonArrayAdjacentHoppingTerm(nSites : Int, xDim : Int, i : Int, c ,→ : Double, qubits : Qubit[]): Unit is Adj + Ctl { 74 let target = qubits[i .. i + 2*xDim - 1 - 2*(i % xDim)]; 75 Exp(GetPauliString(Length(target), PauliX), -0.5*c, target); 76 Exp(GetPauliString(Length(target), PauliY), -0.5*c, target); 77 } 78 79 function GetPauliString (length : Int, direction : Pauli) : Pauli[] { 80 mutable pauli = ConstantArray(length, PauliZ); 81 set pauli w/= 0 <- direction; 82 set pauli w/= length - 1 <- direction; 83 return pauli; 84 } 85 } Code snippet A.2: hubbard.qs

20 www.kth.se