github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/graph/dag.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package graph 21 22 import ( 23 "errors" 24 "fmt" 25 "sort" 26 ) 27 28 type DAG struct { 29 vertices map[Vertex]Vertex 30 edges map[Edge]Edge 31 } 32 33 type Vertex interface{} 34 35 type Edge interface { 36 From() Vertex 37 To() Vertex 38 } 39 40 type realEdge struct { 41 F, T Vertex 42 } 43 44 // WalkFunc defines the action should be taken when we walk through the DAG. 45 // the func is vertex basis 46 type WalkFunc func(v Vertex) error 47 48 var _ Edge = &realEdge{} 49 50 func (r *realEdge) From() Vertex { 51 return r.F 52 } 53 54 func (r *realEdge) To() Vertex { 55 return r.T 56 } 57 58 // AddVertex puts 'v' into 'd' 59 func (d *DAG) AddVertex(v Vertex) bool { 60 if v == nil { 61 return false 62 } 63 d.vertices[v] = v 64 return true 65 } 66 67 // RemoveVertex deletes 'v' from 'd' 68 // the in&out edges are also deleted 69 func (d *DAG) RemoveVertex(v Vertex) bool { 70 if v == nil { 71 return true 72 } 73 for k := range d.edges { 74 if k.From() == v || k.To() == v { 75 delete(d.edges, k) 76 } 77 } 78 delete(d.vertices, v) 79 return true 80 } 81 82 // Vertices returns all vertices in 'd' 83 func (d *DAG) Vertices() []Vertex { 84 vertices := make([]Vertex, 0) 85 for v := range d.vertices { 86 vertices = append(vertices, v) 87 } 88 return vertices 89 } 90 91 // AddEdge puts edge 'e' into 'd' 92 func (d *DAG) AddEdge(e Edge) bool { 93 if e.From() == nil || e.To() == nil { 94 return false 95 } 96 for k := range d.edges { 97 if k.From() == e.From() && k.To() == e.To() { 98 return true 99 } 100 } 101 d.edges[e] = e 102 return true 103 } 104 105 // RemoveEdge deletes edge 'e' 106 func (d *DAG) RemoveEdge(e Edge) bool { 107 for k := range d.edges { 108 if k.From() == e.From() && k.To() == e.To() { 109 delete(d.edges, k) 110 } 111 } 112 return true 113 } 114 115 // Connect vertex 'from' to 'to' by a new edge if not exist 116 func (d *DAG) Connect(from, to Vertex) bool { 117 if from == nil || to == nil { 118 return false 119 } 120 for k := range d.edges { 121 if k.From() == from && k.To() == to { 122 return true 123 } 124 } 125 edge := RealEdge(from, to) 126 d.edges[edge] = edge 127 return true 128 } 129 130 // AddConnect add 'to' to the DAG 'd' and connect 'from' to 'to' 131 func (d *DAG) AddConnect(from, to Vertex) bool { 132 if !d.AddVertex(to) { 133 return false 134 } 135 return d.Connect(from, to) 136 } 137 138 // AddConnectRoot add 'v' to the DAG 'd' and connect root to 'v' 139 func (d *DAG) AddConnectRoot(v Vertex) bool { 140 root := d.Root() 141 if root == nil { 142 return false 143 } 144 return d.AddConnect(root, v) 145 } 146 147 // WalkTopoOrder walks the DAG 'd' in topology order 148 func (d *DAG) WalkTopoOrder(walkFunc WalkFunc, less func(v1, v2 Vertex) bool) error { 149 if err := d.validate(); err != nil { 150 return err 151 } 152 orders := d.topologicalOrder(false, less) 153 for _, v := range orders { 154 if err := walkFunc(v); err != nil { 155 return err 156 } 157 } 158 return nil 159 } 160 161 // WalkReverseTopoOrder walks the DAG 'd' in reverse topology order 162 func (d *DAG) WalkReverseTopoOrder(walkFunc WalkFunc, less func(v1, v2 Vertex) bool) error { 163 if err := d.validate(); err != nil { 164 return err 165 } 166 orders := d.topologicalOrder(true, less) 167 for _, v := range orders { 168 if err := walkFunc(v); err != nil { 169 return err 170 } 171 } 172 return nil 173 } 174 175 // WalkBFS walks the DAG 'd' in breadth-first order 176 func (d *DAG) WalkBFS(walkFunc WalkFunc) error { 177 return d.bfs(walkFunc, nil) 178 } 179 180 func (d *DAG) bfs(walkFunc WalkFunc, less func(v1, v2 Vertex) bool) error { 181 if err := d.validate(); err != nil { 182 return err 183 } 184 queue := make([]Vertex, 0) 185 walked := make(map[Vertex]bool, len(d.Vertices())) 186 187 root := d.Root() 188 queue = append(queue, root) 189 for len(queue) > 0 { 190 var walkErr error 191 for _, vertex := range queue { 192 if err := walkFunc(vertex); err != nil { 193 walkErr = err 194 } 195 } 196 if walkErr != nil { 197 return walkErr 198 } 199 200 nextStep := make([]Vertex, 0) 201 for _, vertex := range queue { 202 adjs := d.outAdj(vertex) 203 if less != nil { 204 sort.SliceStable(adjs, func(i, j int) bool { 205 return less(adjs[i], adjs[j]) 206 }) 207 } 208 for _, adj := range adjs { 209 if !walked[adj] { 210 nextStep = append(nextStep, adj) 211 walked[adj] = true 212 } 213 } 214 } 215 queue = nextStep 216 } 217 218 return nil 219 } 220 221 // Equals tells whether two DAGs are equal 222 // `less` tells whether vertex 'v1' is less than vertex 'v2'. 223 // `less` should return false if 'v1' equals to 'v2'. 224 func (d *DAG) Equals(other *DAG, less func(v1, v2 Vertex) bool) bool { 225 if other == nil || less == nil { 226 return false 227 } 228 // sort both DAGs in topology order. 229 // a DAG may have more than one topology order, func 'less' is used to eliminate randomness 230 // and hence only one deterministic order is generated. 231 vertices1 := d.topologicalOrder(false, less) 232 vertices2 := other.topologicalOrder(false, less) 233 234 // compare them 235 if len(vertices1) != len(vertices2) { 236 return false 237 } 238 for i := range vertices1 { 239 if less(vertices1[i], vertices2[i]) || less(vertices2[i], vertices1[i]) { 240 return false 241 } 242 } 243 return true 244 } 245 246 // Root returns root vertex that has no in adjacent. 247 // our DAG should have one and only one root vertex 248 func (d *DAG) Root() Vertex { 249 roots := make([]Vertex, 0) 250 for n := range d.vertices { 251 if len(d.inAdj(n)) == 0 { 252 roots = append(roots, n) 253 } 254 } 255 if len(roots) != 1 { 256 return nil 257 } 258 return roots[0] 259 } 260 261 func (d *DAG) Merge(subDag *DAG) { 262 for v := range subDag.vertices { 263 d.AddConnectRoot(v) 264 } 265 for e := range subDag.edges { 266 d.AddEdge(e) 267 } 268 } 269 270 // String returns a string representation of the DAG in topology order 271 func (d *DAG) String() string { 272 str := "|" 273 walkFunc := func(v Vertex) error { 274 str += fmt.Sprintf("->%v", v) 275 return nil 276 } 277 if err := d.WalkReverseTopoOrder(walkFunc, nil); err != nil { 278 return "->err" 279 } 280 return str 281 } 282 283 // validate 'd' has single Root and has no cycles 284 func (d *DAG) validate() error { 285 // single Root validation 286 root := d.Root() 287 if root == nil { 288 return errors.New("no single Root found") 289 } 290 291 // self-cycle validation 292 for e := range d.edges { 293 if e.From() == e.To() { 294 return fmt.Errorf("self-cycle found: %v", e.From()) 295 } 296 } 297 298 // cycle validation 299 // use a DFS func to find cycles 300 walked := make(map[Vertex]bool) 301 marked := make(map[Vertex]bool) 302 var walk func(v Vertex) error 303 walk = func(v Vertex) error { 304 if walked[v] { 305 return nil 306 } 307 if marked[v] { 308 return errors.New("cycle found") 309 } 310 311 marked[v] = true 312 adjacent := d.outAdj(v) 313 for _, vertex := range adjacent { 314 if err := walk(vertex); err != nil { 315 return err 316 } 317 } 318 marked[v] = false 319 walked[v] = true 320 return nil 321 } 322 for v := range d.vertices { 323 if err := walk(v); err != nil { 324 return err 325 } 326 } 327 return nil 328 } 329 330 // topologicalOrder returns a vertex list that is in topology order 331 // 'd' MUST be a legal DAG 332 func (d *DAG) topologicalOrder(reverse bool, less func(v1, v2 Vertex) bool) []Vertex { 333 // orders is what we want, a (reverse) topological order of this DAG 334 orders := make([]Vertex, 0) 335 336 // walked marks vertex has been walked, to stop recursive func call 337 walked := make(map[Vertex]bool) 338 339 // walk is a DFS func 340 var walk func(v Vertex) 341 walk = func(v Vertex) { 342 if walked[v] { 343 return 344 } 345 var adjacent []Vertex 346 if reverse { 347 adjacent = d.outAdj(v) 348 } else { 349 adjacent = d.inAdj(v) 350 } 351 if less != nil { 352 sort.SliceStable(adjacent, func(i, j int) bool { 353 return less(adjacent[i], adjacent[j]) 354 }) 355 } 356 for _, vertex := range adjacent { 357 walk(vertex) 358 } 359 walked[v] = true 360 orders = append(orders, v) 361 } 362 vertexLst := d.Vertices() 363 if less != nil { 364 sort.SliceStable(vertexLst, func(i, j int) bool { 365 return less(vertexLst[i], vertexLst[j]) 366 }) 367 } 368 for _, v := range vertexLst { 369 walk(v) 370 } 371 return orders 372 } 373 374 // outAdj returns all adjacent vertices that v points to 375 func (d *DAG) outAdj(v Vertex) []Vertex { 376 vertices := make([]Vertex, 0) 377 for e := range d.edges { 378 if e.From() == v { 379 vertices = append(vertices, e.To()) 380 } 381 } 382 return vertices 383 } 384 385 // inAdj returns all adjacent vertices that point to v 386 func (d *DAG) inAdj(v Vertex) []Vertex { 387 vertices := make([]Vertex, 0) 388 for e := range d.edges { 389 if e.To() == v { 390 vertices = append(vertices, e.From()) 391 } 392 } 393 return vertices 394 } 395 396 // NewDAG news an empty DAG 397 func NewDAG() *DAG { 398 dag := &DAG{ 399 vertices: make(map[Vertex]Vertex), 400 edges: make(map[Edge]Edge), 401 } 402 return dag 403 } 404 405 func RealEdge(from, to Vertex) Edge { 406 return &realEdge{F: from, T: to} 407 }