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