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