github.com/gopherd/gonum@v0.0.4/graph/multi/weighted_undirected.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 multi
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/gopherd/gonum/graph"
    11  	"github.com/gopherd/gonum/graph/iterator"
    12  	"github.com/gopherd/gonum/graph/set/uid"
    13  )
    14  
    15  var (
    16  	wug *WeightedUndirectedGraph
    17  
    18  	_ graph.Graph                        = wug
    19  	_ graph.Weighted                     = wug
    20  	_ graph.Undirected                   = wug
    21  	_ graph.WeightedUndirected           = wug
    22  	_ graph.Multigraph                   = wug
    23  	_ graph.UndirectedMultigraph         = wug
    24  	_ graph.WeightedUndirectedMultigraph = wug
    25  	_ graph.NodeAdder                    = wug
    26  	_ graph.NodeRemover                  = wug
    27  	_ graph.WeightedLineAdder            = wug
    28  	_ graph.LineRemover                  = wug
    29  )
    30  
    31  // WeightedUndirectedGraph implements a generalized undirected graph.
    32  type WeightedUndirectedGraph struct {
    33  	// EdgeWEightFunc is used to provide
    34  	// the WeightFunc function for WeightedEdge
    35  	// values returned by the graph.
    36  	// WeightFunc must accept a nil input.
    37  	EdgeWeightFunc func(graph.WeightedLines) float64
    38  
    39  	nodes map[int64]graph.Node
    40  	lines map[int64]map[int64]map[int64]graph.WeightedLine
    41  
    42  	nodeIDs *uid.Set
    43  	lineIDs map[int64]map[int64]*uid.Set
    44  }
    45  
    46  // NewWeightedUndirectedGraph returns an WeightedUndirectedGraph.
    47  func NewWeightedUndirectedGraph() *WeightedUndirectedGraph {
    48  	return &WeightedUndirectedGraph{
    49  		nodes: make(map[int64]graph.Node),
    50  		lines: make(map[int64]map[int64]map[int64]graph.WeightedLine),
    51  
    52  		nodeIDs: uid.NewSet(),
    53  		lineIDs: make(map[int64]map[int64]*uid.Set),
    54  	}
    55  }
    56  
    57  // AddNode adds n to the graph. It panics if the added node ID matches an existing node ID.
    58  func (g *WeightedUndirectedGraph) AddNode(n graph.Node) {
    59  	if _, exists := g.nodes[n.ID()]; exists {
    60  		panic(fmt.Sprintf("simple: node ID collision: %d", n.ID()))
    61  	}
    62  	g.nodes[n.ID()] = n
    63  	g.nodeIDs.Use(n.ID())
    64  }
    65  
    66  // Edge returns the edge from u to v if such an edge exists and nil otherwise.
    67  // The node v must be directly reachable from u as defined by the From method.
    68  // The returned graph.Edge is a multi.WeightedEdge if an edge exists.
    69  func (g *WeightedUndirectedGraph) Edge(uid, vid int64) graph.Edge {
    70  	return g.WeightedEdge(uid, vid)
    71  }
    72  
    73  // EdgeBetween returns the edge between nodes x and y.
    74  func (g *WeightedUndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
    75  	return g.WeightedEdge(xid, yid)
    76  }
    77  
    78  // Edges returns all the edges in the graph. Each edge in the returned slice
    79  // is a multi.WeightedEdge.
    80  //
    81  // The returned graph.Edges is only valid until the next mutation of
    82  // the receiver.
    83  func (g *WeightedUndirectedGraph) Edges() graph.Edges {
    84  	if len(g.lines) == 0 {
    85  		return graph.Empty
    86  	}
    87  	var edges []graph.Edge
    88  	for xid, u := range g.lines {
    89  		for yid, lines := range u {
    90  			if yid < xid {
    91  				// Do not consider lines when the To node ID is
    92  				// before the From node ID. Both orientations
    93  				// are stored.
    94  				continue
    95  			}
    96  			if len(lines) == 0 {
    97  				continue
    98  			}
    99  			edges = append(edges, WeightedEdge{
   100  				F:             g.Node(xid),
   101  				T:             g.Node(yid),
   102  				WeightedLines: iterator.NewWeightedLines(lines),
   103  				WeightFunc:    g.EdgeWeightFunc,
   104  			})
   105  		}
   106  	}
   107  	if len(edges) == 0 {
   108  		return graph.Empty
   109  	}
   110  	return iterator.NewOrderedEdges(edges)
   111  }
   112  
   113  // From returns all nodes in g that can be reached directly from n.
   114  //
   115  // The returned graph.Nodes is only valid until the next mutation of
   116  // the receiver.
   117  func (g *WeightedUndirectedGraph) From(id int64) graph.Nodes {
   118  	if len(g.lines[id]) == 0 {
   119  		return graph.Empty
   120  	}
   121  	return iterator.NewNodesByWeightedLines(g.nodes, g.lines[id])
   122  }
   123  
   124  // HasEdgeBetween returns whether an edge exists between nodes x and y.
   125  func (g *WeightedUndirectedGraph) HasEdgeBetween(xid, yid int64) bool {
   126  	_, ok := g.lines[xid][yid]
   127  	return ok
   128  }
   129  
   130  // Lines returns the lines from u to v if such an edge exists and nil otherwise.
   131  // The node v must be directly reachable from u as defined by the From method.
   132  func (g *WeightedUndirectedGraph) Lines(uid, vid int64) graph.Lines {
   133  	return g.LinesBetween(uid, vid)
   134  }
   135  
   136  // LinesBetween returns the lines between nodes x and y.
   137  func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) graph.Lines {
   138  	if !g.HasEdgeBetween(xid, yid) {
   139  		return graph.Empty
   140  	}
   141  	var lines []graph.Line
   142  	for _, l := range g.lines[xid][yid] {
   143  		if l.From().ID() != xid {
   144  			l = l.ReversedLine().(graph.WeightedLine)
   145  		}
   146  		lines = append(lines, l)
   147  	}
   148  	return iterator.NewOrderedLines(lines)
   149  }
   150  
   151  // NewNode returns a new unique Node to be added to g. The Node's ID does
   152  // not become valid in g until the Node is added to g.
   153  func (g *WeightedUndirectedGraph) NewNode() graph.Node {
   154  	if len(g.nodes) == 0 {
   155  		return Node(0)
   156  	}
   157  	if int64(len(g.nodes)) == uid.Max {
   158  		panic("simple: cannot allocate node: no slot")
   159  	}
   160  	return Node(g.nodeIDs.NewID())
   161  }
   162  
   163  // NewWeightedLine returns a new WeightedLine from the source to the destination node.
   164  // The returned WeightedLine will have a graph-unique ID.
   165  // The Line's ID does not become valid in g until the Line is added to g.
   166  func (g *WeightedUndirectedGraph) NewWeightedLine(from, to graph.Node, weight float64) graph.WeightedLine {
   167  	xid := from.ID()
   168  	yid := to.ID()
   169  	if yid < xid {
   170  		xid, yid = yid, xid
   171  	}
   172  	var lineID int64
   173  	switch {
   174  	case g.lineIDs[xid] == nil:
   175  		uids := uid.NewSet()
   176  		lineID = uids.NewID()
   177  		g.lineIDs[xid] = map[int64]*uid.Set{yid: uids}
   178  	case g.lineIDs[xid][yid] == nil:
   179  		uids := uid.NewSet()
   180  		lineID = uids.NewID()
   181  		g.lineIDs[xid][yid] = uids
   182  	default:
   183  		lineID = g.lineIDs[xid][yid].NewID()
   184  	}
   185  	return WeightedLine{F: from, T: to, W: weight, UID: lineID}
   186  }
   187  
   188  // Node returns the node with the given ID if it exists in the graph,
   189  // and nil otherwise.
   190  func (g *WeightedUndirectedGraph) Node(id int64) graph.Node {
   191  	return g.nodes[id]
   192  }
   193  
   194  // Nodes returns all the nodes in the graph.
   195  //
   196  // The returned graph.Nodes is only valid until the next mutation of
   197  // the receiver.
   198  func (g *WeightedUndirectedGraph) Nodes() graph.Nodes {
   199  	if len(g.nodes) == 0 {
   200  		return graph.Empty
   201  	}
   202  	return iterator.NewNodes(g.nodes)
   203  }
   204  
   205  // NodeWithID returns a Node with the given ID if possible. If a graph.Node
   206  // is returned that is not already in the graph NodeWithID will return true
   207  // for new and the graph.Node must be added to the graph before use.
   208  func (g *WeightedUndirectedGraph) NodeWithID(id int64) (n graph.Node, new bool) {
   209  	n, ok := g.nodes[id]
   210  	if ok {
   211  		return n, false
   212  	}
   213  	return Node(id), true
   214  }
   215  
   216  // RemoveLine removes the line with the given end point and line IDs from the graph,
   217  // leaving the terminal nodes. If the line does not exist it is a no-op.
   218  func (g *WeightedUndirectedGraph) RemoveLine(fid, tid, id int64) {
   219  	if _, ok := g.nodes[fid]; !ok {
   220  		return
   221  	}
   222  	if _, ok := g.nodes[tid]; !ok {
   223  		return
   224  	}
   225  
   226  	delete(g.lines[fid][tid], id)
   227  	if len(g.lines[fid][tid]) == 0 {
   228  		delete(g.lines[fid], tid)
   229  	}
   230  	delete(g.lines[tid][fid], id)
   231  	if len(g.lines[tid][fid]) == 0 {
   232  		delete(g.lines[tid], fid)
   233  	}
   234  
   235  	xid := fid
   236  	yid := tid
   237  	if yid < xid {
   238  		xid, yid = yid, xid
   239  	}
   240  	g.lineIDs[xid][yid].Release(id)
   241  }
   242  
   243  // RemoveNode removes the node with the given ID from the graph, as well as any edges attached
   244  // to it. If the node is not in the graph it is a no-op.
   245  func (g *WeightedUndirectedGraph) RemoveNode(id int64) {
   246  	if _, ok := g.nodes[id]; !ok {
   247  		return
   248  	}
   249  	delete(g.nodes, id)
   250  
   251  	for from := range g.lines[id] {
   252  		delete(g.lines[from], id)
   253  	}
   254  	delete(g.lines, id)
   255  
   256  	g.nodeIDs.Release(id)
   257  }
   258  
   259  // SetWeightedLine adds l, a line from one node to another. If the nodes do not exist, they are added
   260  // and are set to the nodes of the line otherwise.
   261  func (g *WeightedUndirectedGraph) SetWeightedLine(l graph.WeightedLine) {
   262  	var (
   263  		from = l.From()
   264  		fid  = from.ID()
   265  		to   = l.To()
   266  		tid  = to.ID()
   267  		lid  = l.ID()
   268  	)
   269  
   270  	if _, ok := g.nodes[fid]; !ok {
   271  		g.AddNode(from)
   272  	} else {
   273  		g.nodes[fid] = from
   274  	}
   275  	if _, ok := g.nodes[tid]; !ok {
   276  		g.AddNode(to)
   277  	} else {
   278  		g.nodes[tid] = to
   279  	}
   280  
   281  	switch {
   282  	case g.lines[fid] == nil:
   283  		g.lines[fid] = map[int64]map[int64]graph.WeightedLine{tid: {lid: l}}
   284  	case g.lines[fid][tid] == nil:
   285  		g.lines[fid][tid] = map[int64]graph.WeightedLine{lid: l}
   286  	default:
   287  		g.lines[fid][tid][lid] = l
   288  	}
   289  	switch {
   290  	case g.lines[tid] == nil:
   291  		g.lines[tid] = map[int64]map[int64]graph.WeightedLine{fid: {lid: l}}
   292  	case g.lines[tid][fid] == nil:
   293  		g.lines[tid][fid] = map[int64]graph.WeightedLine{lid: l}
   294  	default:
   295  		g.lines[tid][fid][lid] = l
   296  	}
   297  
   298  	xid := fid
   299  	yid := tid
   300  	if yid < xid {
   301  		xid, yid = yid, xid
   302  	}
   303  	switch {
   304  	case g.lineIDs[xid] == nil:
   305  		uids := uid.NewSet()
   306  		g.lineIDs[xid] = map[int64]*uid.Set{yid: uids}
   307  	case g.lineIDs[xid][yid] == nil:
   308  		uids := uid.NewSet()
   309  		g.lineIDs[xid][yid] = uids
   310  	}
   311  	g.lineIDs[xid][yid].Use(lid)
   312  }
   313  
   314  // Weight returns the weight for the lines between x and y summarised by the receiver's
   315  // EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
   316  func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
   317  	lines := g.WeightedLines(xid, yid)
   318  	return WeightedEdge{WeightedLines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), lines != graph.Empty
   319  }
   320  
   321  // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise.
   322  // The node v must be directly reachable from u as defined by the From method.
   323  // The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
   324  func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
   325  	lines := g.WeightedLines(uid, vid)
   326  	if lines == graph.Empty {
   327  		return nil
   328  	}
   329  	return WeightedEdge{
   330  		F: g.Node(uid), T: g.Node(vid),
   331  		WeightedLines: lines,
   332  		WeightFunc:    g.EdgeWeightFunc,
   333  	}
   334  }
   335  
   336  // WeightedEdgeBetween returns the weighted edge between nodes x and y.
   337  func (g *WeightedUndirectedGraph) WeightedEdgeBetween(xid, yid int64) graph.WeightedEdge {
   338  	return g.WeightedEdge(xid, yid)
   339  }
   340  
   341  // WeightedEdges returns all the edges in the graph. Each edge in the returned slice
   342  // is a multi.WeightedEdge.
   343  //
   344  // The returned graph.WeightedEdges is only valid until the next mutation of
   345  // the receiver.
   346  func (g *WeightedUndirectedGraph) WeightedEdges() graph.WeightedEdges {
   347  	if len(g.lines) == 0 {
   348  		return graph.Empty
   349  	}
   350  	var edges []graph.WeightedEdge
   351  	for xid, u := range g.lines {
   352  		for yid, lines := range u {
   353  			if yid < xid {
   354  				// Do not consider lines when the To node ID is
   355  				// before the From node ID. Both orientations
   356  				// are stored.
   357  				continue
   358  			}
   359  			if len(lines) == 0 {
   360  				continue
   361  			}
   362  			edges = append(edges, WeightedEdge{
   363  				F:             g.Node(xid),
   364  				T:             g.Node(yid),
   365  				WeightedLines: iterator.NewWeightedLines(lines),
   366  				WeightFunc:    g.EdgeWeightFunc,
   367  			})
   368  		}
   369  	}
   370  	if len(edges) == 0 {
   371  		return graph.Empty
   372  	}
   373  	return iterator.NewOrderedWeightedEdges(edges)
   374  }
   375  
   376  // WeightedLines returns the lines from u to v if such an edge exists and nil otherwise.
   377  // The node v must be directly reachable from u as defined by the From method.
   378  func (g *WeightedUndirectedGraph) WeightedLines(uid, vid int64) graph.WeightedLines {
   379  	return g.WeightedLinesBetween(uid, vid)
   380  }
   381  
   382  // WeightedLinesBetween returns the lines between nodes x and y.
   383  func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) graph.WeightedLines {
   384  	if !g.HasEdgeBetween(xid, yid) {
   385  		return graph.Empty
   386  	}
   387  	var lines []graph.WeightedLine
   388  	for _, l := range g.lines[xid][yid] {
   389  		if l.From().ID() != xid {
   390  			l = l.ReversedLine().(graph.WeightedLine)
   391  		}
   392  		lines = append(lines, l)
   393  	}
   394  	return iterator.NewOrderedWeightedLines(lines)
   395  }