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  }