github.com/gopherd/gonum@v0.0.4/graph/simple/dense_directed_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 dm *DirectedMatrix 16 17 _ graph.Graph = dm 18 _ graph.Directed = dm 19 _ edgeSetter = dm 20 _ weightedEdgeSetter = dm 21 ) 22 23 // DirectedMatrix represents a directed 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 DirectedMatrix struct { 28 mat *mat.Dense 29 nodes []graph.Node 30 31 self float64 32 absent float64 33 } 34 35 // NewDirectedMatrix creates a directed 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 NewDirectedMatrix(n int, init, self, absent float64) *DirectedMatrix { 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 &DirectedMatrix{ 50 mat: mat.NewDense(n, n, matrix), 51 self: self, 52 absent: absent, 53 } 54 } 55 56 // NewDirectedMatrixFrom creates a directed 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 NewDirectedMatrixFrom 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 NewDirectedMatrixFrom(nodes []graph.Node, init, self, absent float64) *DirectedMatrix { 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 := NewDirectedMatrix(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 *DirectedMatrix) Edge(uid, vid int64) graph.Edge { 77 return g.WeightedEdge(uid, vid) 78 } 79 80 // Edges returns all the edges in the graph. 81 func (g *DirectedMatrix) Edges() graph.Edges { 82 var edges []graph.Edge 83 r, _ := g.mat.Dims() 84 for i := 0; i < r; i++ { 85 for j := 0; j < r; j++ { 86 if i == j { 87 continue 88 } 89 if w := g.mat.At(i, j); !isSame(w, g.absent) { 90 edges = append(edges, WeightedEdge{F: g.Node(int64(i)), T: g.Node(int64(j)), W: w}) 91 } 92 } 93 } 94 if len(edges) == 0 { 95 return graph.Empty 96 } 97 return iterator.NewOrderedEdges(edges) 98 } 99 100 // From returns all nodes in g that can be reached directly from n. 101 func (g *DirectedMatrix) From(id int64) graph.Nodes { 102 if !g.has(id) { 103 return graph.Empty 104 } 105 var nodes []graph.Node 106 _, c := g.mat.Dims() 107 for j := 0; j < c; j++ { 108 if int64(j) == id { 109 continue 110 } 111 // id is not greater than maximum int by this point. 112 if !isSame(g.mat.At(int(id), j), g.absent) { 113 nodes = append(nodes, g.Node(int64(j))) 114 } 115 } 116 if len(nodes) == 0 { 117 return graph.Empty 118 } 119 return iterator.NewOrderedNodes(nodes) 120 } 121 122 // HasEdgeBetween returns whether an edge exists between nodes x and y without 123 // considering direction. 124 func (g *DirectedMatrix) HasEdgeBetween(xid, yid int64) bool { 125 if !g.has(xid) { 126 return false 127 } 128 if !g.has(yid) { 129 return false 130 } 131 // xid and yid are not greater than maximum int by this point. 132 return xid != yid && (!isSame(g.mat.At(int(xid), int(yid)), g.absent) || !isSame(g.mat.At(int(yid), int(xid)), g.absent)) 133 } 134 135 // HasEdgeFromTo returns whether an edge exists in the graph from u to v. 136 func (g *DirectedMatrix) HasEdgeFromTo(uid, vid int64) bool { 137 if !g.has(uid) { 138 return false 139 } 140 if !g.has(vid) { 141 return false 142 } 143 // uid and vid are not greater than maximum int by this point. 144 return uid != vid && !isSame(g.mat.At(int(uid), int(vid)), g.absent) 145 } 146 147 // Matrix returns the mat.Matrix representation of the graph. The orientation 148 // of the matrix is such that the matrix entry at G_{ij} is the weight of the edge 149 // from node i to node j. 150 func (g *DirectedMatrix) Matrix() mat.Matrix { 151 // Prevent alteration of dimensions of the returned matrix. 152 m := *g.mat 153 return &m 154 } 155 156 // Node returns the node with the given ID if it exists in the graph, 157 // and nil otherwise. 158 func (g *DirectedMatrix) Node(id int64) graph.Node { 159 if !g.has(id) { 160 return nil 161 } 162 if g.nodes == nil { 163 return Node(id) 164 } 165 return g.nodes[id] 166 } 167 168 // Nodes returns all the nodes in the graph. 169 func (g *DirectedMatrix) Nodes() graph.Nodes { 170 if g.nodes != nil { 171 nodes := make([]graph.Node, len(g.nodes)) 172 copy(nodes, g.nodes) 173 return iterator.NewOrderedNodes(nodes) 174 } 175 r, _ := g.mat.Dims() 176 // Matrix graphs must have at least one node. 177 return iterator.NewImplicitNodes(0, r, newSimpleNode) 178 } 179 180 // RemoveEdge removes the edge with the given end point nodes from the graph, leaving the terminal 181 // nodes. If the edge does not exist it is a no-op. 182 func (g *DirectedMatrix) RemoveEdge(fid, tid int64) { 183 if !g.has(fid) { 184 return 185 } 186 if !g.has(tid) { 187 return 188 } 189 // fid and tid are not greater than maximum int by this point. 190 g.mat.Set(int(fid), int(tid), g.absent) 191 } 192 193 // SetEdge sets e, an edge from one node to another with unit weight. If the ends of the edge 194 // are not in g or the edge is a self loop, SetEdge panics. SetEdge will store the nodes of 195 // e in the graph if it was initialized with NewDirectedMatrixFrom. 196 func (g *DirectedMatrix) SetEdge(e graph.Edge) { 197 g.setWeightedEdge(e, 1) 198 } 199 200 // SetWeightedEdge sets e, an edge from one node to another. If the ends of the edge are not in g 201 // or the edge is a self loop, SetWeightedEdge panics. SetWeightedEdge will store the nodes of 202 // e in the graph if it was initialized with NewDirectedMatrixFrom. 203 func (g *DirectedMatrix) SetWeightedEdge(e graph.WeightedEdge) { 204 g.setWeightedEdge(e, e.Weight()) 205 } 206 207 func (g *DirectedMatrix) setWeightedEdge(e graph.Edge, weight float64) { 208 from := e.From() 209 fid := from.ID() 210 to := e.To() 211 tid := to.ID() 212 if fid == tid { 213 panic("simple: set illegal edge") 214 } 215 if int64(int(fid)) != fid { 216 panic("simple: unavailable from node ID for dense graph") 217 } 218 if int64(int(tid)) != tid { 219 panic("simple: unavailable to node ID for dense graph") 220 } 221 if g.nodes != nil { 222 g.nodes[fid] = from 223 g.nodes[tid] = to 224 } 225 // fid and tid are not greater than maximum int by this point. 226 g.mat.Set(int(fid), int(tid), weight) 227 } 228 229 // To returns all nodes in g that can reach directly to n. 230 func (g *DirectedMatrix) To(id int64) graph.Nodes { 231 if !g.has(id) { 232 return graph.Empty 233 } 234 var nodes []graph.Node 235 r, _ := g.mat.Dims() 236 for i := 0; i < r; i++ { 237 if int64(i) == id { 238 continue 239 } 240 // id is not greater than maximum int by this point. 241 if !isSame(g.mat.At(i, int(id)), g.absent) { 242 nodes = append(nodes, g.Node(int64(i))) 243 } 244 } 245 if len(nodes) == 0 { 246 return graph.Empty 247 } 248 return iterator.NewOrderedNodes(nodes) 249 } 250 251 // Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge. 252 // If x and y are the same node or there is no joining edge between the two nodes the weight 253 // value returned is either the graph's absent or self value. Weight returns true if an edge 254 // exists between x and y or if x and y have the same ID, false otherwise. 255 func (g *DirectedMatrix) Weight(xid, yid int64) (w float64, ok bool) { 256 if xid == yid { 257 return g.self, true 258 } 259 if g.HasEdgeFromTo(xid, yid) { 260 // xid and yid are not greater than maximum int by this point. 261 return g.mat.At(int(xid), int(yid)), true 262 } 263 return g.absent, false 264 } 265 266 // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. 267 // The node v must be directly reachable from u as defined by the From method. 268 func (g *DirectedMatrix) WeightedEdge(uid, vid int64) graph.WeightedEdge { 269 if g.HasEdgeFromTo(uid, vid) { 270 // xid and yid are not greater than maximum int by this point. 271 return WeightedEdge{F: g.Node(uid), T: g.Node(vid), W: g.mat.At(int(uid), int(vid))} 272 } 273 return nil 274 } 275 276 // WeightedEdges returns all the edges in the graph. 277 func (g *DirectedMatrix) WeightedEdges() graph.WeightedEdges { 278 var edges []graph.WeightedEdge 279 r, _ := g.mat.Dims() 280 for i := 0; i < r; i++ { 281 for j := 0; j < r; j++ { 282 if i == j { 283 continue 284 } 285 if w := g.mat.At(i, j); !isSame(w, g.absent) { 286 edges = append(edges, WeightedEdge{F: g.Node(int64(i)), T: g.Node(int64(j)), W: w}) 287 } 288 } 289 } 290 if len(edges) == 0 { 291 return graph.Empty 292 } 293 return iterator.NewOrderedWeightedEdges(edges) 294 } 295 296 func (g *DirectedMatrix) has(id int64) bool { 297 r, _ := g.mat.Dims() 298 return 0 <= id && id < int64(r) 299 }