So finally, we're going to take a look at the implications of having negative weights in directed weighted graphs. So the first thing to notice is that the easy solutions don't work at all. Dijkstra's algorithm just doesn't work in the presence of, presence of negative weights. So say, this weight from two to three is -nine. What Dijkstra's algorithm will do is just immediately select vertex three and never revisit that decision. Saying the shortest way to get from zero to three is, is, Is to take this edge of three which has weight two. But the actual shortest path goes over to four and then over to one and down to two for a weight of ten. And then a -nine makes it one. So there's a way to get from, zero to three with a weight path weight just one. Because of the -nine. But Dijkstra's algorithm won't find that. So that's not going to work and you might think, well, why don't we just make all the weights positive by adding nine to everything. Well, that's not going to work because a longer path will have nine added to it a lot of times, so its just not any relation between shortest paths in this graph and shortest paths in that graph. So, those easy attempts, just don't work for dealing with, negative weights, in general graphs. So the conclusion is we need some different algorithm and the top logical sort one isn't going to work cuz the graph might have a cycle in it, so there's no topological sort so we needs some different algorithm. And the main thing to think about in terms of weighted directed graphs, that might have cycles, Is that you can have this situation called a negative cycle. This is a graph, it all looks fine. It's just got this one negative edge from five to four of weight -0.66. And these other ones are 0.37 + 0.28. So that's +0.65. But if you go from five to four to seven to five, Then the distance around that is -0.01. And if we're trying to find, say a shortest path from zero to six, Well as soon as you hit this cycle if you want really want the shortest path, what you could do is spin around this well an infinite number of times. You could make the path a short as you want, if you hit an infinite, if you hit a negative cycle. So, negative cycles definitely can get in the way of trying to solve the shortest path problem. It make somewhat specified. So, the first thing is if there's no negative cycles, then there's a shortest path tree, and if there's a shortest path tree, there's no negative cycles assuming that you can actually get to the vertices. So what we'll want is graphs that have no negative cycles and then we can work with those. And is an algorithm an old algorithm known as the Bellman-Ford algorithm that does the job. It's only slightly more specific than the generic algorithm that we gave before. It just says so if you have V vertices, for just V times all you do is go through all the edges in the graph and relax each edge. So you make V passes through relaxing all the edges in the graph. And that's, an algorithm that finds shortest paths, in graphs that don't have any negative cycles. If there's a negative cycle, it, you know, we'll talk about what happens in a minute. So lets look at that one in terms of a demo. So we'll just take the list of edges in the graph it could be in any order, and we'll just relax them all. So to start out, we, set the distance to the source zero and everybody else's distance, infinity as usual. And then here's the list of edges. So we'll relax In this case, we start out with the edges sort of in the order that you'd get em' by walking through the whole graph so you get all the edges associated with each vertex together. So relax zero, one, relax zero, four, relax zero, seven, that's kind of the way Dijkstra would start out. And then, we go ahead and relax the edges that, come out from one, so one, two takes to, two for the first time, one, three takes us to three for the first time. One, seven is no help so we ignore it. Now we relax. Sorry, went too far. Two, three and two, six and those, that one takes us to six for the first time. And now three, six that does not give us a better way to six so we ignore it. Now we go to four, five that takes us to five for the first time. Four, six well we could get to four and nine and twenty to six so that's no help. Four, five that's no help. Now we do the ones from five, five, two that's going to give a better way to two so we've improved that. And five, six is going to give a better way to six. Now, seven, five that's no help. And seven, two is no help. So we've completed the first paths. And now we're just gonna go through and relax all the edges again. Now a lot of them are really just what we did before so they're not going to improve anything, like the ones at the beginning are not gonna improve anything. But before too long so still no improvement. But here, now the second time we relax two, three we have it gives us a better way to get to three. In, for the first path that didn't help us but the second path we relax two, three to get us to three and seventeen. Now that's going to change things for three, If we had already been through three it wouldn't, wouldn't matter. In this, in this, we'd take another path to deal with, but in this case, we're going to be coming through three later. And, there's another, two to six gets better. Then three to six well, two to six beat it so it doesn't help. And now I think, there's nothing else that helps in this example. And in fact, there's no further changes. Once we have the shortest path stream, which, we know from this example, because it's similar to one we did before then there's going to be no, no changes to it. You're not going to find any edges that successfully relax. And so, go through and, try them all, and in the end we have a shortest path stream. So that is an example of a Bellman-Ford algorithm, on a simple, simple graph. Here's the visualization of the Bellman-Ford algorithm running on a bigger graph. And here's what it looks like after four passes, seven, ten, thirteen. And this graph has 100 something vertices. And it finds the tree in a relatively small number of passes. And we'll talk about the performance in a second of. One thing is to be sure that the algorithm works. And there's a couple of ways to prove it. And again, the reason the proof has to be done with care is that there could be edges with negative weights but no negative cycles. The idea of the proof is that after you've done the i for pass, you've found the shortest path containing at most i edges. And, and we'll leave that proof for the book. Now there is a couple of ways to improve the Bellman-Ford algorithm in practice. And, the most important one is that if you didn't change the distance to a vertex during one pass, Then you don't have to worry about it in the next pass. You don't have to worry about it, it's edges in the next pass. And so, what we do in practice, is if you just maintain a queue of the vertices that we found shorter paths to, in each path. We want to make sure that we keep, at most, one copy of each vertex on the queue. Otherwise, you could wind up with situations where, the, queue, size of the queue, blows up. But that's easy to do, with, a vertex index array to keep track of that. And so, in the worst case, you could have all the vertices on the queue for, And then therefore all the edges relaxed, in every one of the V passes. But in practice not too many vertices really get relaxed. So, this is a, a quick summary of the algorithms that we've looked for, for, shortest paths, And, we didn't do the code for Bellman-Ford. We've done enough code today. It's quite simple code that you'll find in the book over on the book site. So, probably the simplest algorithm is when there are no directed cycles. And that's when we just topologically sort the vertices and then go through that list and relax every edge. So that's linear time. You relax all the edges in the graph and that's it. And that works even if there are negative weights. If there's no negative weights but there maybe cycles, then Dijkstra's algorithm works. And then we looked at E log V algorithms which are slightly faster depending on what kind of heap you use. The Bellman-Ford algorithm which works with negative weights as long as it's no negative cycles it's if, if you do the q you can get the in, in practice it turns out to be linear time for the times of graphs that arise in practice. Although in principle the worst case it could be E times V which is much to slow. So, directed cycles make the problem harder. You need a Dijkstra instead of top logical sort. Negative weights definitely make the problem harder. Because you might need, to, get the worst case of Bellman-Ford. And negative cycles, in the presence of negative cycles, it actually makes the problem intractable. And we'll talk about that a little bit at the end of the course. One thing that you can do with the Bellman-Ford algorithm is to at least find, find out if there's a negative cycle. In one practical reason to do that is maybe it has something to do with something else specified in the problem. And so if you can detect a negative cycle and deal with it that would be useful. And we'll look at another important practical reason for finding negative cycles in just a second. So anyway, since its a useful thing to do we're going to add two methods to the API. And that is does the graph have a negative cycle and in an interval? If it does have a negative cycle please give it to me. So for this graph, it would return true. And then it would give an iterable that would have these three edges that give me the negative cycle. So, the, observation or the way to find a negative cycle is to realize that what will happen with a Bellman-Ford algorithm if there's a negative cycle is that it'll every, every path through, it'll basically go around the cycle. Well not every pass in the, in the whole length in the cycle. It'll get stuck in a loop going around the cycle depending on the order of vertices. And it'll update and just get stuck going around the, around the cycle. So by testing what happens after Bellman-Ford is done, even if there's negative cycles present, we can find the negative cycles and that, in fact, If some vertex is updated in the last phase of the Bellman-Ford algorithm then there's a negative cycle. And not only that. Edge 2v is the last edge, on that cycle. That's the way you got to v. And you can trace back to find the cycle. So just run the Bellman-Ford algorithm and if some vertex gets, get updated, the last time through it means there's a negative cycle. In practice actually, you don't have to wait till the last phase. You can check these two entries for negative cycles, more frequently. But anyway, it's possible to find a negative cycle. And let's look at an application. This is an application that it really motives some people to understand the shortest paths to algorithms. And the idea is currency exchange. And so these are typical numbers that you might see in a newspaper table, or nowadays in an app on your mobile device that gives the exchange rates for various currencies. So to convert a dollar to euros using factor of points 0.741, I compute Euros too, Great Britain pounds, 0.888, and so forth. So this table is a table of exchange rates. And the problem is, is there an arbitrage opportunity? So what is an arbitrage. Well arbitrage is when just by making exchanges according to the legal rates in the table you can make money. So say we had a $1000. And then these are the going rates. Well we can convert that $1000 into 741 Euros. So now, if we take those 71 Euros and convert them into Canadian dollars it works out that we get 1,012.206 Canadian dollars. And if we take those Canadian dollars and convert them back to U.S. Dollars, it works out that we have $1,007. Well that's arbitrage and that's interesting. If we could go ahead and do for well let say $10,000 then would make $70 on the deal or a $100,000 would make $700 or maybe a million dollars would make $7,000 or maybe a billion would make $7,000,000. No reason to stop there. With arbitrage opportunity you're making money off the system. So of course there's intense interest in looking for opportunities like this. Course in modern financial market. It's there's many more things that you can trade than currencies. And these tables are big and complex. And the market is suppose to take care of eliminating these opportunities. But if you are using a computer and you've got a fast algorithm for finding negative cycles in directed graphs then maybe you can find the opportunity and take advantage of it before the market. So let's look at how it works. What we do is again model the situation with a graph. So we're going to have a vertex for every currency. And then on the edge corresponding to every transaction or every entry in the table so this is actually a complete directed graph. And the weight is equal to the exchange rate. And what we are trying to find is a directed cycle whose product of edge weights is greater than one. So that doesn't look like a shortest path problem although it's close. And actually it's very close. To convert this to a shortest path problem what we want to do is just take logs. If instead of using the exchange rate, we take minus the log of the exchange rate. And then multiplying turns to addition for looking for multiplying the exchange rates. That's the same as summing the logs. And, we took minus log it means that what we're looking for when we're trying to find products bigger than one. We're looking for a directed cycle whose sum of edge weights is less than zero. Find a directed cycle whose sum of edge weights is less than zero in a complete digraph. That's the negative cycle detection problem, and we just saw that, we can, do that with the, Bellman-Ford algorithm. And again, in a huge, directed graph in a modern trading situation, that's an extraordinarily valuable algorithm, and you can believe that you know, There are people out there running these algorithms in order to detect and take advantage of arbitrage opportunities. And if you don't have a fast algorithm, if you're using a slow one it'll be gone before you can take advantage of it. That's a fine example of the value of building efficient algorithm for a practical problem. So here's our summary of short as fast today. We've seen some great, classic algorithms that are, important and extraordinarily useful. First one is Dijkstra's algorithm which is, a fine, efficient workhorse algorithm that you see used, every day. And it's a, a grass search algorithm that is, similar to, Prim's, depth for search and rep for search that we've seen before and we'll see again if the graphs are, have no cycles which is a situation that arises in several important applications. We can do better than Dykstra's algorithm. It's easier. And also, negative weights are no problem, which enables us to solve scheduling problems in other examples If there's negative weights and negative cycles we can detect negative cycles. And if there aren't any negative cycles, we can find shortest paths. In, the general problem is intractable, and we'll come back to that. But the key point that I want people to remember from today's lecture is that shortest path is our first real example of a general problem solving model where there's a lot of important practical problems that we cast solve as shortest path problems. Number one and number two we have fast solutions to those problems, with these classic algorithms that we've talked about