gonum.org/v1/gonum@v0.14.0/graph/multi/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 "gonum.org/v1/gonum/graph" 11 "gonum.org/v1/gonum/graph/iterator" 12 "gonum.org/v1/gonum/graph/set/uid" 13 ) 14 15 var ( 16 ug *UndirectedGraph 17 18 _ graph.Graph = ug 19 _ graph.Undirected = ug 20 _ graph.Multigraph = ug 21 _ graph.UndirectedMultigraph = ug 22 _ graph.NodeAdder = ug 23 _ graph.NodeRemover = ug 24 _ graph.LineAdder = ug 25 _ graph.LineRemover = ug 26 ) 27 28 // UndirectedGraph implements a generalized undirected graph. 29 type UndirectedGraph struct { 30 nodes map[int64]graph.Node 31 lines map[int64]map[int64]map[int64]graph.Line 32 33 nodeIDs *uid.Set 34 lineIDs map[int64]map[int64]*uid.Set 35 } 36 37 // NewUndirectedGraph returns an UndirectedGraph. 38 func NewUndirectedGraph() *UndirectedGraph { 39 return &UndirectedGraph{ 40 nodes: make(map[int64]graph.Node), 41 lines: make(map[int64]map[int64]map[int64]graph.Line), 42 43 nodeIDs: uid.NewSet(), 44 lineIDs: make(map[int64]map[int64]*uid.Set), 45 } 46 } 47 48 // AddNode adds n to the graph. It panics if the added node ID matches an existing node ID. 49 func (g *UndirectedGraph) AddNode(n graph.Node) { 50 if _, exists := g.nodes[n.ID()]; exists { 51 panic(fmt.Sprintf("simple: node ID collision: %d", n.ID())) 52 } 53 g.nodes[n.ID()] = n 54 g.nodeIDs.Use(n.ID()) 55 } 56 57 // Edge returns the edge from u to v if such an edge exists and nil otherwise. 58 // The node v must be directly reachable from u as defined by the From method. 59 // The returned graph.Edge is a multi.Edge if an edge exists. 60 func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge { 61 l := g.LinesBetween(uid, vid) 62 if l == graph.Empty { 63 return nil 64 } 65 return Edge{F: g.Node(uid), T: g.Node(vid), Lines: l} 66 } 67 68 // EdgeBetween returns the edge between nodes x and y. 69 func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge { 70 return g.Edge(xid, yid) 71 } 72 73 // Edges returns all the edges in the graph. Each edge in the returned slice 74 // is a multi.Edge. 75 // 76 // The returned graph.Edges is only valid until the next mutation of 77 // the receiver. 78 func (g *UndirectedGraph) Edges() graph.Edges { 79 if len(g.lines) == 0 { 80 return graph.Empty 81 } 82 var edges []graph.Edge 83 for xid, u := range g.lines { 84 for yid, lines := range u { 85 if yid < xid { 86 // Do not consider lines when the To node ID is 87 // before the From node ID. Both orientations 88 // are stored. 89 continue 90 } 91 if len(lines) == 0 { 92 continue 93 } 94 edges = append(edges, Edge{ 95 F: g.Node(xid), 96 T: g.Node(yid), 97 Lines: iterator.NewLines(lines), 98 }) 99 } 100 } 101 if len(edges) == 0 { 102 return graph.Empty 103 } 104 return iterator.NewOrderedEdges(edges) 105 } 106 107 // From returns all nodes in g that can be reached directly from n. 108 // 109 // The returned graph.Nodes is only valid until the next mutation of 110 // the receiver. 111 func (g *UndirectedGraph) From(id int64) graph.Nodes { 112 if len(g.lines[id]) == 0 { 113 return graph.Empty 114 } 115 return iterator.NewNodesByLines(g.nodes, g.lines[id]) 116 } 117 118 // HasEdgeBetween returns whether an edge exists between nodes x and y. 119 func (g *UndirectedGraph) HasEdgeBetween(xid, yid int64) bool { 120 _, ok := g.lines[xid][yid] 121 return ok 122 } 123 124 // Lines returns the lines from u to v if such an edge exists and nil otherwise. 125 // The node v must be directly reachable from u as defined by the From method. 126 func (g *UndirectedGraph) Lines(uid, vid int64) graph.Lines { 127 return g.LinesBetween(uid, vid) 128 } 129 130 // LinesBetween returns the lines between nodes x and y. 131 func (g *UndirectedGraph) LinesBetween(xid, yid int64) graph.Lines { 132 if !g.HasEdgeBetween(xid, yid) { 133 return graph.Empty 134 } 135 var lines []graph.Line 136 for _, l := range g.lines[xid][yid] { 137 if l.From().ID() != xid { 138 l = l.ReversedLine() 139 } 140 lines = append(lines, l) 141 } 142 return iterator.NewOrderedLines(lines) 143 } 144 145 // NewLine returns a new Line from the source to the destination node. 146 // The returned Line will have a graph-unique ID. 147 // The Line's ID does not become valid in g until the Line is added to g. 148 func (g *UndirectedGraph) NewLine(from, to graph.Node) graph.Line { 149 xid := from.ID() 150 yid := to.ID() 151 if yid < xid { 152 xid, yid = yid, xid 153 } 154 var lineID int64 155 switch { 156 case g.lineIDs[xid] == nil: 157 uids := uid.NewSet() 158 lineID = uids.NewID() 159 g.lineIDs[xid] = map[int64]*uid.Set{yid: uids} 160 case g.lineIDs[xid][yid] == nil: 161 uids := uid.NewSet() 162 lineID = uids.NewID() 163 g.lineIDs[xid][yid] = uids 164 default: 165 lineID = g.lineIDs[xid][yid].NewID() 166 } 167 return Line{F: from, T: to, UID: lineID} 168 } 169 170 // NewNode returns a new unique Node to be added to g. The Node's ID does 171 // not become valid in g until the Node is added to g. 172 func (g *UndirectedGraph) NewNode() graph.Node { 173 if len(g.nodes) == 0 { 174 return Node(0) 175 } 176 if int64(len(g.nodes)) == uid.Max { 177 panic("simple: cannot allocate node: no slot") 178 } 179 return Node(g.nodeIDs.NewID()) 180 } 181 182 // Node returns the node with the given ID if it exists in the graph, 183 // and nil otherwise. 184 func (g *UndirectedGraph) Node(id int64) graph.Node { 185 return g.nodes[id] 186 } 187 188 // Nodes returns all the nodes in the graph. 189 // 190 // The returned graph.Nodes is only valid until the next mutation of 191 // the receiver. 192 func (g *UndirectedGraph) Nodes() graph.Nodes { 193 if len(g.nodes) == 0 { 194 return graph.Empty 195 } 196 return iterator.NewNodes(g.nodes) 197 } 198 199 // NodeWithID returns a Node with the given ID if possible. If a graph.Node 200 // is returned that is not already in the graph NodeWithID will return true 201 // for new and the graph.Node must be added to the graph before use. 202 func (g *UndirectedGraph) NodeWithID(id int64) (n graph.Node, new bool) { 203 n, ok := g.nodes[id] 204 if ok { 205 return n, false 206 } 207 return Node(id), true 208 } 209 210 // RemoveLine removes the line with the given end point and line Ids from the graph, leaving 211 // the terminal nodes. If the line does not exist it is a no-op. 212 func (g *UndirectedGraph) RemoveLine(fid, tid, id int64) { 213 if _, ok := g.nodes[fid]; !ok { 214 return 215 } 216 if _, ok := g.nodes[tid]; !ok { 217 return 218 } 219 220 delete(g.lines[fid][tid], id) 221 if len(g.lines[fid][tid]) == 0 { 222 delete(g.lines[fid], tid) 223 } 224 delete(g.lines[tid][fid], id) 225 if len(g.lines[tid][fid]) == 0 { 226 delete(g.lines[tid], fid) 227 } 228 229 xid := fid 230 yid := tid 231 if yid < xid { 232 xid, yid = yid, xid 233 } 234 g.lineIDs[xid][yid].Release(id) 235 } 236 237 // RemoveNode removes the node with the given ID from the graph, as well as any edges attached 238 // to it. If the node is not in the graph it is a no-op. 239 func (g *UndirectedGraph) RemoveNode(id int64) { 240 if _, ok := g.nodes[id]; !ok { 241 return 242 } 243 delete(g.nodes, id) 244 245 for from := range g.lines[id] { 246 delete(g.lines[from], id) 247 } 248 delete(g.lines, id) 249 250 g.nodeIDs.Release(id) 251 } 252 253 // SetLine adds l, a line from one node to another. If the nodes do not exist, they are added 254 // and are set to the nodes of the line otherwise. 255 func (g *UndirectedGraph) SetLine(l graph.Line) { 256 var ( 257 from = l.From() 258 fid = from.ID() 259 to = l.To() 260 tid = to.ID() 261 lid = l.ID() 262 ) 263 264 if _, ok := g.nodes[fid]; !ok { 265 g.AddNode(from) 266 } else { 267 g.nodes[fid] = from 268 } 269 if _, ok := g.nodes[tid]; !ok { 270 g.AddNode(to) 271 } else { 272 g.nodes[tid] = to 273 } 274 275 switch { 276 case g.lines[fid] == nil: 277 g.lines[fid] = map[int64]map[int64]graph.Line{tid: {lid: l}} 278 case g.lines[fid][tid] == nil: 279 g.lines[fid][tid] = map[int64]graph.Line{lid: l} 280 default: 281 g.lines[fid][tid][lid] = l 282 } 283 switch { 284 case g.lines[tid] == nil: 285 g.lines[tid] = map[int64]map[int64]graph.Line{fid: {lid: l}} 286 case g.lines[tid][fid] == nil: 287 g.lines[tid][fid] = map[int64]graph.Line{lid: l} 288 default: 289 g.lines[tid][fid][lid] = l 290 } 291 292 xid := fid 293 yid := tid 294 if yid < xid { 295 xid, yid = yid, xid 296 } 297 switch { 298 case g.lineIDs[xid] == nil: 299 uids := uid.NewSet() 300 g.lineIDs[xid] = map[int64]*uid.Set{yid: uids} 301 case g.lineIDs[xid][yid] == nil: 302 uids := uid.NewSet() 303 g.lineIDs[xid][yid] = uids 304 } 305 g.lineIDs[xid][yid].Use(lid) 306 }