Two parameters

A potential mended where it broke

A stored reweighting on a 256-vertex graph with negative arcs costs 10,045 relaxations to rebuild, and rebuilding it every time an update breaks it stops paying once half a per cent of arcs change between queries. Mending it from the arcs that broke costs 16 to 442 relaxations instead, and the stored potential stays at two thirds of the per-query cost at every rate of change. When the change is a vertex whose costs all move together, a repair reaches nearly every vertex. It still costs a third of a rebuild.

One Bellman–Ford buys every Dijkstra measured Johnson’s trick. A graph with negative arcs needs Bellman–Ford, which is slow, but one Bellman–Ford run from a virtual source gives every vertex a potential hh. Repriced by w+h(u)h(v)w + h(u) - h(v), every arc becomes non-negative, and every query after that can use Dijkstra.

How long a reweighting stays true put that trick into a stream of queries on a graph that changes. On a 256-vertex graph with 2,009 arcs, the reweighting cost 10,045 relaxations and paid for itself after under three queries. When arc costs were redrawn between queries, the stored potential broke whenever some repriced arc went negative. Recomputed from scratch each time, it stopped paying at about half a per cent of arcs redrawn.

It ended by pointing out waste in its own method. A recomputation starts again from the virtual source and throws away a potential that is still correct everywhere except behind the arcs that fell. When an arc (u,v)(u, v) falls so that its repriced cost is negative, the only values that can need to fall are those of vertices reachable from vv. The repair is the same relaxation Bellman–Ford performs, started at the damage and not at the source. The prediction was that single-arc repairs would be small, and that as more arcs fell together the repaired regions would overlap until a repair cost as much as a recomputation.

The first half of that holds. The second half does not.

The repair

The stored potential is the vector of shortest-path distances from a virtual source joined to every vertex at cost zero. After an update, the redrawn arcs are checked. Any arc whose repriced cost is negative is relaxed: h(v)h(v) is lowered to h(u)+wh(u) + w, and vv goes on a queue. Each vertex taken from the queue relaxes its outgoing arcs, and any neighbour whose value falls joins the queue. When the queue empties, every arc is non-negative again, and the potential is a valid one. That is checked over every arc after every repair, not assumed.

An arc whose cost rises never needs a repair. A potential only has to keep repriced costs non-negative, and a higher cost does that more easily. The stored values stop being exact shortest distances, but they remain a valid potential, and Dijkstra only needs validity.

The work is counted in the same relaxation attempts as the full reweighting: one check for each redrawn arc, and one attempt for each outgoing arc of each vertex taken from the queue.

Mending a broken potential keeps a stored reweighting at 0.65–0.71 of Bellman–Ford per query at every churn, where recomputing it climbs to 1.43Total counted work of 128 single-source queries on a 256-vertex graph with negative arcs, answered from a stored potential, divided by the work of Bellman–Ford from every query's source, against the share of arcs redrawn between queries. Recomputing the potential from scratch whenever an arc goes negative: 0.02% 0.731 with 4 reweightings, 0.1% 0.803 with 16 reweightings, 0.5% 1.015 with 62 reweightings, 2% 1.433 with 122 reweightings, 5% 1.410 with 129 reweightings, 20% 1.394 with 129 reweightings. Mending it from the broken arcs: 0.02% 0.710 with 3 repairs, 0.1% 0.699 with 15 repairs, 0.5% 0.677 with 57 repairs, 2% 0.671 with 92 repairs, 5% 0.652 with 109 repairs, 20% 0.647 with 92 repairs. One is where the stored potential stops paying. The horizontal axis is logarithmic.00.50011.50arcs redrawn between queriesstored potential ÷ Bellman–Ford per query0.02%0.1%0.5%2%5%20%recomputed from nothingmended from the broken arcs256 vertices, 128 queriesarc costs redrawn
Fig. 1 Total counted work of 128 single-source queries, answered from a stored potential, divided by the work of Bellman–Ford from every query’s source, against the share of the 2,009 arcs redrawn between queries. Recomputing the potential whenever it breaks: 0.731 at 0.02%, 0.803 at 0.1%, 1.015 at 0.5%, 1.433 at 2% and 1.394 at 20%. Mending it from the broken arcs: 0.710, 0.699, 0.677, 0.671, 0.652 and 0.647.

