 Thank you to the organizer for offering me to speak about this topic. It's one of my favorite topics, and I don't have the opportunity to teach a lot at Microsoft, so I'm very glad to be here. And I'm not going to assume much about quantum error correction. My goal is to talk to those of you who don't know much about error correction. I'm going to start even with a little bit about classical error correction, and I'm happy if it's interactive. Feel free to jump in if something is not clear. Feel free to ask questions. There will be many examples throughout the lecture. I'm happy to go through only half of my slides today if you have questions. So feel free to jump in anytime. And my main goal for the week is to teach you how to build your own quantum memory based on quantum LDPC codes. And we want this quantum memory to be fault-tolerant. I'm going to explain all these words. And when I say that, I'm not a physicist, so you're not going to build much, but by that I mean on paper. And for the first lecture, I'm going to start with classical codes. Then I'm going to discuss stabilizer codes. And I'm going to show you that we can build good stabilizer codes. And this is something we wanted to do for a long time with quantum LDPC codes. And then I'm going to go through some examples of construction of quantum LDPC codes if we have time. So first, classical codes. How does it work? I'm going to start with a very basic example with the repetition code. We want to send some information through a channel and some bits are flipped when we send this information. So to correct a bit flip, we are going to repeat the information. Here we repeat three times. We encode the information by repeating each bit three times. When we send it through the channel, some bits are flipped. But even if some bits are flipped, we can still see what is a dominant value and we can decode to find the original code word, the original information. Now what's happening if there is two bit flip in the same code word? Then we're going to make a mistake. So we can improve this scheme. Do you know how we improve this scheme? More repetition, okay, great. So we can do more repetition, we can repeat five times. And with that, we can correct two bit flips. But if there is three bit flips, we make a mistake. There is a decoding error. And what we like to do in classical and quantum error correction is to check the performance of a scheme we propose. We do that by a Monte Carlo simulation. So we take a family of codes, the repetition code, where we repeat a bit D times. And when we keep increasing D, we can correct more and more bit flips. And we simulate this scheme. So we simulate those random bit flips a million times. And we report the performance. And the kind of plot you see in most error correction paper, and quantum error correction paper, is this kind of plot. Where you see the family of repetition code here. I increase the code distance, the number of repetition. And when I increase the code distance, the performance improves if the bit flip probability is low enough. So if there is 30% bit flip, when I move from three repetition to five, I decrease the decoding failure rate. So my logical bits become better and better. And we like to plot it in log scale where we see that if we're at a low noise rate, there is an exponential decay of the failure rate when we increase the code size, the code distance. So you have seen the exact same plots with surface codes, or with LDPC codes, if you have looked at some papers with LDPC codes. So it's a way to check the performance. And what we like to report as a number is the code threshold. This code can tolerate 50% noise. We can correct 50% of bit flip, up to 50%, which is a lot, it's great. And we can represent this code with a graph. I'm going to use a lot of graphs. And the graph that represents this code is called the tanner graph, where we have two types of vertices. We have bits that are circles. We put one bit on each circle, and we have checks that are squares. And a check is checking that the parity of the incident bits is zero. So if I sum the value of these two bits, I want to find a bit zero. And when we do that, we do get, we enforce a repetition code here. Because if this value is zero, the sum of the two neighboring bits must be zero. So this value must be zero. And it propagates through to force the bit string zero or the bit string one everywhere. Yes, so the bigger code get worse, that's true. So in this case, there is a symmetry that allows us even to use the code in this regime. So you're right, it's a great code. So I believe it's not possible to have a threshold as high as that in the quantum setting because of the no cloning theorem. So if you can have a threshold above 50%, you can just send in the information to two people. Each of them will get 50% of the information and recover a copy. So you will have a clone. So it's impossible, sorry. So I haven't defined it, but you can define it as a value P such that below P, the logical error rate goes down when you increase the code distance. It goes down, so if you are below 50%, when you increase the code length, the code distance, the logical error rate will go down. For any values, strictly below 50%. And here there is a very clean threshold because the code is so simple. It's also something we see in statistical physics in the easing model. It's the same notion of threshold, of phase transition. It's only defined for a family of codes, yeah. Okay, so we have our graph representation of the code. And now what's happening in this graph? If there is, if we receive a code word like that, where three bits were flipped out of seven, we can correct them. We have distance seven here. We have seven bits, three flip. We are going to look at the value of the checks and we see that some checks are telling us that there is an issue. Some of the bits have been flipped. So we are going to use this value called the syndrome to correct errors. And yeah, so the, the, the repetition code is great. But it has one issue is that it encodes very little information. There's only one logical bit. So if you see, if you need to send 10 bits or 50 bits or 1,000 bits each time you want to send one bit of data, it's going to be far too expensive. So the solution is to go beyond this graph. This graph is too simple. We're going to look at more interesting graphs. And we can look at any bipartite graph. And we can get two things simultaneously. We can encode many logical bits, more than one. And we can correct many bit flip in the same time. I'm not going to show it. I'm not going to prove it for linear codes, for this class of codes. But I'm going to give the same proof for stabilizer codes, for quantum codes later. So you, so you have to trust me for this claim. And this is an example of a more interesting graph. And it defines a hamming code. It's a code with seven bits and three checks. So there is four bits of information. It's seven minus three. Each check is killing one bit, is removing one logical bit. And with that, we can correct any single bit flip. So we can correct a bit less than with a seven bit repetition code. But we can encode four bits instead of one. It can be an interesting trade off. And when we have a bit string like that, we get a syndrome zero. If we compute all these checks, we get the value zero. Because this is a proper code world. So this is one of the bit strings we can send through our channel, and which is protected. And another case is, we start with any bit string, and there is a flip. One of the bit is flipped. So bit four is flipped. Then the incident checks are gonna detect that there was something wrong. Here, the only incident check is this check. The value of the check is flipped. We can use this information to correct errors. And the last component we need is an algorithm to tell us what was the bit flip given the syndrome. And I'm going through these three components because we're gonna build the same in the quantum setting. So we need these three components, the code, the syndrome, and the decoder. So the decoder is the algorithm that tells you, given the possible values for your syndrome, for your three checks, what should you flip back? What was flipped? And we can build a table. There is a small number of values here. We have only seven values, and we can check what value corresponds to what flip. We can store that, store those seven values, and this is an efficient decoder. It doesn't work if you have a larger code because you will have an exponential number of values here. But for a small code, you can decode like that. Okay. And the issue is that the codes are not gonna be small. So in general, this algorithm does not work. This table is gonna have an exponential size. Because of that, we need a family of codes with an efficient decoding algorithm. Better than just storing everything in memory in an exponential space. Yes. So that's one thing we are gonna get back to, but in the classical setting, yes. When you send some information through your channel, the receiver can have a very reliable computer. If you think about cell phone communication, your cell phone will communicate with a tower. You are far, some bits will be flipped, but when you receive the information on your cell phone, the machine you have in your cell phone, the computing capacity you have in your cell phone, is perfect, is pretty much perfect. So you have no more noise. And that's a good point that in the quantum setting, these checks are gonna lie to us. They will be noisy. And it's a big issue. It's a major issue. Thank you. So the last issue I want to point with classical code is that they don't have this decoding algorithm. And that's why we move to sparse graph. Because sparse is always easier for an algorithm. And this is what our LDPC codes. Low density parity check. So those checks have low densities. They are sparse. And that means we have a graph with low degree. If we have a graph with low degree, it looks like that. We have a very large code, many bits. By many, I mean thousands for LDPC codes. And we have many checks, but they act only on a small number of bits. So for instance, we have a bit string here, and we have a check that checks that the sum of three of the bits is zero. And because it's sparse, we have an efficient decoder. And the idea for this decoder is to say, if the sum of three bits is one, then you know that one of these three bits, at least, was flipped. So each check gives you a lot of information. You have only three choices for the bit flip here. So by using all the checks and comparing the information they give you, you can easily decode and you can do that locally in a sparse graph. And this is what makes them efficient. So now we want all of that in the quantum setting. Yes? So low in the classical case, it's not very well defined. It depends on your application. In the quantum case, we typically want very low. If you think that you have to measure a check acting on 10 qubits, a 10 qubit measurement is already a lot. It's challenging in hardware. It's going to be noisy. So we like to use surface code with wait for, or we can consider, I will discuss, of quantum LDPC codes with wait seven. I would like below 10 ideally. Okay, any more comments about the classical case? Okay, great. So what do we need? We need to encode many logical qubits. We need to correct many poly errors instead of bit flip. We need an efficient decoder. And we need the fault tolerance. As you were saying, the checks are noisy. That's what we mean by fault tolerance. We need to be able to implement our quantum error correction scheme with a hardware that doesn't work. And this introduces more noise. So we want to make sure that we don't introduce more noise than we correct by running error correction. And right now, all the quantum error correction codes that have been implemented do introduce more noise than they remove. We are not in this regime yet. But it's going to come soon, hopefully. And that's why we want quantum LDPC codes. They seem to satisfy all these features in theory. And it's a class of quantum stabilizer codes. I'm going to first introduce those codes, which are the quantum version of linear codes. So I'm going to go very slowly. So let me know if I'm too slow. Or if, but my goal is mainly to make sure that the notations are clear because I'm going to use these notations for the whole week. So by parli operators, I mean those strings of identity X, Y, Z with a phase. So I'm going to denote it like that. Or I will remove the tensor product sign, but it's still there. Or I will denote it like that with X acting on qubit one, X acting on qubit three with a sparse notation. And I will use PN for the N qubit parli group. Now, one important thing for error correction is the commutation relation between those parli operators. And there is two possibilities. If you take two parli operators, either they commute or they anti-commute. So when you write their commutator, you get plus or minus one, the identity. For instance, do you think that these two parli operators commute? Can anyone see immediately if they commute? No. So we can see it by comparing each qubit. X anti-commute with Z. The others all commute pairwise. So it anti-commute once, they anti-commute. These two, we can do the same thing. I compare X with Z, there is one anti-commutation. Y with X, two. Z with Y, three times anti-commute, they anti-commute. And those two, okay. They commute. How do you see it right away? Okay, so it's made with I's and X. And we are gonna use that. We are gonna build some code that use the fact that those guys always commute. And those two, they anti-commute four times, so they commute. And I'm gonna use a notation that is maybe non-standard for the commutator. So what I call the commutator is not gonna be the group commutator. It's gonna be just a single bit. So I keep basically the phase plus or minus one of the commutator, and I store a bit. The reason I do that is because this looks like a symplectic inner product. And if you write these as symplectic vectors, it's exactly this symplectic inner product. So it's convenient in some cases. Okay, so now what is the stabilizer code? It's a code defined as a fixed subspace of a family of polyoperators, a set of polyoperators. And it's a subset of the n-cubit space, the n-cubit space, which is C2 tensor n times. So we have this subset. And what can we say about this set S? If we have this definition, this set S has a strong structure. It's not any set. It's a group. It's a group, it's commutative, and it does not contain minus one. And conversely, if you take a group with these three properties, a subgroup of PN, that is commutative and that does not contain minus one, then the set of fixed vectors is a subspace of the n-cubit space, and it's a stabilizer code. So it's just a subspace of the Hilbert space fixed by polyoperators. So you have a convenient way to describe your subspace. And I'm going to use the term stabilizer group for this group with the three properties, commutation, a subgroup commutation and minus one is not in it. The elements of S, the operators that fix the code space are going to be called stabilizers. And the stabilizer generators are the generators of this group. If they are given, we have stabilizer generators. And I'm going to let you do the proof. I'm going to include a bunch of open proof. Most of them are simple. I'm happy to chat any of them if it's not clear. And some of them that are not trivial will be done during the problem session. Okay, so now we can put all of that in a matrix. So we can put our stabilizer generators in a matrix. Where the rows are the generators. And we can, when we look at this matrix, we can check that the rows commute. The first and the second one commute. We can check that all of them commute. It's a commutative subgroup and it does not contain minus i, so it's a stabilizer group. It defines a code that we call the five qubit code because it encodes some information inside five qubits. There's five physical qubits. Okay, now how much information does it encode? How many logical qubits do we have in the five qubit code? And before I was telling you, each time you add a check, it removes one logical bit. Here it's the same. Each time you have a stabilizer generator, it's one extract on strain. It's going to kill one logical bit. So for this one, what will be your guess? How many logical qubits do we have? One, we have five physical qubits and we kill four of them. We fix four of them. And it's a general claim. If we have a set of independent poly operators, that means that there is no product of these operators that is the identity. Then, if you have r of them, the number of logical qubits is n minus r. So we can compute the dimension of the code and the number of logical qubits. And you are going to prove that later. And n is called the code length, like for classical codes. And we denote the parameters that has nk, where k is the number of logical qubits. And we use double brackets for quantum codes. Yeah, this is a stabilizer group. Yeah, so it's not written here, but s is the previous stabilizer group. I may omit some assumptions sometimes. But thanks for clarifying. Okay, so now what do you think about this code? How many logical qubits do you see? Sorry? The rows are not independent. Okay, that was a trap. So the last row is related to the others. And you can see that if you take the product of all of them, you have two x's and two z's everywhere. They cancel out. So the rows are not independent, but if we remove the last one, we get the five qubit code. So there is one logical qubit. And for this one, one, okay. How do you see it? I'm sorry. Okay, so because you know the answer, that's not a great proof, but that's true. So it's one. So x and z are independent. So we don't need to check the independence between the first three and the others. And between the first three, this is a standard form for a matrix, right? It's a echelon form. So the first row is independent from the second one and the third one because it's the only one with the x here on the first column. The second column is independent from the others for the same reason. And the third one as well. So they are independent, same in z. We have six independent stabilizers. And we call that a CSS code because it has only x checks, x stabilizers, or z stabilizers, stabilizer generators. Okay, now we can introduce a second object we need. We have a code, we need a syndrome. We need to detect some information about errors. So the syndrome, it's a bit string, like in the classical case. So I will denote it's sigma of e. It's a syndrome of a poly error. And sigma of e, it's a bit string where each bit is given by the commutation. If the error commutes with the stabilizer generator, then there is a trivial syndrome bit, trivial check. If it anti-commutes, we put a one. We have a non-trivial syndrome bit. This is our bit string, but it's not enough. This bit string, in the classical case, we can directly compute it. We can compute a parity. In the quantum case, we cannot compute a parity. Those bits, where are they? How do we get them? Does anyone know how we get them in a real quantum computer? We need to measure something. Yes, so we need to measure something. And what do we measure? Okay, so to get sigma i, we are gonna measure si. And it's also something we need to prove. And it's also something you are gonna do yourself. But what you can prove is that if you measure si, the outcome of the measurement is, it's an eigenvalue plus or minus one, and it's plus or minus one. It's minus one to the sigma i. So the syndrome bit can be detected by measuring this operator. And when you measure, it changes your state. So if you start with a quantum state that suffers from a polyurethane, it does nothing to your quantum state. So you can measure, you can get your syndrome bit without changing the state of the system. So it's exactly what we want. And you can prove those two by using the postulates of quantum mechanics. Sigma i of e, it's this sigma i. It's the syndrome bit. So how do you measure a syndrome bit? You measure the corresponding stabilizer generator. And it's a proper quantum measurement. I haven't told you how to implement it, but it exists mathematically. It's the first step. Okay, so now let's look at our favorite example. So what is the syndrome of those three errors? So first the first one. One. So the syndrome, so first the syndrome, it's a vector that lives in which space? How many bits do you have? So you have one for each row. So you have six bits. So I want an answer that contains six bits. Okay, zero, zero, zero, zero, zero, yeah. Why? How do you see that? It commutes with everything. Did you check the commutation with everything? Okay, yes, that's the first stabilizer. So if it's a stabilizer, it commutes with all the other stabilizers. So the syndrome of stabilizers is always zero. And that makes sense. Those stabilizers are doing nothing to our quantum state. So we don't want to detect them. We don't care about them. They are not harmful. Another second one. This one is more harmful. So I look at X on qubit three. It's commuting with this row. It's commuting with this row, with this row. Anti-commute here, here, and that's all. So it has two non-trivial syndrome bits on those two bits, on bit three and four. So it's this one. What about the other one, X1, X2? So it's the same. Now, can you find an error with trivial syndrome? I know there are one. Sorry? All Xs? You could do all Xs. What if I tell you that there is a hint in this slide? Sorry, I cannot hear you. X1, X2, X3. How do you know that? Okay, so we are going to take the product of those two. And when we take the product of those two, the syndrome is a linear map. Because it's a linear map, the syndrome of the product of the two is going to be this syndrome plus this syndrome. They can sell out. So it's going to be zero. And is it a stabilizer? It's an operator with syndrome zero. The first answer you should have given me is this one. We did it already. So our second example is the sum of these two, but it's different. The operator X1, X2, X3 is not a stabilizer. It cannot be written as a product of the first three rows. It's an X operator, so only the first three rows can be used. And those rows, you can check that. Oops, okay. You can check that they overlap only on an even number of positions and they have weight four. So any product will have even weight. You cannot build X1, X2, X3 with even weight. So it's not a stabilizer. It's another type of error. It's something that you cannot detect, but that is not a stabilizer. It's an issue, right? You should detect it, but you cannot. And it's called a logical operator, a logical error. So now a logical error for a stabilizer code. It's a parli error with trivial syndrome. And there is two types of them. Those that we care about, that we want to correct, but we cannot, and those that we don't care about. So the stabilizers, we are fine with them. They do nothing to our code space, but those that are not stabilizer, I will call them non-trivial logical error. And those are gonna be changing your state. They change your state, but you cannot detect it. In that case, you just cannot correct. And when you have a stabilizer code, you can build those non-trivial logical errors. You can build all of them. You can build a family that generates them. And they look exactly like standard parli operators. They can be written as X1 bar, Z1 bar, up to Xk bar, Zk bar. So it's like logical parli operators. And they look the same in the sense that they satisfy the same commutation relation. So the X i bar and the Z i bar, anti-commute, but X i and Z j for a different value of j, commute. Just like if you remove the bar. So they behave the same. And when i and j are different, so of course the X i commutes and the Z i commutes. It's just a notation. They don't even have to be X type parli operator for X i bar, but it's a very convenient basis. And I'm gonna let you prove it using Gram-Schmidt. Okay, here is an example. We have our five qubit code and we can take X1 bar equal X, X, X, X and Z1 bar, which is a Z. They have syndrome zero and they anti-commute. And there is only one pair because there is one logical qubit. So it's fair to call them logical parli operators. Okay, and now we know that there is some error that we cannot detect. It's gonna give us a notion of distance. The maximum amount of parli errors, the maximum number of parli errors that we can detect is gonna define the minimum distance of a code. So it's the minimum weight of a non-trivial logical error. The minimum weight of an error that you cannot correct. And for instance, with our previous code, with the Sting code or the Hamming code, the parameters are seven, one, three where we add the distance when we know it because we saw that there is a weight three operator that is a logical operator. It has syndrome zero and we cannot detect it and it's not a stabilizer. So we saw that and it's a code with distance three. And the reason why we care about the distance because the number of parli errors you can correct is half the distance. So I'm gonna prove it quickly. So for that we define a decoder. By a decoder, I mean a mathematical decoder. Not an implementation yet, not an algorithm. It's a map that maps our syndrome, our bit string, onto a parli correction. So given a syndrome, we want to determine the correction to apply. And we say that the decoder works, the decoder corrects a parli error. If when you give it the syndrome of this error, it gives you back an error which is equivalent up to a stabilizer. It doesn't have to be the same but if it's the same up to a stabilizer, they have the same effect on the code because stabilizers are trivial. And we like to build minimum weight decoders. A minimum weight decoder, it's a decoder that returns a minimum weight correction. Why do we like that? It's because if you have a minimum weight decoder you can correct any parli error with weight up to D over two. So we can guarantee that. If we can find an algorithm that gives you a minimum weight correction for each syndrome, we are sure that we can correct any parli error acting on D over two qubits. This is the same thing as in the classical case. And I'm gonna skip the proof but we can discuss it if you want after the lecture. Okay, so how many errors can we correct with this code? How many parli errors? I think I heard the answer. One? How do you know? Okay. Okay, great. So it has this, oh, sorry. It has distance. I think the battery in this thing is dying. Oh, maybe it's not in here. It, okay. So it has distance three. So we can correct three over two. We can correct one error. We can correct any single qubit parli error with this code. So the decoder, it's something that identifies the parli error that occurred. After you find it, you need to apply it in some way to your hardware so you can apply the inverse of this correction. No, it does not, yeah. At least not in my definition. Okay, so we can correct one error. That's a great news. Yes, a minimum distance. So the minimum distance is the minimum weight of a non-trivial logical error. Ah, it's here, right? A minimum weight error. It's an error with syndrome sigma that has minimum weight in the set of error with syndrome sigma. So I look at all the possible errors with syndrome sigma. I know that the error which occurs as syndrome sigma, I'm gonna select the one that has minimum weight. And there may be multiple of them. That's why it's a minimum weight decoder. You may be, you have two solutions with weight one. In some cases, you pick one of them. Thank you. Okay, so we can correct one error. I'm gonna move from correcting one error to correcting many errors with many logical qubits. So we're gonna make a big step and this is what we call a good code. A good code, it's a code that, it's a family of code with parameters N, K, D and we want K and D to be good in the sense to be optimal. They are both linear in the length. That's the best we can do. So we can correct a linear number of logical qubits and we can correct a linear number of poly errors on these logical qubits, on all of them. So we can encode a lot of information and protect it well. It may seem surprising, but this code exists and I'm gonna show you that we can achieve a linear ratio with some constant here for K over N. So K is linear in N and this is the constant and D is linear in N and this is the constant delta. So I'm gonna give you a proof because I think it's a very pretty argument. It's a probabilistic method. We are not gonna construct explicitly these codes. We're gonna pick a random one and see that a random codes does achieve that. So it's gonna be a little bit of combinatorics. And the idea is to consider a random variable. We pick a random stabilizer code. So our random variable will be, when you pick a random code, it should be a Q, sorry. What is the number of poly errors that are not in the stabilizer group but that have syndrome zero and that have weight below our bound, delta N. So I'm counting the number of low-weight poly errors and my goal is to show that this number is zero. There exists a family of codes for which this number is zero. There is no low-weight logical error, non-trivial logical error. That means I have a large distance, right? And to do that, we do what we always do in the probabilistic method. We look at the average. We're gonna show that in average, a code can always give you, in average a code give you a very small number of Y. So we're gonna pick K over N which is small enough. I will tell you how small later. And we're gonna show that the number of code words in the ball with radius delta N goes to zero. The average number. And because the average number, because the average goes to zero, there is one family of codes for which this number is exactly zero. Do you know why? So if I tell you that the average value of something goes to zero, you cannot always conclude that this thing itself is zero. It could be very close to zero, but not exactly zero. It works here because this number we are counting is an integer. So if it were not zero, it will be at least one and then the average will not go to zero. So it's because there is a gap between zero and the smallest positive value, strictly positive value, that we can have this argument. And by definition, that means that the distance of the code is at least delta N. Okay, now how do we do that? We are gonna first start by counting stabilizer codes. So the question is, I take a stabilizer group with N minus K generators. So a stabilizer code with K logical qubits. And I want to know how many stabilizer codes there are. Is there any guess? You should not answer this question. Okay, I'm sure that was your guess. And we are gonna try to explain this number. So first I want to count the number of stabilizer codes. So I'm gonna count the number of choices for our independent generators, the SI. And I'm gonna divide by the number of possible choices of SI that give you the same group so that I don't over count. And this is this fraction. So the top is gonna be number of choices for N minus K generators. How do we choose our SI? So first, we know that when we have a stabilizer group, we can change the phase SI. We can multiply it by minus one. So for each of them, we have a plus or minus one freedom which give us two to the K possible choices. This is two to the N minus K. There is two choices for each of them. Then we are gonna choose the first generator, S1. What do we want for S1? We want S1 to be independent with itself. That means just non-zero. So a non-zero parli operator, the phase is already chosen. So it's two to the two N parli operators minus the zero, the identity. I will always confuse zero and identity. Sorry about that. Now let's choose the second one. We have chosen S1. It's fixed and we need a second generator such that they form a stabilizer group. So this second generator must commute with the first one. And the number of commuting operator is two to the two N minus one. There is half of them that commute, half of them that anti-commute. And we remove, we cannot take zero but we cannot take S1 either. So we remove two operators. Then we take the third one, it should be a three. And we want something that commute with the first two. There is only a quarter of the parli operator. So it's two to the two N minus four. And we remove any product of S1 and S2. There's four such products. And we keep going and taking the product of all these numbers, we get the numerator. So we get our first term and the numerator of this product. And then we do the same thing to compute the number of generating set for this operator. And this gives us the denominator. I will not do it, but it's a similar argument. Okay, now that we have that, we can do the same thing for counting a second set. We fix the parli error, a non-trivial parli error. And we want to count the number of stabilizer groups or stabilizer codes for which this error has a trivial syndrome. How many of the codes are detecting E and how many are not? And we can count that with the exact same argument, but adding the commutation with E. So we get a similar product. And now we are gonna use that right now. So let's get back to our proof. Our proof is let's count the number of non-trivial logical errors. So the number of non-trivial logical errors, we are gonna write it as a sum of random variable it's a standard technique where each XE takes the value zero or one. It takes the value one if the syndrome is zero because we want to count those errors with syndrome zero. So we are writing this number, let's say it's N. I'm gonna write N as one plus one plus one plus one N times. It seems very useless and trivial, but it's powerful when we take the expectation. By linearity of the expectation, now we can take the expectation of this thing which is zero or one. And the expectation of this thing is the probability that the syndrome is zero. And it happens that we just computed this thing. So the probability that the syndrome is zero is the number of codes for which the syndrome is zero divided by the total number of codes. We computed those two things. When I take the ratio of the two previous fractions, I get these products and if I remove the second term, minus two to the I on both side, I get exactly one half and a product N minus K times. So it's two to the minus N minus K. And when you think about it, it's what we expect. We expect that if we have an error and we select N minus K stabilizer generator, each of them has 50% chances to detect it. So the probability that we don't detect it is one over two to the number of stabilizer generators that we add. Each of them has 50% chances to detect it. Okay, now that we have that, we can get back to our expectation and we can plug in this bound on the probability to have a non-trivial syndrome. And this bound gives us some formula that comes from sterling formula from bounds on the binomial coefficients. And I'm going to directly skip to the N and here you see some polynomial, some small number times an exponentially small number which is going to dominate. So if it's two to the minus N times some constant, if this constant is strictly positive, then this expectation goes to zero and we are done. So we just need to figure out when this constant is strictly positive. And this is happening when we take the ratio K over N to be small enough. So we take K over N to be one minus H of delta minus this log minus epsilon. So as soon as we have this number of logical qubits, we are sure that we're gonna have a linear distance. That proves that we can build good quantum codes, good stabilizer codes. Okay, now what we want is not good stabilizer codes, we want good LDPC codes because again, they are not sparse. They are hard to measure. They are defined by high weight measurements. We want to move to low weight measurements. So I have seven minutes so I'm gonna go through three examples of construction and later I'm gonna discuss the proof of the parameter for one of them. So I'm just gonna give you the construction and let you check that it seems to work. They commute. The stabilizer generators do commute. And you're gonna see them in the problem session in details. So the kit I have to record is defined by taking a solution of the torus. So it's a square grid on the torus. So the green side on top is the same as the bottom side in the bottom. If you identify them on both opposite side, the red side is identified to the red side, you get a torus. And you're gonna place a qubit on each edge. The white circles are qubits. And you're gonna define a X-stabilizer generator for each vertex. So each vertex of the cellulation, each black vertex give you, oh, okay. So each black vertex like this one give you a stabilizer generator in X on the incident qubits. And each Z generator is defined from a face. And when you have a face and a star, they do commute. They overlap in two positions. This is kit I have codes. And it's also what led to the surface code which is one of the most popular codes today. We can generalize it to 3D. In 3D you take a cubic grid of a torus. Again, opposite sides are identified. This time there is three identifications. And it works for any cellulation of a three manifold more generally. So you place a qubit on each face. The face is something like that. There is horizontal face, vertical faces. There is three types of faces. So you have many qubits. And then you place X generators and Z generators. Like before, you're gonna place them on the dimension below and above. So when you have faces for your qubits, the dimension below is an edge. So when you have an edge, you place an X stabilizer on the four incident faces. And on the dimension above, above a face it's a three dimensional cell, a cube. So for each cube, you place a Z on each of the faces of the cube. So you have a weight six operator. And when you do that, you can see that the cube does commute with this cross operator. They overlap in two faces if you shift the cube. So it does define a proper stabilizer code. It does. It means that because you use higher weight stabilizer generators, it's gonna be easier to detect X errors than Z errors. And we are gonna see it in distance. The distance in X is gonna be bigger than the distance in Z. So there is a trade-off in 3D. And we can do something different. We can place qubits on edges. And we do the same thing. X generator on vertices, Z generator on faces. So all these codes are topological codes. They are defined from many fold. But there is another construction that I will discuss more in two details because it's based on graphs and it gives you better codes, better than all the topological codes. So I'm just gonna give you the example. You take two bipartite graphs. So two tanner graphs with squares, with checks and bits. You take two of them and you are gonna look at the Cartesian product. And you're gonna place qubits and stabilizers on the Cartesian product. So the Cartesian product has four types of vertices. There is circle, circle. So you place a qubit on each circle, circle. There's nine qubits here. There's also square, square. You are also gonna place a qubit on square, square. And then there is two other types on which we are gonna place X generators. So we're gonna do it, for instance, for each square, circle node here. We are gonna place the X check that is acting on the qubit connected vertically, given by the vertical tanner graph. So there is two incident qubits here. And horizontally, the only horizontal edge comes to this vertex. So we have a weight three stabilizer generator in X. And we can do the same thing in Z. But with the circle square nodes. When we have a circle square and we select one of them, it defines a stabilizer generator corresponding to the same thing, horizontal edges. Each horizontal edge connected to this square defines a qubit, give you a qubit. And vertical edges connected to this qubit, to this bit, give you a qubit. And what you can check is that because you move horizontally and vertically in parallel ways, in the same way on each row and each column, they are gonna commute. Here you see that they overlap on two qubits exactly. They overlap on two qubits exactly. And this is because of this square structure in the graph. So I'm gonna stop here. What I'm gonna show you is the parameters of these codes. This is a theorem that I'm gonna prove next time. Partly prove. And if I give you two classical codes where I give you the number of bits and the number of checks.