github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/simple/dense_undirected_matrix.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 simple 6 7 import ( 8 "sort" 9 10 "github.com/jingcheng-WU/gonum/graph" 11 "github.com/jingcheng-WU/gonum/graph/internal/ordered" 12 "github.com/jingcheng-WU/gonum/graph/iterator" 13 "github.com/jingcheng-WU/gonum/mat" 14 ) 15 16 var ( 17 um *UndirectedMatrix 18 19 _ graph.Graph = um 20 _ graph.Undirected = um 21 _ edgeSetter = um 22 _ weightedEdgeSetter = um 23 ) 24 25 // UndirectedMatrix represents an undirected graph using an adjacency 26 // matrix such that all IDs are in a contiguous block from 0 to n-1. 27 // Edges are stored implicitly as an edge weight, so edges stored in 28 // the graph are not recoverable. 29 type UndirectedMatrix struct { 30 mat *mat.SymDense 31 nodes []graph.Node 32 33 self float64 34 absent float64 35 } 36 37 // NewUndirectedMatrix creates an undirected dense graph with n nodes. 38 // All edges are initialized with the weight given by init. The self parameter 39 // specifies the cost of self connection, and absent specifies the weight 40 // returned for absent edges. 41 func NewUndirectedMatrix(n int, init, self, absent float64) *UndirectedMatrix { 42 matrix := make([]float64, n*n) 43 if init != 0 { 44 for i := range matrix { 45 matrix[i] = init 46 } 47 } 48 for i := 0; i < len(matrix); i += n + 1 { 49 matrix[i] = self 50 } 51 return &UndirectedMatrix{ 52 mat: mat.NewSymDense(n, matrix), 53 self: self, 54 absent: absent, 55 } 56 } 57 58 // NewUndirectedMatrixFrom creates an undirected dense graph with the given nodes. 59 // The IDs of the nodes must be contiguous from 0 to len(nodes)-1, but may 60 // be in any order. If IDs are not contiguous NewUndirectedMatrixFrom will panic. 61 // All edges are initialized with the weight given by init. The self parameter 62 // specifies the cost of self connection, and absent specifies the weight 63 // returned for absent edges. 64 func NewUndirectedMatrixFrom(nodes []graph.Node, init, self, absent float64) *UndirectedMatrix { 65 sort.Sort(ordered.ByID(nodes)) 66 for i, n := range nodes { 67 if int64(i) != n.ID() { 68 panic("simple: non-contiguous node IDs") 69 } 70 } 71 g := NewUndirectedMatrix(len(nodes), init, self, absent) 72 g.nodes = nodes 73 return g 74 } 75 76 // Edge returns the edge from u to v if such an edge exists and nil otherwise. 77 // The node v must be directly reachable from u as defined by the From method. 78 func (g *UndirectedMatrix) Edge(uid, vid int64) graph.Edge { 79 return g.WeightedEdgeBetween(uid, vid) 80 } 81 82 // EdgeBetween returns the edge between nodes x and y. 83 func (g *UndirectedMatrix) EdgeBetween(uid, vid int64) graph.Edge { 84 return g.WeightedEdgeBetween(uid, vid) 85 } 86 87 // Edges returns all the edges in the graph. 88 func (g *UndirectedMatrix) Edges() graph.Edges { 89 var edges []graph.Edge 90 r, _ := g.mat.Dims() 91 for i := 0; i < r; i++ { 92 for j := i + 1; j < r; j++ { 93 if w := g.mat.At(i, j); !isSame(w, g.absent) { 94 edges = append(edges, WeightedEdge{F: g.Node(int64(i)), T: g.Node(int64(j)), W: w}) 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 func (g *UndirectedMatrix) From(id int64) graph.Nodes { 106 if !g.has(id) { 107 return graph.Empty 108 } 109 var nodes []graph.Node 110 r := g.mat.Symmetric() 111 for i := 0; i < r; i++ { 112 if int64(i) == id { 113 continue 114 } 115 // id is not greater than maximum int by this point. 116 if !isSame(g.mat.At(int(id), i), g.absent) { 117 nodes = append(nodes, g.Node(int64(i))) 118 } 119 } 120 if len(nodes) == 0 { 121 return graph.Empty 122 } 123 return iterator.NewOrderedNodes(nodes) 124 } 125 126 // HasEdgeBetween returns whether an edge exists between nodes x and y. 127 func (g *UndirectedMatrix) HasEdgeBetween(uid, vid int64) bool { 128 if !g.has(uid) { 129 return false 130 } 131 if !g.has(vid) { 132 return false 133 } 134 // uid and vid are not greater than maximum int by this point. 135 return uid != vid && !isSame(g.mat.At(int(uid), int(vid)), g.absent) 136 } 137 138 // Matrix returns the mat.Matrix representation of the graph. 139 func (g *UndirectedMatrix) Matrix() mat.Matrix { 140 // Prevent alteration of dimensions of the returned matrix. 141 m := *g.mat 142 return &m 143 } 144 145 // Node returns the node with the given ID if it exists in the graph, 146 // and nil otherwise. 147 func (g *UndirectedMatrix) Node(id int64) graph.Node { 148 if !g.has(id) { 149 return nil 150 } 151 if g.nodes == nil { 152 return Node(id) 153 } 154 return g.nodes[id] 155 } 156 157 // Nodes returns all the nodes in the graph. 158 func (g *UndirectedMatrix) Nodes() graph.Nodes { 159 if g.nodes != nil { 160 nodes := make([]graph.Node, len(g.nodes)) 161 copy(nodes, g.nodes) 162 return iterator.NewOrderedNodes(nodes) 163 } 164 r := g.mat.Symmetric() 165 // Matrix graphs must have at least one node. 166 return iterator.NewImplicitNodes(0, r, newSimpleNode) 167 } 168 169 // RemoveEdge removes the edge with the given end point IDs from the graph, leaving the terminal 170 // nodes. If the edge does not exist it is a no-op. 171 func (g *UndirectedMatrix) RemoveEdge(fid, tid int64) { 172 if !g.has(fid) { 173 return 174 } 175 if !g.has(tid) { 176 return 177 } 178 // fid and tid are not greater than maximum int by this point. 179 g.mat.SetSym(int(fid), int(tid), g.absent) 180 } 181 182 // SetEdge sets e, an edge from one node to another with unit weight. If the ends of the edge are 183 // not in g or the edge is a self loop, SetEdge panics. SetEdge will store the nodes of 184 // e in the graph if it was initialized with NewUndirectedMatrixFrom. 185 func (g *UndirectedMatrix) SetEdge(e graph.Edge) { 186 g.setWeightedEdge(e, 1) 187 } 188 189 // SetWeightedEdge sets e, an edge from one node to another. If the ends of the edge are not in g 190 // or the edge is a self loop, SetWeightedEdge panics. SetWeightedEdge will store the nodes of 191 // e in the graph if it was initialized with NewUndirectedMatrixFrom. 192 func (g *UndirectedMatrix) SetWeightedEdge(e graph.WeightedEdge) { 193 g.setWeightedEdge(e, e.Weight()) 194 } 195 196 func (g *UndirectedMatrix) setWeightedEdge(e graph.Edge, weight float64) { 197 from := e.From() 198 fid := from.ID() 199 to := e.To() 200 tid := to.ID() 201 if fid == tid { 202 panic("simple: set illegal edge") 203 } 204 if int64(int(fid)) != fid { 205 panic("simple: unavailable from node ID for dense graph") 206 } 207 if int64(int(tid)) != tid { 208 panic("simple: unavailable to node ID for dense graph") 209 } 210 if g.nodes != nil { 211 g.nodes[fid] = from 212 g.nodes[tid] = to 213 } 214 // fid and tid are not greater than maximum int by this point. 215 g.mat.SetSym(int(fid), int(tid), weight) 216 } 217 218 // Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge. 219 // If x and y are the same node or there is no joining edge between the two nodes the weight 220 // value returned is either the graph's absent or self value. Weight returns true if an edge 221 // exists between x and y or if x and y have the same ID, false otherwise. 222 func (g *UndirectedMatrix) Weight(xid, yid int64) (w float64, ok bool) { 223 if xid == yid { 224 return g.self, true 225 } 226 if g.HasEdgeBetween(xid, yid) { 227 // xid and yid are not greater than maximum int by this point. 228 return g.mat.At(int(xid), int(yid)), true 229 } 230 return g.absent, false 231 } 232 233 // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. 234 // The node v must be directly reachable from u as defined by the From method. 235 func (g *UndirectedMatrix) WeightedEdge(uid, vid int64) graph.WeightedEdge { 236 return g.WeightedEdgeBetween(uid, vid) 237 } 238 239 // WeightedEdgeBetween returns the weighted edge between nodes x and y. 240 func (g *UndirectedMatrix) WeightedEdgeBetween(uid, vid int64) graph.WeightedEdge { 241 if g.HasEdgeBetween(uid, vid) { 242 // uid and vid are not greater than maximum int by this point. 243 return WeightedEdge{F: g.Node(uid), T: g.Node(vid), W: g.mat.At(int(uid), int(vid))} 244 } 245 return nil 246 } 247 248 // WeightedEdges returns all the edges in the graph. 249 func (g *UndirectedMatrix) WeightedEdges() graph.WeightedEdges { 250 var edges []graph.WeightedEdge 251 r, _ := g.mat.Dims() 252 for i := 0; i < r; i++ { 253 for j := i + 1; j < r; j++ { 254 if w := g.mat.At(i, j); !isSame(w, g.absent) { 255 edges = append(edges, WeightedEdge{F: g.Node(int64(i)), T: g.Node(int64(j)), W: w}) 256 } 257 } 258 } 259 if len(edges) == 0 { 260 return graph.Empty 261 } 262 return iterator.NewOrderedWeightedEdges(edges) 263 } 264 265 func (g *UndirectedMatrix) has(id int64) bool { 266 r := g.mat.Symmetric() 267 return 0 <= id && id < int64(r) 268 }