Mended instead of recomputed, the stored potential never stops paying. Across a thousandfold range of churn it costs between 0.647 and 0.710 of running Bellman–Ford for every query. Recomputed, it crosses one at half a per cent and costs 43% more than Bellman–Ford per query at two. The break-even the earlier plate drew was the price of recomputation, not of keeping a potential.

What a repair costs

Relaxations a repair makes against a full reweighting's 10,045: 17, 16, 28, 62, 128, 442 as redrawn arcs growMean relaxation attempts per repair of a stored potential, including the check of every redrawn arc, against the share of arcs redrawn between queries, on a 256-vertex graph over 128 queries: 0.02%: 3 repairs averaging 17 relaxations and 2.0 vertices changed; 0.1%: 15 repairs averaging 16 relaxations and 1.7 vertices changed; 0.5%: 57 repairs averaging 28 relaxations and 2.2 vertices changed; 2%: 92 repairs averaging 62 relaxations and 2.7 vertices changed; 5%: 109 repairs averaging 128 relaxations and 3.4 vertices changed; 20%: 92 repairs averaging 442 relaxations and 5.2 vertices changed. Recomputing the potential from the virtual source costs 10,045 each time, drawn as the dashed line. Both axes are logarithmic.0.02%0.1%0.5%2%5%20%10010³10⁴arcs redrawn between queriesrelaxations a repaira full reweightingone repair256 vertices, 128 queriesarc costs redrawn
Fig. 2 Mean relaxation attempts per repair, including the check of every redrawn arc, against the share of arcs redrawn, with a full reweighting’s 10,045 as the dashed line. 17 at 0.02%, 16 at 0.1%, 28 at 0.5%, 62 at 2%, 128 at 5% and 442 at 20%. The repairs change 2.0, 1.7, 2.2, 2.7, 3.4 and 5.2 vertices’ values on average.

A repair after a single broken arc makes about sixteen relaxations and changes about two vertices. At 20% of arcs redrawn between queries, about 400 arcs change at once, and a repair makes 442 relaxations. Almost all of those are the checks of the redrawn arcs themselves. The actual propagation still changes only five vertices.

The prediction expected overlapping repairs to grow into the whole graph, and here they do not, because in this model the damage never spreads. The reason is in how arcs are redrawn. Every cost is drawn as one to nine plus the difference of a hidden potential fixed when the graph was built. That keeps the question answerable, since no cycle can become negative, but it also means every new cost is consistent with the same hidden structure as the old one. A redrawn arc can fall below the stored potential by at most a few units, and the vertex at its head needs to fall by that much and no more. Its own outgoing arcs usually have that much slack, so the change stops there.

The reweighting leaves 277 of 2,009 arcs at exactly zero slack, and a redraw only breaks the potential when it falls further than the slack it lands onHow many of the 2,009 repriced arc costs take each value, on a 256-vertex graph whose raw costs run from one to nine plus the difference of a potential. An arc's repriced cost is its slack: a redraw breaks the potential only if the new cost is lower than the old one by more than that. 0: 277, 1: 120, 2: 155, 3: 175, 4: 202, 5: 215, 6: 192, 7: 158, 8: 167, 9: 116, 10: 91, 11: 46, 12: 44; above 12: 51. The 277 arcs at zero are the ones on shortest paths from the virtual source, and every one of them is broken by any redraw at all that lowers it.0139277012345678910111213+repriced cost — the arc's slackarcs256 vertices, 2,009 arcsdark: zero slack, broken by any fall
Fig. 3 How many of the 2,009 repriced arc costs take each value on the 256-vertex graph. 277 arcs sit at exactly zero, 120 at one, and the distribution runs past twelve. An arc at zero breaks the potential if its cost falls at all, and an arc with slack s absorbs a fall of s before its head’s value needs to change.

The slack distribution is what how long a reweighting stays true measured to explain how often a potential breaks. It also explains how far a break spreads. A fall of a few units at a vertex passes to a neighbour only through an arc whose slack is smaller than the fall. Most arcs have more slack than that, so a repair dies out within a step or two.

That makes the arc model the easy case for a repair, and saying so is part of the result. The flat line on the first plate is partly a property of how the updates were generated. A fair test needs updates whose effects are designed to spread.

Updates that spread

A different kind of change spreads by construction. Instead of redrawing one arc’s cost, lower one vertex’s hidden potential by up to forty units. Every arc leaving that vertex becomes cheaper by the same amount and every arc entering it dearer. Costs stay consistent with a potential, so no negative cycle appears. But every vertex downstream of the moved one can now be reached more cheaply from the virtual source, and a strongly connected random graph has almost everything downstream of almost everything.

