// Copyright ©2014 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package graph_test import ( "fmt" "gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph/iterator" "gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/topo" ) // GraphNode is a node in an implicit graph. type GraphNode struct { id int64 neighbors []graph.Node roots []*GraphNode } // NewGraphNode returns a new GraphNode. func NewGraphNode(id int64) *GraphNode { return &GraphNode{id: id} } // Node allows GraphNode to satisfy the graph.Graph interface. func (g *GraphNode) Node(id int64) graph.Node { if id == g.id { return g } seen := map[int64]struct{}{g.id: {}} for _, root := range g.roots { if root.ID() == id { return root } if root.has(seen, id) { return root } } for _, n := range g.neighbors { if n.ID() == id { return n } if gn, ok := n.(*GraphNode); ok { if gn.has(seen, id) { return gn } } } return nil } func (g *GraphNode) has(seen map[int64]struct{}, id int64) bool { for _, root := range g.roots { if _, ok := seen[root.ID()]; ok { continue } seen[root.ID()] = struct{}{} if root.ID() == id { return true } if root.has(seen, id) { return true } } for _, n := range g.neighbors { if _, ok := seen[n.ID()]; ok { continue } seen[n.ID()] = struct{}{} if n.ID() == id { return true } if gn, ok := n.(*GraphNode); ok { if gn.has(seen, id) { return true } } } return false } // Nodes allows GraphNode to satisfy the graph.Graph interface. func (g *GraphNode) Nodes() graph.Nodes { nodes := []graph.Node{g} seen := map[int64]struct{}{g.id: {}} for _, root := range g.roots { nodes = append(nodes, root) seen[root.ID()] = struct{}{} nodes = root.nodes(nodes, seen) } for _, n := range g.neighbors { nodes = append(nodes, n) seen[n.ID()] = struct{}{} if gn, ok := n.(*GraphNode); ok { nodes = gn.nodes(nodes, seen) } } return iterator.NewOrderedNodes(nodes) } func (g *GraphNode) nodes(dst []graph.Node, seen map[int64]struct{}) []graph.Node { for _, root := range g.roots { if _, ok := seen[root.ID()]; ok { continue } seen[root.ID()] = struct{}{} dst = append(dst, graph.Node(root)) dst = root.nodes(dst, seen) } for _, n := range g.neighbors { if _, ok := seen[n.ID()]; ok { continue } dst = append(dst, n) if gn, ok := n.(*GraphNode); ok { dst = gn.nodes(dst, seen) } } return dst } // From allows GraphNode to satisfy the graph.Graph interface. func (g *GraphNode) From(id int64) graph.Nodes { if id == g.ID() { return iterator.NewOrderedNodes(g.neighbors) } seen := map[int64]struct{}{g.id: {}} for _, root := range g.roots { seen[root.ID()] = struct{}{} if result := root.findNeighbors(id, seen); result != nil { return iterator.NewOrderedNodes(result) } } for _, n := range g.neighbors { seen[n.ID()] = struct{}{} if gn, ok := n.(*GraphNode); ok { if result := gn.findNeighbors(id, seen); result != nil { return iterator.NewOrderedNodes(result) } } } return nil } func (g *GraphNode) findNeighbors(id int64, seen map[int64]struct{}) []graph.Node { if id == g.ID() { return g.neighbors } for _, root := range g.roots { if _, ok := seen[root.ID()]; ok { continue } seen[root.ID()] = struct{}{} if result := root.findNeighbors(id, seen); result != nil { return result } } for _, n := range g.neighbors { if _, ok := seen[n.ID()]; ok { continue } seen[n.ID()] = struct{}{} if gn, ok := n.(*GraphNode); ok { if result := gn.findNeighbors(id, seen); result != nil { return result } } } return nil } // HasEdgeBetween allows GraphNode to satisfy the graph.Graph interface. func (g *GraphNode) HasEdgeBetween(uid, vid int64) bool { return g.EdgeBetween(uid, vid) != nil } // Edge allows GraphNode to satisfy the graph.Graph interface. func (g *GraphNode) Edge(uid, vid int64) graph.Edge { return g.EdgeBetween(uid, vid) } // EdgeBetween allows GraphNode to satisfy the graph.Graph interface. func (g *GraphNode) EdgeBetween(uid, vid int64) graph.Edge { if uid == g.id || vid == g.id { for _, n := range g.neighbors { if n.ID() == uid || n.ID() == vid { return simple.Edge{F: g, T: n} } } return nil } seen := map[int64]struct{}{g.id: {}} for _, root := range g.roots { seen[root.ID()] = struct{}{} if result := root.edgeBetween(uid, vid, seen); result != nil { return result } } for _, n := range g.neighbors { seen[n.ID()] = struct{}{} if gn, ok := n.(*GraphNode); ok { if result := gn.edgeBetween(uid, vid, seen); result != nil { return result } } } return nil } func (g *GraphNode) edgeBetween(uid, vid int64, seen map[int64]struct{}) graph.Edge { if uid == g.id || vid == g.id { for _, n := range g.neighbors { if n.ID() == uid || n.ID() == vid { return simple.Edge{F: g, T: n} } } return nil } for _, root := range g.roots { if _, ok := seen[root.ID()]; ok { continue } seen[root.ID()] = struct{}{} if result := root.edgeBetween(uid, vid, seen); result != nil { return result } } for _, n := range g.neighbors { if _, ok := seen[n.ID()]; ok { continue } seen[n.ID()] = struct{}{} if gn, ok := n.(*GraphNode); ok { if result := gn.edgeBetween(uid, vid, seen); result != nil { return result } } } return nil } // ID allows GraphNode to satisfy the graph.Node interface. func (g *GraphNode) ID() int64 { return g.id } // AddMeighbor adds an edge between g and n. func (g *GraphNode) AddNeighbor(n *GraphNode) { g.neighbors = append(g.neighbors, graph.Node(n)) } // AddRoot adds provides an entrance into the graph g from n. func (g *GraphNode) AddRoot(n *GraphNode) { g.roots = append(g.roots, n) } func Example_implicit() { // This example shows the construction of the following graph // using the implicit graph type above. // // The visual representation of the graph can be seen at // https://graphviz.gitlab.io/_pages/Gallery/undirected/fdpclust.html // // graph G { // e // subgraph clusterA { // a -- b // subgraph clusterC { // C -- D // } // } // subgraph clusterB { // d -- f // } // d -- D // e -- clusterB // clusterC -- clusterB // } // graph G { G := NewGraphNode(0) // e e := NewGraphNode(1) // subgraph clusterA { clusterA := NewGraphNode(2) // a -- b a := NewGraphNode(3) b := NewGraphNode(4) a.AddNeighbor(b) b.AddNeighbor(a) clusterA.AddRoot(a) clusterA.AddRoot(b) // subgraph clusterC { clusterC := NewGraphNode(5) // C -- D C := NewGraphNode(6) D := NewGraphNode(7) C.AddNeighbor(D) D.AddNeighbor(C) clusterC.AddRoot(C) clusterC.AddRoot(D) // } clusterA.AddRoot(clusterC) // } // subgraph clusterB { clusterB := NewGraphNode(8) // d -- f d := NewGraphNode(9) f := NewGraphNode(10) d.AddNeighbor(f) f.AddNeighbor(d) clusterB.AddRoot(d) clusterB.AddRoot(f) // } // d -- D d.AddNeighbor(D) D.AddNeighbor(d) // e -- clusterB e.AddNeighbor(clusterB) clusterB.AddNeighbor(e) // clusterC -- clusterB clusterC.AddNeighbor(clusterB) clusterB.AddNeighbor(clusterC) G.AddRoot(e) G.AddRoot(clusterA) G.AddRoot(clusterB) // } if topo.IsPathIn(G, []graph.Node{C, D, d, f}) { fmt.Println("C--D--d--f is a path in G.") } // Output: // // C--D--d--f is a path in G. }