mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 23:26:52 +08:00
386 lines
7.3 KiB
Go
386 lines
7.3 KiB
Go
// 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.
|
|
}
|