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