Relaxations a repair makes against a full reweighting's 10,045: 2,704, 3,040, 3,322, 3,680, 3,710, 3,901 as moving vertices growMean relaxation attempts per repair of a stored potential, including the check of every redrawn arc, against the share of vertices whose hidden potential falls between queries, on a 256-vertex graph over 128 queries: 0.4%: 80 repairs averaging 2,704 relaxations and 232.4 vertices changed; 1%: 120 repairs averaging 3,040 relaxations and 244.9 vertices changed; 2%: 127 repairs averaging 3,322 relaxations and 253.8 vertices changed; 5%: 128 repairs averaging 3,680 relaxations and 252.6 vertices changed; 10%: 128 repairs averaging 3,710 relaxations and 250.4 vertices changed; 30%: 128 repairs averaging 3,901 relaxations and 243.7 vertices changed. Recomputing the potential from the virtual source costs 10,045 each time, drawn as the dashed line. Both axes are logarithmic.0.4%1%2%5%10%30%10⁴vertices whose hidden potential falls between queriesrelaxations a repaira full reweightingone repair256 vertices, 128 querieshidden potentials move
Fig. 4 Mean relaxations per repair when vertices’ hidden potentials fall between queries, against the share of vertices moved, with the full reweighting’s 10,045 as the dashed line. 2,704 at 0.4%, 3,040 at 1%, 3,322 at 2%, 3,680 at 5%, 3,710 at 10% and 3,901 at 30%. Each repair changes between 232 and 254 of the 256 vertices’ values.

Under this model the repaired region really is the whole graph. A single moved vertex, at 0.4%, already changes 232 vertices’ values on average, and at 2% it is 254 of 256. That is the overlap the prediction expected. The cost still does not reach a recomputation. A repair touching nearly every vertex makes 2,704 to 3,901 relaxations, a quarter to two fifths of the 10,045 a rebuild makes.

The difference is in what each method relaxes. A rebuild from the virtual source runs passes over all 2,009 arcs until a pass changes nothing, five passes here. It relaxes every arc in every pass whether or not its tail moved. The queue relaxes an arc only when its tail’s value has just fallen. A repair that changes 250 vertices, each dequeued once or twice with eight outgoing arcs, makes a few thousand relaxations, because it never looks at an arc whose tail did not change. Covering the whole graph is not the same as scanning it repeatedly.

When a vertex's hidden potential moves, recomputing costs 1.35 to 1.72 of Bellman–Ford per query, and mending 0.87 to 1.04Total counted work of 128 single-source queries on a 256-vertex graph with negative arcs, answered from a stored potential, divided by the work of Bellman–Ford from every query's source, against the share of vertices whose hidden potential falls between queries. Recomputing the potential from scratch whenever an arc goes negative: 0.4% 1.349 with 82 reweightings, 1% 1.693 with 121 reweightings, 2% 1.613 with 128 reweightings, 5% 1.724 with 129 reweightings, 10% 1.596 with 129 reweightings, 30% 1.645 with 129 reweightings. Mending it from the broken arcs: 0.4% 0.873 with 80 repairs, 1% 0.945 with 120 repairs, 2% 0.983 with 127 repairs, 5% 1.010 with 128 repairs, 10% 1.026 with 128 repairs, 30% 1.043 with 128 repairs. One is where the stored potential stops paying. The horizontal axis is logarithmic.00.50011.50vertices whose hidden potential falls between queriesstored potential ÷ Bellman–Ford per query0.4%1%2%5%10%30%recomputed from nothingmended from the broken arcs256 vertices, 128 querieshidden potentials move
Fig. 5 The total-work ratio when vertices’ hidden potentials move. Recomputing the potential whenever it breaks: 1.349 at 0.4% of vertices moved, 1.693 at 1% and between 1.596 and 1.724 up to 30%. Mending it: 0.873, 0.945, 0.983, 1.010, 1.026 and 1.043.

In total work, the harder model sits near the break-even line with mending and well above it without. A single moved vertex between queries leaves the mended potential at 0.873 of Bellman–Ford per query. The recomputed potential is at 1.349 and has already lost. From about 5% of vertices moving, the mended potential is within a few per cent of breaking even. Here every query breaks the potential, and the repairs cost about a third of a Bellman–Ford run each. A Dijkstra query plus a third of a rebuild comes to roughly one Bellman–Ford run.

