-
Notifications
You must be signed in to change notification settings - Fork 547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
graph/path: Improve performance of YenKShortestPaths
#2004
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This variant can terminate early when the target is reached. This can make a large difference in running time for Yen's k-shortest paths algorithm, as it makes many calls to the shortest path subroutine.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -20,8 +20,30 @@ import ( | |||||||
// | ||||||||
// The time complexity of DijkstraFrom is O(|E|.log|V|). | ||||||||
func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest { | ||||||||
return dijkstraFrom(u, nil, g) | ||||||||
} | ||||||||
|
||||||||
// DijkstraFromTo returns a shortest path from u to v in the graph g. The | ||||||||
// result is equivalent to DijkstraFrom(u, g).To(v), but DijkstraFromTo can be | ||||||||
// more efficient, as it can terminate early if v is reached. If the graph does | ||||||||
// not implement Weighted, UniformCost is used. DijkstraFromTo will panic if g | ||||||||
// has a u-reachable negative edge weight that is discovered before reaching v. | ||||||||
// | ||||||||
// The time complexity of DijkstraFromTo is O(|E|.log|V|). | ||||||||
func DijkstraFromTo(u graph.Node, v graph.Node, g traverse.Graph) (path []graph.Node, weight float64) { | ||||||||
// An implementation of Bidirectional Dijkstra's algorithm could be even | ||||||||
// more efficient, but it requires a transposed (or undirected) graph. | ||||||||
if v == nil { | ||||||||
panic("dijkstra: nil target node") | ||||||||
} | ||||||||
return dijkstraFrom(u, v, g).To(v.ID()) | ||||||||
} | ||||||||
|
||||||||
// Shared implementation of DijkstraFrom and DijkstraFromTo. | ||||||||
func dijkstraFrom(u, target graph.Node, g traverse.Graph) Shortest { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
var path Shortest | ||||||||
if h, ok := g.(graph.Graph); ok { | ||||||||
// Use the incremental version when a target is provided | ||||||||
if h, ok := g.(graph.Graph); target == nil && ok { | ||||||||
if h.Node(u.ID()) == nil { | ||||||||
return Shortest{from: u} | ||||||||
} | ||||||||
|
@@ -58,6 +80,9 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest { | |||||||
continue | ||||||||
} | ||||||||
mnid := mid.node.ID() | ||||||||
if target != nil && mnid == target.ID() { | ||||||||
break | ||||||||
} | ||||||||
to := g.From(mnid) | ||||||||
for to.Next() { | ||||||||
v := to.Node() | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,29 @@ func TestDijkstraFrom(t *testing.T) { | |
g.SetWeightedEdge(e) | ||
} | ||
|
||
checkPath := func(typ string, p []graph.Node, weight float64) { | ||
if weight != test.Weight { | ||
t.Errorf("%q %s: unexpected weight from To: got:%f want:%f", | ||
test.Name, typ, weight, test.Weight) | ||
} | ||
|
||
var got []int64 | ||
for _, n := range p { | ||
got = append(got, n.ID()) | ||
} | ||
ok := len(got) == 0 && len(test.WantPaths) == 0 | ||
for _, sp := range test.WantPaths { | ||
if reflect.DeepEqual(got, sp) { | ||
ok = true | ||
break | ||
} | ||
} | ||
if !ok { | ||
t.Errorf("%q %s: unexpected shortest path:\ngot: %v\nwant from:%v", | ||
test.Name, typ, p, test.WantPaths) | ||
} | ||
} | ||
|
||
for _, tg := range []struct { | ||
typ string | ||
g traverse.Graph | ||
|
@@ -56,38 +79,41 @@ func TestDijkstraFrom(t *testing.T) { | |
t.Fatalf("%q %s: unexpected from node ID: got:%d want:%d", test.Name, tg.typ, pt.From().ID(), test.Query.From().ID()) | ||
} | ||
|
||
p, weight := pt.To(test.Query.To().ID()) | ||
if weight != test.Weight { | ||
t.Errorf("%q %s: unexpected weight from To: got:%f want:%f", | ||
test.Name, tg.typ, weight, test.Weight) | ||
} | ||
if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight { | ||
t.Errorf("%q %s: unexpected weight from Weight: got:%f want:%f", | ||
test.Name, tg.typ, weight, test.Weight) | ||
} | ||
|
||
var got []int64 | ||
for _, n := range p { | ||
got = append(got, n.ID()) | ||
} | ||
ok := len(got) == 0 && len(test.WantPaths) == 0 | ||
for _, sp := range test.WantPaths { | ||
if reflect.DeepEqual(got, sp) { | ||
ok = true | ||
break | ||
} | ||
} | ||
if !ok { | ||
t.Errorf("%q %s: unexpected shortest path:\ngot: %v\nwant from:%v", | ||
test.Name, tg.typ, p, test.WantPaths) | ||
} | ||
p, weight := pt.To(test.Query.To().ID()) | ||
checkPath(tg.typ, p, weight) | ||
|
||
np, weight := pt.To(test.NoPathFor.To().ID()) | ||
if pt.From().ID() == test.NoPathFor.From().ID() && (np != nil || !math.IsInf(weight, 1)) { | ||
t.Errorf("%q %s: unexpected path:\ngot: path=%v weight=%f\nwant:path=<nil> weight=+Inf", | ||
test.Name, tg.typ, np, weight) | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be in the loop, with helper sharing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
// Test the single-destination variant. | ||
var ( | ||
p []graph.Node | ||
weight float64 | ||
panicked bool | ||
) | ||
func() { | ||
defer func() { | ||
panicked = recover() != nil | ||
}() | ||
p, weight = DijkstraFromTo(test.Query.From(), test.Query.To(), g.(graph.Graph)) | ||
}() | ||
if panicked || test.HasNegativeWeight { | ||
if !test.HasNegativeWeight { | ||
t.Errorf("%q: unexpected panic", test.Name) | ||
} | ||
continue | ||
} | ||
|
||
checkPath("from-to", p, weight) | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second param is not a v.