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  }