Vertices a repair changes, of 256: 2.0, 1.7, 2.2, 2.7, 3.4, 5.2 when arcs are redrawn, and 232, 245, 254, 253, 250, 244 when vertices moveThe mean number of vertices whose stored potential a repair lowers, of 256. Arcs redrawn 0.02%: 2.0. Arcs redrawn 0.1%: 1.7. Arcs redrawn 0.5%: 2.2. Arcs redrawn 2%: 2.7. Arcs redrawn 5%: 3.4. Arcs redrawn 20%: 5.2. Vertices move 0.4%: 232.4. Vertices move 1%: 244.9. Vertices move 2%: 253.8. Vertices move 5%: 252.6. Vertices move 10%: 250.4. Vertices move 30%: 243.7. Redrawing an arc's cost from the same construction breaks the potential locally; lowering a vertex's hidden potential lowers the distance to everything downstream of it.vertices changed by a repair, of 256arcs redrawn 0.02%2.0arcs redrawn 0.1%1.7arcs redrawn 0.5%2.2arcs redrawn 2%2.7arcs redrawn 5%3.4arcs redrawn 20%5.2vertices move 0.4%232.4vertices move 1%244.9vertices move 2%253.8vertices move 5%252.6vertices move 10%250.4vertices move 30%243.7256 vertices, 128 querieslight: arcs redrawn · dark: vertices move
Fig. 6 Vertices whose stored potential a repair changes, of 256. When arc costs are redrawn: 2.0, 1.7, 2.2, 2.7, 3.4 and 5.2, at 0.02% to 20% of arcs. When vertices’ hidden potentials fall: 232.4, 244.9, 253.8, 252.6, 250.4 and 243.7, at 0.4% to 30% of vertices.

The two models bracket the question. In one, a repair touches a couple of vertices. In the other, it touches almost all of them. Real updates presumably fall between: a congestion change raises or lowers one road, and a price change moves everything leaving one node. The repair wins against recomputation at both extremes, by roughly six hundred times at one and three times at the other.

Finding what broke

A repair has to start from the broken arcs, and finding them has a cost of its own that belongs in the count. Here each update touches a known set of arcs, and only those are checked: one relaxation attempt each, included in every repair cost above. That is what makes the arc model’s 442 relaxations at 20% churn almost entirely checking. About 400 arcs change, each is checked once, and only a handful are found broken.

A system that does not know which arcs changed cannot do this. It would have to scan all 2,009 arcs to find the broken ones, which is two fifths of a full reweighting spent before the repair starts. Most systems that change arc costs do so through an interface that knows which arc it touched, so the cheaper case is the realistic one. It is still an assumption, and a potential shared between a writer that changes costs and a reader that answers queries needs that set passed between them.

The recompute policy on the earlier plate checked the same way. The validity check before deciding to recompute is not charged to either policy, because both need it. The comparison between them is a comparison of what happens after a break is found.

The same waste in landmark tables

Route search has another kind of stored distance, and it has the same problem. Where the landmarks stand gave a search four tables of exact distances from chosen cells, and what the queries know that the map does not chose those cells from past traffic. A landmark table is a potential in the sense used here: distances from one source, used to price a search. When a cell’s cost changes, the table’s entries downstream of it are wrong.

A landmark bound is a difference of two stored distances, so unlike a potential it can go wrong when costs change in either direction. For falls the repair is the same queue-driven relaxation started at the fallen cell, rises would need values raised, which this repair never does, and the same two cases would apply: a local cost change repairs a few entries, and a change that moves everything downstream repairs most of the table at a fraction of a rebuild. Nothing here measures landmark tables under updates. The mechanism transfers directly, and a system holding several landmark tables pays the rebuild’s cost once per table.

Why a queue and not passes

The whole difference between the two policies is the shape of the work, and it is worth making precise.

Bellman–Ford from a virtual source runs in passes. Each pass relaxes every arc once, and the passes stop when one changes nothing. On this graph that is five passes of 2,009 arcs. The pass structure is what makes Bellman–Ford correct without any bookkeeping, and it is also what makes it wasteful after a small change: the first pass after an update re-relaxes two thousand arcs to find the handful whose tails moved.

A queue keeps exactly that bookkeeping. A vertex enters the queue when its value falls, and only its outgoing arcs are relaxed when it leaves. A relaxation happens only where something changed one step earlier. Counting on a graph measured traversals that do work in proportion to what they reach and not to what exists, and the repair is a traversal of the same kind: its cost is the degree times the number of times a vertex is dequeued.

