github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/multi/weighted_directed.go (about) 1 // Copyright ©2014 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package multi 6 7 import ( 8 "fmt" 9 10 "github.com/jingcheng-WU/gonum/graph" 11 "github.com/jingcheng-WU/gonum/graph/iterator" 12 "github.com/jingcheng-WU/gonum/graph/set/uid" 13 ) 14 15 var ( 16 wdg *WeightedDirectedGraph 17 18 _ graph.Graph = wdg 19 _ graph.Weighted = wdg 20 _ graph.Directed = wdg 21 _ graph.WeightedDirected = wdg 22 _ graph.Multigraph = wdg 23 _ graph.DirectedMultigraph = wdg 24 _ graph.WeightedDirectedMultigraph = wdg 25 _ graph.NodeAdder = wdg 26 _ graph.NodeRemover = wdg 27 _ graph.WeightedLineAdder = wdg 28 _ graph.LineRemover = wdg 29 ) 30 31 // WeightedDirectedGraph implements a generalized directed graph. 32 type WeightedDirectedGraph struct { 33 // EdgeWeightFunc is used to provide 34 // the WeightFunc function for WeightedEdge 35 // values returned by the graph. 36 // WeightFunc must accept a nil input. 37 EdgeWeightFunc func(graph.WeightedLines) float64 38 39 nodes map[int64]graph.Node 40 from map[int64]map[int64]map[int64]graph.WeightedLine 41 to map[int64]map[int64]map[int64]graph.WeightedLine 42 43 nodeIDs *uid.Set 44 lineIDs map[int64]map[int64]*uid.Set 45 } 46 47 // NewWeightedDirectedGraph returns a WeightedDirectedGraph. 48 func NewWeightedDirectedGraph() *WeightedDirectedGraph { 49 return &WeightedDirectedGraph{ 50 nodes: make(map[int64]graph.Node), 51 from: make(map[int64]map[int64]map[int64]graph.WeightedLine), 52 to: make(map[int64]map[int64]map[int64]graph.WeightedLine), 53 54 nodeIDs: uid.NewSet(), 55 lineIDs: make(map[int64]map[int64]*uid.Set), 56 } 57 } 58 59 // AddNode adds n to the graph. It panics if the added node ID matches an existing node ID. 60 func (g *WeightedDirectedGraph) AddNode(n graph.Node) { 61 if _, exists := g.nodes[n.ID()]; exists { 62 panic(fmt.Sprintf("simple: node ID collision: %d", n.ID())) 63 } 64 g.nodes[n.ID()] = n 65 g.nodeIDs.Use(n.ID()) 66 } 67 68 // Edge returns the edge from u to v if such an edge exists and nil otherwise. 69 // The node v must be directly reachable from u as defined by the From method. 70 // The returned graph.Edge is a multi.WeightedEdge if an edge exists. 71 func (g *WeightedDirectedGraph) Edge(uid, vid int64) graph.Edge { 72 return g.WeightedEdge(uid, vid) 73 } 74 75 // Edges returns all the edges in the graph. Each edge in the returned slice 76 // is a multi.WeightedEdge. 77 func (g *WeightedDirectedGraph) Edges() graph.Edges { 78 if len(g.nodes) == 0 { 79 return graph.Empty 80 } 81 var edges []graph.Edge 82 for _, u := range g.nodes { 83 for _, e := range g.from[u.ID()] { 84 var lines []graph.WeightedLine 85 for _, l := range e { 86 lines = append(lines, l) 87 } 88 if len(lines) != 0 { 89 edges = append(edges, WeightedEdge{ 90 F: g.Node(u.ID()), 91 T: g.Node(lines[0].To().ID()), 92 WeightedLines: iterator.NewOrderedWeightedLines(lines), 93 WeightFunc: g.EdgeWeightFunc, 94 }) 95 } 96 } 97 } 98 if len(edges) == 0 { 99 return graph.Empty 100 } 101 return iterator.NewOrderedEdges(edges) 102 } 103 104 // From returns all nodes in g that can be reached directly from n. 105 // 106 // The returned graph.Nodes is only valid until the next mutation of 107 // the receiver. 108 func (g *WeightedDirectedGraph) From(id int64) graph.Nodes { 109 if len(g.from[id]) == 0 { 110 return graph.Empty 111 } 112 return iterator.NewNodesByWeightedLines(g.nodes, g.from[id]) 113 } 114 115 // HasEdgeBetween returns whether an edge exists between nodes x and y without 116 // considering direction. 117 func (g *WeightedDirectedGraph) HasEdgeBetween(xid, yid int64) bool { 118 if _, ok := g.from[xid][yid]; ok { 119 return true 120 } 121 _, ok := g.from[yid][xid] 122 return ok 123 } 124 125 // HasEdgeFromTo returns whether an edge exists in the graph from u to v. 126 func (g *WeightedDirectedGraph) HasEdgeFromTo(uid, vid int64) bool { 127 if _, ok := g.from[uid][vid]; !ok { 128 return false 129 } 130 return true 131 } 132 133 // Lines returns the lines from u to v if such any such lines exists and nil otherwise. 134 // The node v must be directly reachable from u as defined by the From method. 135 func (g *WeightedDirectedGraph) Lines(uid, vid int64) graph.Lines { 136 edge := g.from[uid][vid] 137 if len(edge) == 0 { 138 return graph.Empty 139 } 140 var lines []graph.Line 141 for _, l := range edge { 142 lines = append(lines, l) 143 } 144 return iterator.NewOrderedLines(lines) 145 } 146 147 // NewNode returns a new unique Node to be added to g. The Node's ID does 148 // not become valid in g until the Node is added to g. 149 func (g *WeightedDirectedGraph) NewNode() graph.Node { 150 if len(g.nodes) == 0 { 151 return Node(0) 152 } 153 if int64(len(g.nodes)) == uid.Max { 154 panic("simple: cannot allocate node: no slot") 155 } 156 return Node(g.nodeIDs.NewID()) 157 } 158 159 // NewWeightedLine returns a new WeightedLine from the source to the destination node. 160 // The returned WeightedLine will have a graph-unique ID. 161 // The Line's ID does not become valid in g until the Line is added to g. 162 func (g *WeightedDirectedGraph) NewWeightedLine(from, to graph.Node, weight float64) graph.WeightedLine { 163 fid := from.ID() 164 tid := to.ID() 165 var lineID int64 166 switch { 167 case g.lineIDs[fid] == nil: 168 uids := uid.NewSet() 169 lineID = uids.NewID() 170 g.lineIDs[fid] = map[int64]*uid.Set{tid: uids} 171 case g.lineIDs[fid][tid] == nil: 172 uids := uid.NewSet() 173 lineID = uids.NewID() 174 g.lineIDs[fid][tid] = uids 175 default: 176 lineID = g.lineIDs[fid][tid].NewID() 177 } 178 return WeightedLine{F: from, T: to, W: weight, UID: lineID} 179 } 180 181 // Node returns the node with the given ID if it exists in the graph, 182 // and nil otherwise. 183 func (g *WeightedDirectedGraph) Node(id int64) graph.Node { 184 return g.nodes[id] 185 } 186 187 // Nodes returns all the nodes in the graph. 188 // 189 // The returned graph.Nodes is only valid until the next mutation of 190 // the receiver. 191 func (g *WeightedDirectedGraph) Nodes() graph.Nodes { 192 if len(g.nodes) == 0 { 193 return graph.Empty 194 } 195 return iterator.NewNodes(g.nodes) 196 } 197 198 // NodeWithID returns a Node with the given ID if possible. If a graph.Node 199 // is returned that is not already in the graph NodeWithID will return true 200 // for new and the graph.Node must be added to the graph before use. 201 func (g *WeightedDirectedGraph) NodeWithID(id int64) (n graph.Node, new bool) { 202 n, ok := g.nodes[id] 203 if ok { 204 return n, false 205 } 206 return Node(id), true 207 } 208 209 // RemoveLine removes the line with the given end point and line IDs from the graph, 210 // leaving the terminal nodes. If the line does not exist it is a no-op. 211 func (g *WeightedDirectedGraph) RemoveLine(fid, tid, id int64) { 212 if _, ok := g.nodes[fid]; !ok { 213 return 214 } 215 if _, ok := g.nodes[tid]; !ok { 216 return 217 } 218 219 delete(g.from[fid][tid], id) 220 if len(g.from[fid][tid]) == 0 { 221 delete(g.from[fid], tid) 222 } 223 delete(g.to[tid][fid], id) 224 if len(g.to[tid][fid]) == 0 { 225 delete(g.to[tid], fid) 226 } 227 228 g.lineIDs[fid][tid].Release(id) 229 } 230 231 // RemoveNode removes the node with the given ID from the graph, as well as any edges attached 232 // to it. If the node is not in the graph it is a no-op. 233 func (g *WeightedDirectedGraph) RemoveNode(id int64) { 234 if _, ok := g.nodes[id]; !ok { 235 return 236 } 237 delete(g.nodes, id) 238 239 for from := range g.from[id] { 240 delete(g.to[from], id) 241 } 242 delete(g.from, id) 243 244 for to := range g.to[id] { 245 delete(g.from[to], id) 246 } 247 delete(g.to, id) 248 249 g.nodeIDs.Release(id) 250 } 251 252 // SetWeightedLine adds l, a line from one node to another. If the nodes do not exist, they are added 253 // and are set to the nodes of the line otherwise. 254 func (g *WeightedDirectedGraph) SetWeightedLine(l graph.WeightedLine) { 255 var ( 256 from = l.From() 257 fid = from.ID() 258 to = l.To() 259 tid = to.ID() 260 lid = l.ID() 261 ) 262 263 if _, ok := g.nodes[fid]; !ok { 264 g.AddNode(from) 265 } else { 266 g.nodes[fid] = from 267 } 268 if _, ok := g.nodes[tid]; !ok { 269 g.AddNode(to) 270 } else { 271 g.nodes[tid] = to 272 } 273 274 switch { 275 case g.from[fid] == nil: 276 g.from[fid] = map[int64]map[int64]graph.WeightedLine{tid: {lid: l}} 277 case g.from[fid][tid] == nil: 278 g.from[fid][tid] = map[int64]graph.WeightedLine{lid: l} 279 default: 280 g.from[fid][tid][lid] = l 281 } 282 switch { 283 case g.to[tid] == nil: 284 g.to[tid] = map[int64]map[int64]graph.WeightedLine{fid: {lid: l}} 285 case g.to[tid][fid] == nil: 286 g.to[tid][fid] = map[int64]graph.WeightedLine{lid: l} 287 default: 288 g.to[tid][fid][lid] = l 289 } 290 291 switch { 292 case g.lineIDs[fid] == nil: 293 uids := uid.NewSet() 294 g.lineIDs[fid] = map[int64]*uid.Set{tid: uids} 295 case g.lineIDs[fid][tid] == nil: 296 uids := uid.NewSet() 297 g.lineIDs[fid][tid] = uids 298 } 299 g.lineIDs[fid][tid].Use(lid) 300 } 301 302 // To returns all nodes in g that can reach directly to n. 303 // 304 // The returned graph.Nodes is only valid until the next mutation of 305 // the receiver. 306 func (g *WeightedDirectedGraph) To(id int64) graph.Nodes { 307 if len(g.to[id]) == 0 { 308 return graph.Empty 309 } 310 return iterator.NewNodesByWeightedLines(g.nodes, g.to[id]) 311 } 312 313 // Weight returns the weight for the lines between x and y summarised by the receiver's 314 // EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise. 315 func (g *WeightedDirectedGraph) Weight(uid, vid int64) (w float64, ok bool) { 316 lines := g.WeightedLines(uid, vid) 317 return WeightedEdge{WeightedLines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), lines != graph.Empty 318 } 319 320 // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. 321 // The node v must be directly reachable from u as defined by the From method. 322 // The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists. 323 func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { 324 lines := g.WeightedLines(uid, vid) 325 if lines == graph.Empty { 326 return nil 327 } 328 return WeightedEdge{ 329 F: g.Node(uid), T: g.Node(vid), 330 WeightedLines: lines, 331 WeightFunc: g.EdgeWeightFunc, 332 } 333 } 334 335 // WeightedEdges returns all the edges in the graph. Each edge in the returned slice 336 // is a multi.WeightedEdge. 337 func (g *WeightedDirectedGraph) WeightedEdges() graph.WeightedEdges { 338 if len(g.nodes) == 0 { 339 return graph.Empty 340 } 341 var edges []graph.WeightedEdge 342 for _, u := range g.nodes { 343 for _, e := range g.from[u.ID()] { 344 var lines []graph.WeightedLine 345 for _, l := range e { 346 lines = append(lines, l) 347 } 348 if len(lines) != 0 { 349 edges = append(edges, WeightedEdge{ 350 F: g.Node(u.ID()), 351 T: g.Node(lines[0].To().ID()), 352 WeightedLines: iterator.NewOrderedWeightedLines(lines), 353 WeightFunc: g.EdgeWeightFunc, 354 }) 355 } 356 } 357 } 358 if len(edges) == 0 { 359 return graph.Empty 360 } 361 return iterator.NewOrderedWeightedEdges(edges) 362 } 363 364 // WeightedLines returns the weighted lines from u to v if such any such lines exists 365 // and nil otherwise. The node v must be directly reachable from u as defined by the From method. 366 func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) graph.WeightedLines { 367 edge := g.from[uid][vid] 368 if len(edge) == 0 { 369 return graph.Empty 370 } 371 var lines []graph.WeightedLine 372 for _, l := range edge { 373 lines = append(lines, l) 374 } 375 return iterator.NewOrderedWeightedLines(lines) 376 }