mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Add methods for topological sorts
A topological walk was previously only done in Terraform via the concurrent method used for walking the primary dependency graph in core. Sometime however we want a dependency ordering without the overhead of instantiating the concurrent walk with the channel-based edges. Add TopologicalOrder and ReverseTopologicalOrder to obtain a list of nodes which can be used to visit each while ensuring that all dependencies are satisfied.
This commit is contained in:
parent
95019e3d02
commit
ca272b2107
@ -179,6 +179,69 @@ type vertexAtDepth struct {
|
||||
Depth int
|
||||
}
|
||||
|
||||
// TopologicalOrder returns a topological sort of the given graph. The nodes
|
||||
// are not sorted, and any valid order may be returned. This function will
|
||||
// panic if it encounters a cycle.
|
||||
func (g *AcyclicGraph) TopologicalOrder() []Vertex {
|
||||
return g.topoOrder(upOrder)
|
||||
}
|
||||
|
||||
// ReverseTopologicalOrder returns a topological sort of the given graph,
|
||||
// following each edge in reverse. The nodes are not sorted, and any valid
|
||||
// order may be returned. This function will panic if it encounters a cycle.
|
||||
func (g *AcyclicGraph) ReverseTopologicalOrder() []Vertex {
|
||||
return g.topoOrder(downOrder)
|
||||
}
|
||||
|
||||
func (g *AcyclicGraph) topoOrder(order walkType) []Vertex {
|
||||
// Use a dfs-based sorting algorithm, similar to that used in
|
||||
// TransitiveReduction.
|
||||
sorted := make([]Vertex, 0, len(g.vertices))
|
||||
|
||||
// tmp track the current working node to check for cycles
|
||||
tmp := map[Vertex]bool{}
|
||||
|
||||
// perm tracks completed nodes to end the recursion
|
||||
perm := map[Vertex]bool{}
|
||||
|
||||
var visit func(v Vertex)
|
||||
|
||||
visit = func(v Vertex) {
|
||||
if perm[v] {
|
||||
return
|
||||
}
|
||||
|
||||
if tmp[v] {
|
||||
panic("cycle found in dag")
|
||||
}
|
||||
|
||||
tmp[v] = true
|
||||
var next Set
|
||||
switch {
|
||||
case order&downOrder != 0:
|
||||
next = g.downEdgesNoCopy(v)
|
||||
case order&upOrder != 0:
|
||||
next = g.upEdgesNoCopy(v)
|
||||
default:
|
||||
panic(fmt.Sprintln("invalid order", order))
|
||||
}
|
||||
|
||||
for _, u := range next {
|
||||
visit(u)
|
||||
}
|
||||
|
||||
tmp[v] = false
|
||||
perm[v] = true
|
||||
sorted = append(sorted, v)
|
||||
}
|
||||
|
||||
for _, v := range g.Vertices() {
|
||||
visit(v)
|
||||
}
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
type walkType uint64
|
||||
|
||||
const (
|
||||
|
@ -429,7 +429,7 @@ func TestAcyclicGraphWalkOrder(t *testing.T) {
|
||||
*/
|
||||
|
||||
var g AcyclicGraph
|
||||
for i := 0; i <= 11; i++ {
|
||||
for i := 1; i <= 11; i++ {
|
||||
g.Add(i)
|
||||
}
|
||||
g.Connect(BasicEdge(1, 3))
|
||||
@ -509,6 +509,41 @@ func TestAcyclicGraphWalkOrder(t *testing.T) {
|
||||
t.Errorf("expected visits:\n%v\ngot:\n%v\n", expect, visits)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TopologicalOrder", func(t *testing.T) {
|
||||
order := g.topoOrder(downOrder)
|
||||
|
||||
// Validate the order by checking it against the initial graph. We only
|
||||
// need to verify that each node has it's direct dependencies
|
||||
// satisfied.
|
||||
completed := map[Vertex]bool{}
|
||||
for _, v := range order {
|
||||
deps := g.DownEdges(v)
|
||||
for _, dep := range deps {
|
||||
if !completed[dep] {
|
||||
t.Fatalf("walking node %v, but dependency %v was not yet seen", v, dep)
|
||||
}
|
||||
}
|
||||
completed[v] = true
|
||||
}
|
||||
})
|
||||
t.Run("ReverseTopologicalOrder", func(t *testing.T) {
|
||||
order := g.topoOrder(upOrder)
|
||||
|
||||
// Validate the order by checking it against the initial graph. We only
|
||||
// need to verify that each node has it's direct dependencies
|
||||
// satisfied.
|
||||
completed := map[Vertex]bool{}
|
||||
for _, v := range order {
|
||||
deps := g.UpEdges(v)
|
||||
for _, dep := range deps {
|
||||
if !completed[dep] {
|
||||
t.Fatalf("walking node %v, but dependency %v was not yet seen", v, dep)
|
||||
}
|
||||
}
|
||||
completed[v] = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const testGraphTransReductionStr = `
|
||||
|
Loading…
Reference in New Issue
Block a user