That is also why the vertex model’s repairs, which reach nearly every vertex, still cost a third of a rebuild. At 2% of vertices moving, a repair makes 3,322 relaxations and changes 254 vertices, which with eight outgoing arcs a vertex works out to each changed vertex being dequeued about 1.6 times, and costing about thirteen relaxations. Reaching nearly all 256 vertices that way costs about 3,300, against a rebuild that relaxes all 2,009 arcs five times over. The general form is the one the cost is the number of subproblems set out for dynamic programming: after a change, the work that matters is the number of stored values that actually change, and a method that finds only those beats one that recomputes all of them.

Why the earlier crossing was the wrong question

The earlier essay’s break-even, half a per cent of arcs, looked like a property of a potential under change. It was a property of a policy: throwing the potential away whenever one arc broke it. The repair shows how much of that cost was the policy. Under the arc model the stored potential’s advantage is nearly constant across a thousandfold range of churn, so the crossing disappears. Under the vertex model a crossing exists, near 5% of vertices moving between queries, and it sits there because Dijkstra’s own cost plus a third of a rebuild adds up to one Bellman–Ford.

That last observation is the general form of the result. With repair, a stored potential pays as long as

Dijkstra+repair<Bellman–Ford from the source,\text{Dijkstra} + \text{repair} < \text{Bellman–Ford from the source},

query by query. The repair’s cost is bounded by the size of the region it changes times the degree, not by the graph’s arcs times its passes. On this graph Dijkstra with repriced arcs costs about two thirds of a Bellman–Ford run, so repairs can use up about a third before the stored potential stops paying. What amortised means is the page about spreading a large cost over many operations. Here the large cost has mostly gone. What remains is a per-query cost, and it is small because the queue does work only where values move.

What is settled and what is not

Settled, by counting on a 256-vertex, 2,009-arc graph over 128 queries, with every repaired potential checked valid on every arc: with arc costs redrawn between queries, mending costs 16 to 442 relaxations per repair and keeps the stored potential at 0.647 to 0.710 of Bellman–Ford per query from 0.02% to 20% churn, where recomputing reaches 1.43. With vertices’ hidden potentials lowered between queries, repairs change 232 to 254 vertices and cost 2,704 to 3,901 relaxations, against 10,045 to recompute. The mended potential there costs 0.87 to 1.04 of Bellman–Ford per query and the recomputed one 1.35 to 1.72.

Not settled:

Queue order. The queue here is first in, first out. The order of a label-correcting repair changes how many times a vertex is re-queued, and a priority queue keyed on the size of the fall might halve the relaxations in the vertex model. It would add heap comparisons, and nothing here measures the trade.

Rises that should be tightened. Rising costs are left alone, since they never break validity. The potential drifts away from exact distances, and a looser potential makes Dijkstra do more work. Some of the vertex model’s slow rise in the per-query ratio as churn grows may be that drift. It is not separated here.

Larger graphs. At 256 vertices a repair covering the whole graph is a few thousand relaxations. On a road network of millions of vertices, a vertex-model change reaches only its own component, and whether that is most of the network depends on the network. The claim here is about one graph size.

Still open: a potential that is never tightened

The repair lowers values and never raises them, so over a long stream the stored potential becomes a looser and looser bound. A looser potential leaves more slack on arcs, which makes future repairs smaller. It also leaves more slack on the arcs Dijkstra explores, which makes queries larger. Those two effects pull in opposite directions, and the stream length at which a potential should be tightened, by a full recomputation or by a repair that also raises values, is a genuine optimum nobody has measured here.

The measurement that follows runs streams of thousands of queries under both models, with an optional tightening every so many queries. It counts the repairs’ work, the queries’ work and the tightening’s work separately, and it measures the stored potential’s mean distance from exact shortest distances as the stream goes on. The prediction is that under the arc model tightening never pays, because the drift is small, and that under the vertex model there is an interval of a few dozen queries at which a periodic recomputation lowers the total. If both hold, the right policy depends on which kind of change a system sees, and the potential’s own drift is the measurement that tells it which.

Named alongside this one

Essays reaching for the same objects. Nobody chose these; they are what the concept index makes visible.

The objects this essay names

Each one links to every other essay that touches it.

AmortisationBellman–FordBreak-evenDijkstra's algorithmDynamic graphHonest limitInvalidationNegative weightPotential functionRelaxationReweightingShortest path