 So good morning, everyone. Thank you very much, Sebastiano, for the introduction. So it's a pleasure to be here. And my task in the following four lectures lots is basically to introduce you to the technique called an exact diagonalization. So as you can see from the title slide, so it will be basically about solving the Schrodinger equation in various forms for various quantum antibody systems. And I will tell you a lot of different aspects to it. And also what you can actually do with this technique, which might be interesting for many applications. So the plan of the four slots of the lectures is to start now and give you some first introduction to exact diagonalization and explaining basically what are the ingredients? What is the technique about? What can you roughly do with it? Just in an overview sense and then start understanding different parts of what you actually need to understand in order to write a performant exact diagonalization code yourself. Then in the second part today, I will continue. And then the symmetries of a Hamiltonian which you want to use in your exact diagonalization approach will be important. And also with exact diagonalization you can also do real-time and frequency dynamics using some clever approaches. And that's also something I'm going to explain to you. Then the lecture tomorrow in the morning is a bit more about physics application. And so something that what exact diagonalization is quite good at is actually to do a spectroscopy, meaning resolving spectra of quantum antibody systems by various quantum numbers, which are connected to symmetries, for example. So for example, you can plot many body spectra as a function of momentum, which is the quantum number related to translation symmetries, for example, and similarly for lattice rotations or for SU2 symmetry, which some of the quantum spin models have. And actually it turns out that this is not just a useful thing to just get benchmark energies to compare to other methods. But there's actually a lot of physical understanding you can extract from just looking at the many body energy spectrum. And that would basically show you in a few applications what you can actually do with that. And sometimes this allows you to understand things which are hard to grasp using other methods, even though they might work on substantially larger systems. But you actually get a rather complete account of the energy spectrum of a quantum antibody system is something which I think only exact diagonalization is able to do on an efficient level. And then tomorrow in the afternoon we'll have some tutorial session, which might be a combination of doing an alps application where there is an exact diagonalization code as you probably have heard. So you can either do that or we can implement some time dependent simulation using some Krilov techniques, which is also a nice illustration of what you can do using some of these linear algebra tricks. So in contrast to many other methods, the main idea of exact diagonalization is rather quickly told. It's basically you want to solve the Schrodinger equation of a quantum antibody system numerically. So that's the Schrodinger equation here, the stationary one, time independent one. And you just try to do that as efficiently and as fast as possible. And so what we will see in many applications, if you choose a clever basis of your quantum antibody system, then actually the matrix H is sparse, which means it does not have a substantial number of entries per column or per row. But for quantum antibody systems, the Hilbert space, the vector space, which would work in growth exponentially with the number of spins or qubits or whatever. I mean, I guess this is well known, but this is a serious limitation at first. And so some people actually think that exact diagonalization can be limited to that. You're just doing energy kind of calculating some eigenvalues. But there's a lot more to the technique in order to really make it into a powerful tool. It's not just a benchmark technique, but a useful technique for its own sake. Yeah, because you can get a lot of physical information out of a finite system. And in the end, actually, since you also have access to the full wave function and several wave functions like in the low-lying energy spectrum, it's really quite a useful quantum mechanics toolbox because you can really do a lot of kind of numerical experiments and extract a lot of information from your finite system. And for many applications, not for all of them, but for many applications, it's actually you can extract kind of relevant physics already from the available system sizes. There are also problems which are too challenging, but still there is a lot to be done even nowadays. So as a kind of a first glimpse of how a vector space or the Hilbert space really grows, let us look at a simple quantum antibody system, a spin system here on some lattice with two basis states per site. I'd say spin up or spin down. And then as you know, the Hilbert space overall without any constraints grows exponentially. So 2 to the n in that case, where n is the number of sites. And then if you do the calculation, so if you have 10 spins, which is already some number, the Hilbert space amounts to roughly 1,000 states. And if you go to 20 spins, it's a million. And then for 30 spins, it's about a billion and so on. For example, for 50, you already are 10 to the whatever 15. So it's really a huge Hilbert space. And the question is, yeah, what can you actually do? So where are the current kind of records? And actually using symmetry reductions, as we will talk about, and also using parallelization techniques to actually do these iterations of your matrix vector multiplications on the highly parallel machine, we're actually currently able to go close to 50 spins. So we can actually solve subspaces or assemble the Hilbert space by working in subspaces. So we get information of ground states of a problem with about 50 spins. So 48 spins have been done. And I think 50 or 52 spins are within reach for certain problems. So that tells you roughly where one could can go. And I mean, 50 spins, that's something where 20 years ago, people were doing or quantum Monte Carlo for that. So now you can do ED. But OK, it's in the exponentially scaling method. So we cannot expect to do 100 spins in the near future either. But still, there is progress on that side as well. And so basically, we would like to know the quantum mechanical wave function. And that is a vector in this Hilbert space. And we would like to know the ground state of a certain Hamiltonian, and perhaps a few of the low lying eigenstates. So that's one field of application is somehow to go for ground states or low energy properties. In that case, you're obviously only interested in the ground state and a few excited state. That gives you access to the low temperature physics. In some other cases, you're actually interested to do thermodynamics. Or if you're interested, say, in many body localization, you also want to know a lot of information about states anywhere in the spectrum or at some finite energy density. Then we're not able to actually completely diagonalize a problem of that size. So if you're interested in 50, around 50 spins, we can only calculate the absolute ground states and the few of the excited state. But it's not possible to calculate all eigenstates. So that's not the case. If you're interested in calculating all the eigenstates or some eigenstates in the middle of the spectrum, you roughly have to divide the system size in number of particles or sites by two. So if you want to do full diagonalization or some more elaborate technique to target states in the inner of the spectrum, you roughly are, say, between 20 and 30 spins, but more on the 20 side to give you an idea if you want to have everything, all eigenfunctions or the complete spectrum. Then might be interesting also as a motivation to see a bit in what fields exact dinization is applied. So one field where it plays an important role still is our quantum magnets, mostly spin-a-half magnets, but also magnets with larger spin. And the reason is that, as we have seen, that's basically since they have one of the smaller Hilbert spaces per site, it's actually possible to do the largest systems in terms of lattice sites in the end. And then what one can do quite well using exact dinization is to characterize the nature of novel phases. Some things, for example, spin-ematic phases, which some time ago have not been really appreciated, but these are phases which you can diagnose very well using exact diagonalization. Actually, because of its spectral properties, that you can calculate energy spectra quite efficiently, you can also learn a lot about critical points in one dimension, so some of the quantum critical points have conformal symmetry, and then there is a lot of field theory knowledge about that, and then you can actually see a lot of these appearing in energy spectra of finite size system. So ED has been very useful in obtaining that. And also they have some track record in doing calculating dynamical correlation functions in one and two spatial dimensions. By dynamical correlation functions, I mean something like a dynamical spin structure factor or some photo emission spectral function, which is closely related to experimental techniques, such as inelastic neutrons scattering or photo emission. And I will tell you a little bit how to calculate these also using some linear algebra tricks. Then some other field of application are fermionic models, so that is our Harvard or T.J. models, where you can calculate the gap-sparing properties and also correlation exponents in one dimension. They also have been used a lot in the field of the fraction quantum Hall effect, so some topological state of matter, which where the Hamiltonian is slightly more complicated than just a spin Hamiltonian, because it typically has four-body interactions or six-body interactions for some model Hamiltonians. And then using these techniques, you can again calculate energy-related properties like energy gaps, but you can also calculate overlaps with model states, which play an important role in that field and also entanglement spectra. So these are spectra of reduced density matrices of wave functions they can be obtained using this technique. Then also something which is popular are so-called constrained models, where the Hilbert space is not just a plane, a tensor product of a local Hilbert space, so to say the spin chains I have told you about briefly, that there the Hilbert space is a tensor product of a spin and a half, degrees of freedom, but sometimes you're interested in what we call constrained models, and so these can be quantum diamond models or anionic chains, and the quantum diamond model is a model, you might have heard about it already, is a model where basically the degree of freedom is kind of, is that each site actually has an outgoing part of a dimer, and then a valid configuration basically means that each site has an outgoing diamond, it matches another one from a neighboring site, which basically means that each site is part of a dimer and connects two sites together, and if you think about that, that's not the Hilbert space which is a tensor product of local Hilbert spaces, but because there are these constraints which are non-sites, so they're non-local in the sense that there are constraints on the level of a bond or some environment of a spin, and also some of the what we call anion chains, so anions are excitations which are neither bosons or fermions and people have thought about writing down interacting models of anions, and they also have the property that somehow their local Hilbert space is not an integer for something, can be something like square root of two, and if you try to write down a many body Hilbert space of such a chain of anions, it actually also turns out you can, at least for some of them, you can write down as a constrained Hilbert space, for example, for some of the Fibonacci anions, you can actually write that Hilbert space as a, where locally it can be like up or down, but then you have the constraints that no two neighboring spins can be simultaneously in the upstate, so there's some local constraint of that kind, and then the overall Hilbert space also has these constraints, and then it scales with a smaller kind of quantum dimension as two, because you project out some of the states. Each time there are two neighboring ups, you discard them from your Hilbert space, and that's something which in exactization is not a fundamental problem, you just use a different method to generate your Hilbert space, but once the Hilbert space is there, you can work with your Hamiltonian in that basis, and you can apply the other techniques, whereas in other approaches, for example, in matrix product state methods, or also in quantum Monte Carlo, it typically requires additional work to really get these more complex Hilbert spaces to work. It's sometimes possible, but it's often not a routine thing to do. Then perhaps you have heard about it, there is also a method called full CI, full configuration interaction in quantum chemistry, so if you're doing that without any approximation, then as far as I know, this is called really full CI, and that's basically doing a similar thing as doing exactingization for a Hubbard model. It's just that in quantum chemistry, the Hamiltonian is not the Hubbard model, but it has the same type of Hilbert space, so you have orbitals which spin up and spin down fermions in it, and it can also be W-occupied, just that the Hamiltonian somehow is a full fermion interaction which couples different orbitals together, but otherwise it's comparable to a Hubbard type Hamiltonian. And also some people do use techniques very close to the philosophy of exactingization also for nuclear structure, so where you have two-body and three-body interactions among nucleons, and you want to understand the energy spectra of nuclei, then you can use similar techniques to do that. And also something which appeared in the last few years as well is that I can actually also try to simulate quantum field theories, which there was recently a paper out a few months ago where people actually did a Hilbert space truncation starting off, say, the phi to the fourth theory in one plus one dimension, but you actually start with a massive phi squared, a massive scalar field, and then you write down the Hilbert space formally and then you truncate it at some energy level and then you put the phi to the fourth interaction in it, and this is actually an interesting complementary approach to, say, the usual lattice discretization because you actually write down your field theory on a finite volume but not on a lattice in the continuum and then you truncate in energy space and this is a complementary way to do it and then if you actually think about how that Hilbert space is organized and so on, then it actually is very close to exactingization also for some other models, like comparable to this fractional quantum hall or this full CI techniques for quantum chemistry. So that's quite close and perhaps one can also make progress in understanding some of the open questions in that field as well. So are there questions so far? I mean, I encourage you to actually ask questions also and this is supposed to be a school so I'm looking forward for this event to be interactive. So if you have questions or so just do not hesitate to interrupt me and ask. Now, so let's have a look to show you a bit what is possible, what are kind of system sizes in terms of spins or and lattices what people have been able to study in the literature. So if you start with the larger systems because they kind of have the smallest Hilbert space and they're not very precise, these are these spin one half spin chain or spin lattice models. So in literature you can find results for about 40 spins square lattice. So obviously the 40 spins are in total so this is not a 40 by 40 lattice but really 40 spins overall volume and then you can find results for 39 side triangular lattice, 42 side honeycomb lattice. And there's also this simulation for the eight side the cargo me lattice which are which are possible. And so these numbers which I document here they are basic for a spin model with an AC conservation, which means that they have the AC component that overall AC component is conserved and also these lattices have translation symmetry so you can use these symmetries to reduce the block which you're looking at and also the magnetization sector so the AC is zero which means that you're looking at the system half filled in boson language. That's typically where the Hilbert space is largest. If you however change the feeling so either to change the magnetization or change the feeling in terms of hardcore bosons then obviously the Hilbert space is smaller if you're at higher magnetization or lower magnetization than zero and then you can go larger then you can easily do like an eight by eight lattice in elevated magnetization. If you're really interested in few particle physics for example four particles or something then you can easily go to probably 100 or 200 sides because then the Hilbert space is some combinatorical factor but you're only putting four particles on a large lattice that grows only like the fourth power of your volume whereas if you're kind of working at constant filling then your Hilbert space grows exponentially. So if you're interested in few body physics you can actually do obviously problems on really large lattices. And to give you, because over in the end at the end of the day what really matters in most of the cases is the Hilbert space in the particular block you're considering the one which you really need to allocate memory in order to do linear algebra operations which means that after symmetry reduction the largest simulations which have been reported basically use 500 billion basis states so 500 billion is five times 10 to the 11 so that's the Hilbert space or the vector size of your Hilbert space block which you are considering and if you translate that in terms of memory you realize that then even a single vector in that Hilbert space uses several terabytes of memory so this gives you an idea of what the cutting edge linear algebra problems were in what kind of dimensions they're working. I will talk about that later in the lecture I will tell you some strategies to parallelize the code. So I mean for some I mean actually there are shared memory machines which have like 16 terabytes of memory so if you have access to such a machine you don't have to worry too much you can do some open MP parallelization but the things which actually scale better is to really chunk your Hilbert space and distribute it over several different nodes which do not have shared memory but then you have to think how these different tasks communicate with each other and you have to think about the parallelization strategy and how they communicate efficiently but this can be done, it's some work but it can be done and then you're actually distributing your Lanzos vectors over say 1,000 or 10,000 different MPI processes and typically you need two or three or four depending whether you use real arithmetics or complex arithmetics or whether you're going for a ground state wave function or just a ground state energy then you typically need between two and four vectors. Then in the context of the fraction quantum hall effect there you have filling fractions but typically you can do 16 to 20 electrons so in the fraction quantum hall effect at least in the simplest problems the electrons are spin polarized so the amount will have spinless fermions and here you can see that the simulations which have been reported in the literature they operate at a few billion basic states and now you could ask so what's the difference? Why are we able to do like 500 billions for spin models but only say a few billions for the fraction quantum hall effect here and the reason is basically that in both problems the Hamiltonian is formally sparse which means it does not have an exponential number of elements so the ratio of number of elements per the length of a column or a row is really going to zero still the number of operations can be quite different. As you will see later like if you have a spin model with some really local interactions like a nearest neighbor coupling then the number of matrix elements per basis state somehow is proportional to the number of bonds that's basically where an action can happen and so number of bonds is proportional to the number of sites so it's kind of linear in the number of sites but if you actually look how a Hamiltonian in the fraction quantum hall effect look like it's actually more like a four fermion interactions which means you will annihilate two fermions at some locations and you will put back two fermions possibly somewhere else and this is an object which has four indices because there are two incoming kind of momenta or orbital and two outgoing ones and since there is only one overall momentum conservation the number of terms which are possibly non-zero scales like the third power of the number of sites and so one of the reasons then is that the number of matrix elements you have to process is much larger per basis state here than it is here and since processing matrix elements is like the core ingredient of an exact visualization code like the more matrix elements you have the heavier the calculation gets even though the memory you require might still be small the amount of work you have to do in order to calculate matrix elements becomes larger and then computing time becomes a limiting factor so here is not so much the memory of the Lancer's vectors which limits you it's more the computing time because the calculations get heavier because here they basically scale like with the third power of the number of orbitals where you need a number of matrix and only scale linear with the number of lattice sites Yeah? You have to store the matrix No, I will talk about that also later but in these cutting edge applications you do not store the matrix and so what you gain obviously is that you don't waste memory so you're not limited by memory because you're not storing it but then in each iteration you have to calculate the matrix elements anew and that costs obviously computing time but it's clear that if you're working on a large machine you simply cannot afford to store the matrix otherwise your calculation becomes impossible whereas here it becomes possible but it takes a long time so if you're interested in diagonalizing smaller problems where your Hamiltonian actually fits in memory using some of these sparse arrays for example as Chris and perhaps others have explained to you if you can do that obviously that's a good choice to actually store the matrix because then the matrix vector multiplication becomes very cheap you don't have to do lookups and things like we will discuss later you have stored your matrix once and for all and then it's obviously much quicker to just use the stored matrix but if you want to go really to large systems then you have to think about strategies to somehow get your largest problem done and then it's actually more promising to not store the matrix but to recalculate it each time but how we invest in parallelization schemes where you use a large number of processors or cores in order to get these matrix elements efficiently and then with Hubbard models which is roughly corresponds as I said to full CI in quantum chemistry there the number of sites ranges like in the low 20s so there are reports of square lattice the calculations with using 20 sites 20 sites, 21 sites triangle lattice have been reported and also one sector of a 24 site honeycomb lattice so these calculations again are at half filling all of them which means there are as many particles as there are lattice sites and in addition the spin is also AC equals to zero that's the sector where the Hubbard model has the largest Hilbert space for a fixed system size and here the Hilbert space size is comparable to here let's say so it is roughly 200 billion basis states but all these reported calculations are already like 10 years old so if one probably gives a new shot these days one could probably go to like 10 to the 12 for even a bit larger because the Hubbard model somehow has an interesting factorization property of the Hilbert space because you can write down the Hilbert space as a product of all configurations of the up spins of the up fermions the configurations of the downs and there are no constraints so then the Hilbert space is really a tensor product of these two kind of spin like Hilbert spaces and then this actually simplifies the organization of some of the lookup tables and so on quite efficiently so Hubbard models are actually in that sense quite fast to implement I mean to run I mean and so as I mentioned before so these kind of numbers of Hilbert spaces they really apply if you're interested in calculating low lying eigenvalues and this is not you cannot calculate currently the full energy spectrum the entire spectrum but only the low lying states either lying at the lower or the upper end of the spectrum in the respective sector yeah please yeah I mean this Hubbard I don't know I mean in some of these here I was involved and so say like this calculation here with 48 side Kagome lattice there one length of iterations takes like between five and 10 minutes depending on exactly the machine it runs on for example it takes like six or so minutes on a blue chain with like a few thousand cores so I mean they really require large machines but if you do that efficiently then one lunch vector iteration takes takes a few minutes like five as I said five to ten minutes which means that an actual run up to convergence in that sector requires perhaps depending on the structure of the energy spectrum perhaps a few hundred iterations two three hundred which means that in a day or so like wall clock time you have a result so the exact organization typically they require a lot of resources to actually get the calculation done but then the time to solution is kind of reasonable which means that the overall amount of computing time is not necessarily larger than what what people do in quantum Monte Carlo I mean quantum Monte Carlo typically runs forever and you stop when you're satisfied with your error bars but I think in terms of computing time these are also substantial you use substantial amount of computing time but you're not forced to actually use like whatever hundred or forty terabytes at once that is different here you really need to have these resources at once otherwise your simulation is not able to proceed Yes What do you say? No, no more like a few really means a handful Yes, for example, yeah I mean, yeah I mean typically you can if you just want to have a gross idea you typically go for the lowest one which you really converge and then that one is converged to machine precision but the rest of the spectrum has already converged somewhat so you already get an optical idea of what the spectrum looks like but if you really want the other eigenvalues to converge then of course you have to iterate longer and then they will also converge further and further and then you might need like two or three or four times more iterations than just go to go for the ground state but if you really want to go high up then it becomes challenging because as we will talk about later then some of these linear algebra techniques have some challenges if you really want to calculate a lot of eigenvalues and you run into orthogonality loss issues and so on and then the scaling of the algorithms slows down Okay So that was kind of a bit of the overview or the motivation what you can do and how much you can do and now I would like to dive into a bit more technical part where I would like to present you what I consider to be the structure of such an exact diagonalization code and then we'll talk about the different ingredients four different parts so the first is actually you need to formulate your problem and the first step means to actually present and describe the Hilbert space in a computationally efficient way so here we will talk about issues like the basis representation I mean how do I actually present basis states on my computer and something which is very important are so-called lookup techniques we will talk about that in more detail the basic question you can have in mind is that consider that you write down a list of all the basis states which have for example a given AC a total number of particles for some hardcore boson system if you list all these basis states then that's a list and you can sort that even if you want but later if you act with your Hamiltonian on one of these states for example you will be flipping bits so the new configuration you get has some other configuration of bits and what I call lookup is basically the task of giving a configuration which has been generated by application of Hamiltonian elements you have a new configuration and you would actually like to figure out where it actually is situated in the list of all the configurations and that basically amounts figuring out what is the index of your matrix elements in the big Hamiltonian matrix like of the many-body matrix where is the index like you start in one configuration so you know where you are but you would like and then you can calculate the matrix element so you know the amplitude but then you actually need to know where the column or the row is situated and that's what I call a lookup so you would like to know where that element is and in your huge lists it can become a problem that searching for that can become expensive so we have to think about speeding up this task and especially in these matrix-free techniques where you don't store the Hamiltonian you need to do that in each iteration again and therefore it's important that you invest some time in finding good solutions if you're dealing with smaller problems where you calculate the matrix once and then you store it in a sparse matrix format it's not that important that your Hamiltonian matrix calculation is really efficient I mean it can be a bit slower because you do it once and then for the other iterations you do it using the stored matrix then it's not that important but if you want to do cutting-edge applications you need to worry about this and then when it comes to Hilbert space also symmetries will be important I mentioned some of these like the asymmetry that's a simple one to implement but then we also, in many applications we actually have lattice symmetries so discrete translations or discrete rotations and we have to think about how to implement that in order to block diagonalize our Hamiltonian so that the block becomes substantially smaller then there is the Hamiltonian matrix part so that's this issue about whether you use calculate the matrix to store it in memory or sometimes on disk or whether you calculate it on the fly in this matrix-free version and you also have to worry basically in the Hamiltonian part you also have to worry what are the expressions for the actual matrix elements and if you use spatial symmetries then the matrix elements get slightly more complicated but I will tell you how the form of the matrix element looks like and then actually only now now that we basically have a representation of our Hilbert space and then in that Hilbert space a form for our Hamiltonian or a way to calculate it efficiently only then comes the linear algebra part in the Eigen solver or for other purposes also time propagation which is really the core linear algebra part but as you can see it's only it's only one aspect it's not it's not everything then obviously if you want to calculate the entire spectrum you can resort using some some libraries like LaPak or Mkl or related vendor libraries which just do the job and do full diagonalization or you can use ScalaPak or something like that to do that distributed and then you can calculate a full spectra of a few ten thousand up to perhaps a few hundred thousand in linear dimensions that's about what you can do for full diagonalization and however if you're interested in the in only low lying eigenvalues you can use this Lanzar type diagonalization and we will talk about that algorithm and its advantages and inconveniences later on but what is also interesting is that you can replace these solvers so if you for example if you're interested in calculating several low lying eigenstates and you also want to resolve degeneracies of eigenstates then perhaps a simple Lanzar algorithm in the basic formulation is not enough and then you want to switch to something else for perhaps a block Lanzar technique which is able to resolve degeneracies or you use a Davidson algorithm Jacobi Davidson then you can simply replace this part here and you not use the Lanzar but some other back end and but you still can rely on your Hamiltonian representations which we a little bit established in the first part something which is particularly nice about the Lanzar algorithm as we will discuss later is that it actually only requires a basic operation of the type give a vector u which is a u is a vector in the Hilbert space which has been obtained previously and then the only operation which a Lanzar algorithm requires from you is basically apply h so the matrix the Hamiltonian matrix onto u and give back the result v which is that's interesting because it's actually up to you how you organize these matrix vector multiplication and the Lanzar algorithm is not interested in actually peaking for particular matrix elements so it's not working on the individual matrix elements of the matrix but it's just interested in what is the action of the Hamiltonian onto some vector which the Lanzar process gives you you have to apply h onto it and give back the results and that's how it works so kind of the Lanzar algorithm only wants to know what is the result of h applied to a vector and it's not interested in individual addressing individual matrix elements and doing something with them so you can really hide all your Hilbert space and all your matrix in just one matrix vector multiplication operation which is all what the Lanzar algorithm wants to see and then say once you actually have calculated energies or also eigenfunctions you can do both with these methods then you also have to think about observance what do you do with this and then you can calculate a lot of static quantities for example correlation functions you can even do multipoint correlation functions correlation density matrix or other more complicated observance also like reduced density matrices then you can also calculate dynamic observables so frequency resolves spectral functions and then density of states and also something which is interesting which I will also tell you a little bit about is a real-time evolution so that's basically what I think is the basic structure the ingredients in order to have a versatile exact diagonalization framework and we'll now talk about the different parts in the rest of the morning so we will now start looking into the Hilbert space are there questions so far as I mentioned before the states of your Hilbert space need to be represented in the computer in some way and so a guideline or a rule of thumb is that you should choose a representation of your Hilbert space which makes it simple to act with the Hamiltonian or other operators on the states and also to find them so to localize them to find them the given state in the basis which you're considering and so one example is basically if you have an ensemble of spin one half size it's just to resort to so-called binary coding which means that here you have a four spin configuration of a chain or of a two by two square lattice or whatever and so here you have a fork state of your spin chain and then you translate that by choosing a convention that spin ups are represented as bits set so one whereas the spin down is the bit which is not set and so you simply translate that into a binary representation so that otherwise this subscript two means this is a binary number and if you write that as a decimal number this should give 13 if you're not mistaken so which basically means if you have a register or a variable on your computer which with the integer 13 in it if you interpret that as a bit pattern it basically tells you that this is a basis state or is a state of four spins which has this configuration and what is nice is that in some of the programming languages like C++ or also many others you can actually do efficient operations on such bit fields for example you can test whether a bit is set or not using simple techniques and for example in some of the spin models you have a spin exchange an XY type spin interaction where S plus acts on you the indices are missing unfortunately but say S plus is like acting on site on some site here and S minus on another one and here minus and plus work on a pair of spins and what is now nice is that you say here we have this configuration which you have written down and now we would like to flip the two spins then the question is how can we do that so the idea is basically to take to prepare a mask like here where you basically put two ones at the locations where the two spins are which you want to exchange where you want to do a spin hop a flip of both spins so here this mask is chosen such that we're talking about the two central spins so these are that one and that one and now if you want to do that in general what you have to do first basically is to take this mask and then to apply it on this bit pattern and then you have to first figure out whether the two spins which you are considering whether they are actually in a flippable state which means of the two sites which you're looking at only one spin can be set and the other one has to be not set otherwise this configuration is not flippable which means the two spins are up or both spins are down there's nothing to flip so then you cannot do something but here this spin one and zero they are in a flippable configuration with respect to each other and now what is interesting here is that you can take this bit mask and use a so-called exclusive OR operation that way where all the sites which are not involved they have a zero here and all the other ones the two which are involved have a one and if you apply that this binary operation here you actually get the result where the two spins here are flipped whereas all the other spins are left intact and so for those who are not familiar with this exclusive OR operation exclusive OR operation is a binary operation which gives you a one as output if the two inputs are different and it gives you a zero if the two inputs are the same which means here for example so you do it you see the XOR works bitwise so here you have a one you have a zero the XOR of the first bit therefore gives you a one because they are different and that one with that one the two are the same XOR gives you a zero so there's a zero coming out here there's a zero and a one they are different so XOR gives you a one and here that one and that zero gives you a one as well because again they are different and if you think about that if you have a zero here if you have a zero here this basically just replicates what you had as an input whereas if you have a one you basically flip what you have as an input and so if you do that if you do it that way you can kind of flip spins very easily and that's typically how you can implement such a spin flip operation on a binary level because like for example that there is an operation using exclusive or even in C or C++ there's such a binary operation which is basically the hat operation don't know if you have encountered that but that's part of the C language to do exclusive or bit operations and so if you so this was a simple example telling you how you could do that for spin one half but obviously if you're working in a different regime you might also think like writing down your basis differently so if you have so this representation here is quite efficient if you're actually thinking about the system which has roughly the same number of up and down spins because then the Hilbert space grows so rapidly that typically you're not working on systems which are larger than say 64 sites and 64 sites then translates into 64 bits and that's basically the size of a register or of a variable of a long integer which you have on current machines so you can really work with the basic data types of your computer which is very fast however if you're interested in a different regime where basically you have only few particles say a handful of particles but on lattices with hundreds of sites like a hundred or perhaps 200 sites then you might wonder whether this is still the right way to do it and indeed then if you only have a few particles then perhaps you're better off actually describing your basis states as a list basically where you say where you say where the locations of your four particles are so in that regime it might not be the best way to actually use 200 bits and tell where the sites are but actually to just tell for the number of particles which you consider what the location is of the particle on your lattice and then of course you have to do these hoppings to implement them differently but if you're interested in this kind of low magnetization applications which is the typical I mean the most encountered application then this is the most efficient way to do it I guess then if you're thinking about the spin one, then the spin one obviously has three different local basis states spin up to zero or spin down and then it's obvious that one bit is not sufficient to encode information about three different states and so here there are actually two different choices you can actually use something like a ternary representation so basically you treat your integer basically as a number with the basis three which means you do like modulo and divide operations with three in order to figure out basically what is the digit of your number in the basis three representation so the three digits zero, one and two basically are then mapped onto down, zero and up or something like that and so you can organize that or also if you, but what you can also do is kind of blow up the representation and simply use two bits, two consecutive bits that they can actually store four states but you only lose three of them where basically you say that every two bits encodes the state of one, spin one state and that's another way to go and I think it's not clear which one is faster but it's clear that the ternary representation is more compact so it uses less bits overall whereas here you're wasting kind of one state per two bits but sometimes you can afford to do that and you can again think about like for spin one although I have not shown you but for spin one what one can typically do if you're considering a spin and SU2 invariant model for spin ones you can typically go to system sizes of perhaps 20, 25 sites something in between there and so if you again have a reference that your typical integers you deal with have 64 bits and here you use two bits to represent the site with 64 bits you can therefore represent basically 32 spin ones and that's still enough for the applications which work in this kind of zero magnetization regime so this is perfectly fine to represent the states of a spin one with two bits and then obviously spin three half can also fit into two bits and then obviously you have to increase for spin two and higher you have to use three bits and so on are the questions for that and then there are a few other models where one has interesting ways to arrange the Hilbert space so if you first think about the TJ model but don't know whether you're familiar with the TJ model the TJ model is a model where you also have three basis states and they are basically in the TJ model you have three basis states you either have an empty site so there's no particle on it or you have a spin up or you have a spin down and the difference with the spin one which I just talked about is that this is basically a fermion a fermion with a spin up and this is also a fermion so the physical picture behind it is different so this is an empty site there's no particle here there's a spin up fermion here there's a spin down fermion and you obtain a TJ model basically by starting from a Hilbert model where you also would have the W-occupied site but you delete that one because you're interested in a large U-regime but U is the W-panelizes W-occupied sites and if something is really suppressed by energy a lot you can actually throw it out of your Hilbert space and so you can reduce the Hilbert model to a TJ model but in a TJ model the number of zeros or the number of ups and downs together is separately conserved which is not the case in the spin one model so the symmetries are slightly different because here you really have an SC conservation in a typical TJ model but you also have charge conservation which means that this is an empty site and these are sites which carry a charge and this is conserved in the TJ model so you can organize your Hilbert space slightly different and so if you're interested in a TJ model at low doping you can actually write down a many-body state basically in a factorized way where you first tell basically in your configuration you have two different bit patterns one is basically to tell you where the holes are so you indicate basically by bits where the zeros are whereas here on the remaining bits you actually encode the state of the spins so let me show make an example so that you understand what I mean is that if we write down a basically a configuration of a TJ model so we have like a spin up then we have a down spin then we have something like that then now for example we can choose the convention so that's a TJ configuration and then we first have a we now decompose that into two different bit patterns which basically means first I tell where the charge is which means now I only use the information in that charge pattern whether there's a charge at a certain site or not which means here there is a charge when there's a particle with spin up or down so everything is up or down I translate that into a one whereas if there's an empty site I put a zero so in the charge sense this maps onto this because each one is a one there was a particle and then what I do basically is then here and here I have a shorter ket and here I basically write down the occupation of the I mean on those sites when there is a charge I now indicate the spin configuration which means now since I on those six sites I only have four charges I'm only left to indicate the four spin states of the four charges and now on the first charge the first particle has been up so I will put the one then I have two downs so there will be zero zero and then there's an up again so there's another one here and now you can ask why did I do that so the idea is basically that in the if you think about the TJ Hamiltonian so for those of you who don't know how a TJ Hamiltonian looks like that's basically a sum over lattice bonds and then there's a hopping of this there's some hopping kinetic energy of the particles with the with the spin also so that's particles like this they can hop but there's also some there's also some for two neighboring sites where there are two spins where both on both sides there are particles with the spin there there's actually a Heisenberg interactions acting between those two neighboring sites however if one on one of the sites there is no charge then obviously this doesn't have an action and so if you think about what what these different terms in your Hamiltonian do then you can see that the charge part here is basically just hopping a spin from that to that location and then you can see for example that what is then nice in this language is that if you're only considering the action of a hop term then what it will do in the TJ model it will hop even exchange these two but what will happen on the level of this representation in separated in charge and spin is that here you just you exchange these two because now the charge hops from that side to this one but what is interesting is that the spin configuration is now not affected because in that this still has the meaning that these are the four positions so the position of that charge change it change but the configuration of the spin sitting on top of the charges has not changed so there's some advantage that you don't you have to do less operations for the for the charge part here and that is of some interest the reason is also that after that you can also organize your your lookup tables in a kind of almost factorized way which somehow means that your the individual tables you have to you have to create are smaller than and if you if you use configurations where on each side you actually use two bits to represent the state of one of these three different local basis states and for Hubbard models one can actually as I mentioned before already one can actually factorize the Hilbert space in up and down electron configurations and then that's also helpful to represent the basis so not not to use two bits sequentially but actually write down a configuration in look where are the ups I mean where are the ups pins and where are the down spins and then can be faster and uses less memory to actually represent the Hilbert space that way then so if you have tensor product Hilbert spaces like the cases we looked at before it's rather simple how to how to construct a different basis states but if you have a if you have a constrained model that just want them diamond models then it's actually it's a non-trivial problem to actually generate all the old diamond coverings and so on the one hand you have an advantage that typically diamond models have Hilbert spaces which per lattice sites grow rather slowly they're still exponential but somehow they're they're the way that they diverge is less is less violent than in other models but on the other hand actually finding all diamond coverings for like a lattice with sort of order hundred sites really requires you to think about how to find these diamond coverings efficiently because if you're doing that the wrong way basically for example taking every lattice sites and trying all four different local orientations of the diamond emanating from one side and then finding matching that way then it can very well be that your algorithm is extremely slow and you're not able to really find all of them in a finite amount of time so you really have to think using perhaps backtracking techniques or other clever techniques in order to find these diamond coverings efficiently but it's just an input that basically if something is simple for tensor product Hilbert space it doesn't mean that for the constrained model it is as simple sometimes you have to work a bit in order to generate Hilbert spaces efficiently and that's an additional challenge for these constrained models and so one of the key challenges because if you're recalculating the matrix elements each time in each non-source vector iteration a new is to find the index of a new configuration in the list of all the configuration that you have that's what I briefly explained before it's basically figuring out the index F like for final state of a matrix element which connects your initial state which you know, you know I typically then you apply a matrix elements and you find a new configuration then you would like to know to what configuration to what F does this configuration correspond and let us now look at that in the context of a simple spin half chain at fixed MSE actually we now encounter the example again which we used before so before we had the number 13 in decimal representation which was 1101 and then we flipped it we flipped the two central spins and now that's our new configuration but that's this bit pattern and the question is now basically how do I find the index of that state and in order to illustrate this I mean if you enumerate your Hilbert space and you do that in a numerically increasing way then you would actually write your four configurations in that Hilbert space because we're considering four sides and we're basically having total AC of one that's you have three up and one down pointing spin so that's total AC of one and so in terms of spin configurations you can basically choose where the down pointing spin is like that and so there are four different choices so they then translate into bit patterns so there's like a zero because the first one is down like this one you can see there are arithmetically increasing because that's the most significant bit so that gives you the largest number so that's the smallest one and the next and the next and the next but now you can see that if you actually number these states in your Hilbert space you would say that this is state number zero because in typically start numbering items by starting with zero so this is the configuration which has index one that's two and that's three but now you see the basic problem I want to expose is that this bit pattern one zero one one if you look in the list here you can see it's actually the state with index one in our list but if you the register or the variable we have has an entry as a value which is 11 in the decimal number so there's a mismatch between if you just look at the pattern it has a value, it's a number but the number is not its position in the list of all the configurations so the basic problem we have is that how do I find out efficiently kind of what is the index of this configuration in the list of my states here in the example of a Hilbert space with the fixed AC and you see that AC constraint actually on the one hand it reduces our Hilbert space because instead of 16 spins which we have for a general four side spin if you impose an AC constraint we reduce our Hilbert space but then we lose the mapping between the value of a spin configuration as a numerical as a number with its index in the list of configurations because if you drop the AC constraint if you really basically consider a spin configuration to be a number that way but you allow all spin configurations then the value of the configuration is its index if you order the configurations increasingly but however if you take a projection project to a fixed number of spins of spin ups for example which means having a conserved AC then basically you realize that the number which a configuration represent is not equal to its index and now you have to think about other techniques in order to kind of speed up that mapping and now the basic technique I would like to show you because it's actually a curious aspect is our so-called LIM tables so HQ LIM introduced that in the paper in the early 90s and so what these LIM tables do is again to map from the binary number like here this value 11 to the index in list of allowed states and actually the idea behind it I try to explain you a little bit what the idea behind this is that this idea then actually works for arbitrary number of additive quantum numbers so the additive quantum number in the game here is the AC conservation and the part which is additive is that that the AC quantum number can actually be obtained by summing local contributions up to the end like going through all sites you add up the AC and then you have a global additive quantum number but you can obtain it by having a contribution like from a left and the right part and that's actually important because this allows you now to look at two different to construct two different tables where we have two tables which have a size of two to the n divided by two which is actually square root of two to the n I mean it's obvious but this might illustrate better what you have in mind and one table for the most significant bits and another table for the least significant bits so the most significant bits basically means are the two bits which in binary representation gives you the larger numbers and the least significant bits are the other two and in order to fully appreciate what that means is that if you're thinking about now not the foresight example as we do here which is really a cartoon problem but if you have something like 20 spins so 20 spins, the overall Hilbert space is like is a million, it's two to the 20 is roughly a million and so in principle you can store your entire Hilbert space as a, I mean for all spin configurations you can basically have a table you go as an index with this number 11 in it and you actually store the index that would be one way to do it really somehow have a list with the indices but that's a bit expensive and with this technique here you can actually use two tables where each table only uses square root entries which means that instead of having one table which has a size of a million we actually have two tables with a size of a thousand and here you can see there's a big difference affording two tables with square root two to the n so a thousand is much more convenient and doesn't use a lot of memory compared to having a table with a million entries so you see that the square root is actually really important and now the trick here is you can see there are now two tables and initially I just present them in a kind of a magic way so there is one table here the orange one which has, it now has only two bits long because we have four bits initially square root of n actually makes that you only consider half the number of bits so these are two and then, so these are the four entries in our table so the values range from zero to three but here they're written in a binary language and these are now tables which we can use to figure out the index of these and other configurations but they all have to come from the same global constraint sector namely they all have to have overall ac equals one and now magically if you take this table this yellow one where zero zero actually does not occur so the value here is not important therefore I put the next but then I have a configuration zero one I give it the value zero I have a configuration one zero on the first two bits I give that the value one and then I have a configuration one one and I give that the value two so take that just for granted for a moment and then you have another table which is relevant for the least significant bits over the second half of the bits so zero zero again does not occur so I don't have to specify the value not important but then I have zero one equals zero one zero equals one and one one equals zero and now the magic is that these two tables they are now ready to actually give you the correct index in the list of order states for any input which is a valid configuration in that subspace and if you take our example so one zero one one is a valid configuration this c equals one state so we break it apart so we take the first two bits one zero these are the most significant bits you take them one zero so that's the number two basically so we corresponding to the third index because we start numbering at zero so here we go with this one zero into this table with this index you get back an answer one so that's actually this case here so we write down a one that's a result from the first look up here and then we take the other two bits one one which are the least significant bits we go into that list here which is relevant for the least significant bits and we get the answer zero and so we add them up and we get one and now here you can actually see here all four basic states which are in that sector are tested using that way so we have zero one one one so zero one gives you zero from here one one in that table gives you zero here so that gives you overall zero it reproduces correctly that's the this zero one one one is the first state in our list that's fine and you can see that all the other ones is one one it gives you two here and that for that configuration also gives you a two so they have the same because they have the same most significant bits but that zero one gives you a zero and that one one gives you sorry that one zero gives you a one so this adds up to three and so now you can ask what's the magic in that is that just the coincidence or what's the logic behind the logic is basically that if you have a configuration of that kind then actually there is an AC on the most significant part bits and there's also a corresponding AC on the least significant bits and the two ACs which the sub parts have they have to add up to satisfy the global constraint which means there has to be some AC on the first two bits and some AC on the second two bits and together they have to add up to the correct overall AC that you want to impose or you can also call it particle number it's the same but the idea is the same and then the idea is basically that that the most significant bits they also here they also correspond here so for example this is a sector with zero particles and the first two bits with one particle with one particle and with two particles and on the other hand here you have you have the sector with zero particles with one, zero, one and with two particles and at least it's simple to understand what this least significant bit part is doing is basically you can write down the table for the least significant bits very easily because you're going through all configurations which are possible on your subsystem on this half of the bits and basically you number the states according to their position within the subspace of that subsystem which means so here basically zero, zero would mean you could actually put the zero because it's the first state which only has two bits which has zero particles so zero, one is actually the first state which has one particle so you put the zero and one, zero is the second state with one particle so it has a one and one, one is the first state which has two particles so it has a zero again so if you do that for example for three just as an example so can you help me how I fill up this list here now but these are the, this is the table for the least significant bits I want to fill it and here I want to know what are the entries in my table zero, yes yes yes okay I guess you have understood the logic and now that's how you do for least significant bits and now what is the idea now on the most significant bits so the most significant bits is slightly more complicated because as you go through all the configurations allowed you basically keep track of how many configurations you have already encountered by going through but the idea is that basically if you yeah so the idea is so say if you're in a certain sector and you start with your most significant bits say like you have a zero, zero here but you say like you have two particles allowed and you have another three bits here so you have another three bits which basically means now if you're going in an ordered way first you have three different configurations here which actually have two particles like these are this configuration, this one and this one so there are three configurations which we'll go through but then as when you encounter the first time you increase the most significant bit by one now you actually have to put as an entry so the entry for zero, zero series because you start at zero is zero but now you have to ask given a global constraints of two particles like how many two particle states have I been able to do given that configuration for the most significant bits then there are basically there are three configurations so when I'm done here and I'm coming to I increase my most significant bits by one basically then I have to add up how many configurations I have seen I have cycled through on the least significant part which means here I have to put three but now you have to see now that I increase that to one I have pulled out one particle from here so there's one particle here but then on the remaining three bits there can only be one particle and okay there are three configurations with one particle possible so I will go through them and then once I'm going to one zero one here for the most significant bit I have gone through another three bits three states on the least significant side and so I have to add them up and then so this will be six here I illustrate that also by writing that out so this is a list of all configurations which can occur without any constraints on the most significant bits and this is a list of all the configurations which can allow without any constraints on the least significant bit but now we have an overall constraints of say two particles which basically mean if we start here with this zero zero zero there is no particle here so all particles have to come from that side which basically means if we are here then basically we can only look at this configuration at that one and that one these are the three which occur and now if you want to fill our table so that's this one here so for zero zero zero we start with zero but if we fix that now we will take this configuration into account that one and that one and now we have it on all on the least significant side and now if you have gone through them you have to go to the next one on that side but when we have done that we can now fill our table because we have visited three configurations we now go from zero to three as the new entry in our table of this lookup table and so if we have one here it basically means there's one particle here and now there is only one particle here so now we're basically visiting this configuration that one and that one so it's another three here so that's fine so if we now are gone through that we have accumulated another three so if we're visiting this configuration as a most significant one we have added three and that's now why we're at six and if we're now at this configuration we have one particle so we're visiting the same ones again so we have to add another another three when you get to zero one one however now it's... now you see there will be a change because now we have all two particles on our first two bits but there's no particle left on the other side so here we will only visit this one otherwise we will not satisfy our global constraint so there's only one state which you pick in so if you go to the next one we have only increased by one so here one zero zero in this most significant lookup table there will be just be written ten and now you can continue if you're here you have one particle so you're again cycling here through the one particle states so you add up another three thirteen and you can go on and complete the table but now if you do that the way we say and this is now the this is now the lookup table for the most significant bits and here that's the lookup table for the least significant bits so if you have now these two you can really take any configuration out of this product Hilbert space which satisfy the global constraint of only have two particles then you can take the first three bits of your configuration and then this will give you a number here which we have established and then you add up that number with the number you get by putting the least significant bits in this table and adding up these numbers and you get the correct index that way yes how do you go from index to the vector this table means the other space yes when you construct the Hilbert space I did not talk about that specifically but when you construct the Hilbert space you can for example go through all possible spin configurations of a spin chain and you check what is the AC of that configuration and you only keep the ones which satisfy the constraint and then typically you put them in a list so you store all the integers going from the index to the configuration is something which you have stored pardon me if you have a not number it just means that one of the lists has one bit less so if you have five sites you can have three bits on the most significant two on the other one or vice versa it's not important this is the simplest setup now with the single conserved number of particles you can actually also do that if your configurations have like a spin quantum number and a charge one or things like that you can still write down the generalized tables that way like the left and the right part and they have a like the way how you construct it on this side is something it's still like summing up the contributions which you have seen by building up your Hilbert space so this is like an intermediate sum which you come up you see here they are monotonically increasing whereas here it still what I mean is that you still have configurations in your least significant bits but they can be have more quantum number sectors like it could be that this configuration has a spin and a charge and then you would actually still go forward and enumerate configurations which are in the same charge and in the same spin sector you would number them continuously through but it might be that they appear scattered here because another configuration might be another charge or another spin sector or in both sectors different and then they would have another account but it's within the sector within the same number of the same charge or the same spin on the subsystem you index them continuously so that's how you build the least significant bits and the most significant bits is by actually accumulating the visited states you can go through your Hilbert space and then your two tables are perfectly consistent and then the main idea is that if you have a configuration you break it up in two you go into the left and the right table you add up and you get the answer and that's quite efficient for these problems of just having conserved charges and even if you have several of them which means for example you can also do SUN spin models where you have more conserved kind of spin quantum of course and it works it's simple to generalize it and it works the same way to break it up into two tables yeah so now now if you have understood this concept of these lint tables the lookup can now be done with just two direct memory reads because the two tables which you have constructed since they're only square root of Hilbert space large they're typically really small compared to your overall Hilbert space dimension so it's negligible to store them compared to storing a long source vector which is size of the Hilbert space and so therefore this is a time and memory efficient approach time because it's only two direct memory access in an ordered vector so that has constant access time and so that's quite okay sometimes when it's not possible to actually build such a table like these lint tables it's still possible to actually do a hash list or to do a binary search in the list of all configurations so say if you're not for some reason you're not able to actually construct these lint tables you can still as we said before there's a list of all configurations which we keep in our code and if you generate a new configuration by applying a matrix element in your Hamiltonian either if you have a hash list this gives you more or less constant access time it tells you where that configuration is you can build such a hash list or you can also since your list of configuration if it's ordered you can do a binary search and then with a logarithmic time in units of the length of your vector you're able to locate it and that's also sometimes still okay however if you're using a spatial symmetries that's not something we considered here we just considered like this diagonal particle number for a C conservation or a C conservation then building this lint table gets a bit more complicated but I will not talk about this right now in these combined symmetries okay so I think now it's 10 o'clock I think it's a good time to make the break and after that we will then start with the symmetries looking into details about lattices and lattice symmetries and how they come into play in exact organization code okay