github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/dag/dag.go (about) 1 package dag 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/iaas-resource-provision/iaas-rpc/internal/tfdiags" 9 10 "github.com/hashicorp/go-multierror" 11 ) 12 13 // AcyclicGraph is a specialization of Graph that cannot have cycles. 14 type AcyclicGraph struct { 15 Graph 16 } 17 18 // WalkFunc is the callback used for walking the graph. 19 type WalkFunc func(Vertex) tfdiags.Diagnostics 20 21 // DepthWalkFunc is a walk function that also receives the current depth of the 22 // walk as an argument 23 type DepthWalkFunc func(Vertex, int) error 24 25 func (g *AcyclicGraph) DirectedGraph() Grapher { 26 return g 27 } 28 29 // Returns a Set that includes every Vertex yielded by walking down from the 30 // provided starting Vertex v. 31 func (g *AcyclicGraph) Ancestors(v Vertex) (Set, error) { 32 s := make(Set) 33 memoFunc := func(v Vertex, d int) error { 34 s.Add(v) 35 return nil 36 } 37 38 if err := g.DepthFirstWalk(g.downEdgesNoCopy(v), memoFunc); err != nil { 39 return nil, err 40 } 41 42 return s, nil 43 } 44 45 // Returns a Set that includes every Vertex yielded by walking up from the 46 // provided starting Vertex v. 47 func (g *AcyclicGraph) Descendents(v Vertex) (Set, error) { 48 s := make(Set) 49 memoFunc := func(v Vertex, d int) error { 50 s.Add(v) 51 return nil 52 } 53 54 if err := g.ReverseDepthFirstWalk(g.upEdgesNoCopy(v), memoFunc); err != nil { 55 return nil, err 56 } 57 58 return s, nil 59 } 60 61 // Root returns the root of the DAG, or an error. 62 // 63 // Complexity: O(V) 64 func (g *AcyclicGraph) Root() (Vertex, error) { 65 roots := make([]Vertex, 0, 1) 66 for _, v := range g.Vertices() { 67 if g.upEdgesNoCopy(v).Len() == 0 { 68 roots = append(roots, v) 69 } 70 } 71 72 if len(roots) > 1 { 73 // TODO(mitchellh): make this error message a lot better 74 return nil, fmt.Errorf("multiple roots: %#v", roots) 75 } 76 77 if len(roots) == 0 { 78 return nil, fmt.Errorf("no roots found") 79 } 80 81 return roots[0], nil 82 } 83 84 // TransitiveReduction performs the transitive reduction of graph g in place. 85 // The transitive reduction of a graph is a graph with as few edges as 86 // possible with the same reachability as the original graph. This means 87 // that if there are three nodes A => B => C, and A connects to both 88 // B and C, and B connects to C, then the transitive reduction is the 89 // same graph with only a single edge between A and B, and a single edge 90 // between B and C. 91 // 92 // The graph must be valid for this operation to behave properly. If 93 // Validate() returns an error, the behavior is undefined and the results 94 // will likely be unexpected. 95 // 96 // Complexity: O(V(V+E)), or asymptotically O(VE) 97 func (g *AcyclicGraph) TransitiveReduction() { 98 // For each vertex u in graph g, do a DFS starting from each vertex 99 // v such that the edge (u,v) exists (v is a direct descendant of u). 100 // 101 // For each v-prime reachable from v, remove the edge (u, v-prime). 102 for _, u := range g.Vertices() { 103 uTargets := g.downEdgesNoCopy(u) 104 105 g.DepthFirstWalk(g.downEdgesNoCopy(u), func(v Vertex, d int) error { 106 shared := uTargets.Intersection(g.downEdgesNoCopy(v)) 107 for _, vPrime := range shared { 108 g.RemoveEdge(BasicEdge(u, vPrime)) 109 } 110 111 return nil 112 }) 113 } 114 } 115 116 // Validate validates the DAG. A DAG is valid if it has a single root 117 // with no cycles. 118 func (g *AcyclicGraph) Validate() error { 119 if _, err := g.Root(); err != nil { 120 return err 121 } 122 123 // Look for cycles of more than 1 component 124 var err error 125 cycles := g.Cycles() 126 if len(cycles) > 0 { 127 for _, cycle := range cycles { 128 cycleStr := make([]string, len(cycle)) 129 for j, vertex := range cycle { 130 cycleStr[j] = VertexName(vertex) 131 } 132 133 err = multierror.Append(err, fmt.Errorf( 134 "Cycle: %s", strings.Join(cycleStr, ", "))) 135 } 136 } 137 138 // Look for cycles to self 139 for _, e := range g.Edges() { 140 if e.Source() == e.Target() { 141 err = multierror.Append(err, fmt.Errorf( 142 "Self reference: %s", VertexName(e.Source()))) 143 } 144 } 145 146 return err 147 } 148 149 func (g *AcyclicGraph) Cycles() [][]Vertex { 150 var cycles [][]Vertex 151 for _, cycle := range StronglyConnected(&g.Graph) { 152 if len(cycle) > 1 { 153 cycles = append(cycles, cycle) 154 } 155 } 156 return cycles 157 } 158 159 // Walk walks the graph, calling your callback as each node is visited. 160 // This will walk nodes in parallel if it can. The resulting diagnostics 161 // contains problems from all graphs visited, in no particular order. 162 func (g *AcyclicGraph) Walk(cb WalkFunc) tfdiags.Diagnostics { 163 w := &Walker{Callback: cb, Reverse: true} 164 w.Update(g) 165 return w.Wait() 166 } 167 168 // simple convenience helper for converting a dag.Set to a []Vertex 169 func AsVertexList(s Set) []Vertex { 170 vertexList := make([]Vertex, 0, len(s)) 171 for _, raw := range s { 172 vertexList = append(vertexList, raw.(Vertex)) 173 } 174 return vertexList 175 } 176 177 type vertexAtDepth struct { 178 Vertex Vertex 179 Depth int 180 } 181 182 // DepthFirstWalk does a depth-first walk of the graph starting from 183 // the vertices in start. 184 func (g *AcyclicGraph) DepthFirstWalk(start Set, f DepthWalkFunc) error { 185 seen := make(map[Vertex]struct{}) 186 frontier := make([]*vertexAtDepth, 0, len(start)) 187 for _, v := range start { 188 frontier = append(frontier, &vertexAtDepth{ 189 Vertex: v, 190 Depth: 0, 191 }) 192 } 193 for len(frontier) > 0 { 194 // Pop the current vertex 195 n := len(frontier) 196 current := frontier[n-1] 197 frontier = frontier[:n-1] 198 199 // Check if we've seen this already and return... 200 if _, ok := seen[current.Vertex]; ok { 201 continue 202 } 203 seen[current.Vertex] = struct{}{} 204 205 // Visit the current node 206 if err := f(current.Vertex, current.Depth); err != nil { 207 return err 208 } 209 210 for _, v := range g.downEdgesNoCopy(current.Vertex) { 211 frontier = append(frontier, &vertexAtDepth{ 212 Vertex: v, 213 Depth: current.Depth + 1, 214 }) 215 } 216 } 217 218 return nil 219 } 220 221 // SortedDepthFirstWalk does a depth-first walk of the graph starting from 222 // the vertices in start, always iterating the nodes in a consistent order. 223 func (g *AcyclicGraph) SortedDepthFirstWalk(start []Vertex, f DepthWalkFunc) error { 224 seen := make(map[Vertex]struct{}) 225 frontier := make([]*vertexAtDepth, len(start)) 226 for i, v := range start { 227 frontier[i] = &vertexAtDepth{ 228 Vertex: v, 229 Depth: 0, 230 } 231 } 232 for len(frontier) > 0 { 233 // Pop the current vertex 234 n := len(frontier) 235 current := frontier[n-1] 236 frontier = frontier[:n-1] 237 238 // Check if we've seen this already and return... 239 if _, ok := seen[current.Vertex]; ok { 240 continue 241 } 242 seen[current.Vertex] = struct{}{} 243 244 // Visit the current node 245 if err := f(current.Vertex, current.Depth); err != nil { 246 return err 247 } 248 249 // Visit targets of this in a consistent order. 250 targets := AsVertexList(g.downEdgesNoCopy(current.Vertex)) 251 sort.Sort(byVertexName(targets)) 252 253 for _, t := range targets { 254 frontier = append(frontier, &vertexAtDepth{ 255 Vertex: t, 256 Depth: current.Depth + 1, 257 }) 258 } 259 } 260 261 return nil 262 } 263 264 // ReverseDepthFirstWalk does a depth-first walk _up_ the graph starting from 265 // the vertices in start. 266 func (g *AcyclicGraph) ReverseDepthFirstWalk(start Set, f DepthWalkFunc) error { 267 seen := make(map[Vertex]struct{}) 268 frontier := make([]*vertexAtDepth, 0, len(start)) 269 for _, v := range start { 270 frontier = append(frontier, &vertexAtDepth{ 271 Vertex: v, 272 Depth: 0, 273 }) 274 } 275 for len(frontier) > 0 { 276 // Pop the current vertex 277 n := len(frontier) 278 current := frontier[n-1] 279 frontier = frontier[:n-1] 280 281 // Check if we've seen this already and return... 282 if _, ok := seen[current.Vertex]; ok { 283 continue 284 } 285 seen[current.Vertex] = struct{}{} 286 287 for _, t := range g.upEdgesNoCopy(current.Vertex) { 288 frontier = append(frontier, &vertexAtDepth{ 289 Vertex: t, 290 Depth: current.Depth + 1, 291 }) 292 } 293 294 // Visit the current node 295 if err := f(current.Vertex, current.Depth); err != nil { 296 return err 297 } 298 } 299 300 return nil 301 } 302 303 // SortedReverseDepthFirstWalk does a depth-first walk _up_ the graph starting from 304 // the vertices in start, always iterating the nodes in a consistent order. 305 func (g *AcyclicGraph) SortedReverseDepthFirstWalk(start []Vertex, f DepthWalkFunc) error { 306 seen := make(map[Vertex]struct{}) 307 frontier := make([]*vertexAtDepth, len(start)) 308 for i, v := range start { 309 frontier[i] = &vertexAtDepth{ 310 Vertex: v, 311 Depth: 0, 312 } 313 } 314 for len(frontier) > 0 { 315 // Pop the current vertex 316 n := len(frontier) 317 current := frontier[n-1] 318 frontier = frontier[:n-1] 319 320 // Check if we've seen this already and return... 321 if _, ok := seen[current.Vertex]; ok { 322 continue 323 } 324 seen[current.Vertex] = struct{}{} 325 326 // Add next set of targets in a consistent order. 327 targets := AsVertexList(g.upEdgesNoCopy(current.Vertex)) 328 sort.Sort(byVertexName(targets)) 329 for _, t := range targets { 330 frontier = append(frontier, &vertexAtDepth{ 331 Vertex: t, 332 Depth: current.Depth + 1, 333 }) 334 } 335 336 // Visit the current node 337 if err := f(current.Vertex, current.Depth); err != nil { 338 return err 339 } 340 } 341 342 return nil 343 } 344 345 // byVertexName implements sort.Interface so a list of Vertices can be sorted 346 // consistently by their VertexName 347 type byVertexName []Vertex 348 349 func (b byVertexName) Len() int { return len(b) } 350 func (b byVertexName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 351 func (b byVertexName) Less(i, j int) bool { 352 return VertexName(b[i]) < VertexName(b[j]) 353 }