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