gonum: directed acyclic graph

Implements graph.Directed capable of storing at most one edge between any two nodes.
Uses the Undirected implementation for space efficiency (~30% space savings).
This commit is contained in:
Jordan Liggitt 2017-05-22 21:15:14 -04:00
parent b1ac314046
commit ec1d79da19
No known key found for this signature in database
GPG Key ID: 24E7ADF9A3B42012
3 changed files with 122 additions and 1 deletions

View File

@ -10,7 +10,10 @@ load(
go_test(
name = "go_default_test",
srcs = ["undirected_test.go"],
srcs = [
"directed_acyclic_test.go",
"undirected_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//third_party/forked/gonum/graph:go_default_library"],
@ -19,6 +22,7 @@ go_test(
go_library(
name = "go_default_library",
srcs = [
"directed_acyclic.go",
"simple.go",
"undirected.go",
],

View File

@ -0,0 +1,55 @@
package simple
import (
"k8s.io/kubernetes/third_party/forked/gonum/graph"
)
// DirectedAcyclicGraph implements graph.Directed using UndirectedGraph,
// which only stores one edge for any node pair.
type DirectedAcyclicGraph struct {
*UndirectedGraph
}
func NewDirectedAcyclicGraph(self, absent float64) *DirectedAcyclicGraph {
return &DirectedAcyclicGraph{
UndirectedGraph: NewUndirectedGraph(self, absent),
}
}
func (g *DirectedAcyclicGraph) HasEdgeFromTo(u, v graph.Node) bool {
edge := g.UndirectedGraph.EdgeBetween(u, v)
if edge == nil {
return false
}
return (edge.From().ID() == u.ID())
}
func (g *DirectedAcyclicGraph) From(n graph.Node) []graph.Node {
if !g.Has(n) {
return nil
}
fid := n.ID()
nodes := make([]graph.Node, 0, len(g.UndirectedGraph.edges[n.ID()]))
for _, edge := range g.UndirectedGraph.edges[n.ID()] {
if edge.From().ID() == fid {
nodes = append(nodes, g.UndirectedGraph.nodes[edge.To().ID()])
}
}
return nodes
}
func (g *DirectedAcyclicGraph) To(n graph.Node) []graph.Node {
if !g.Has(n) {
return nil
}
tid := n.ID()
nodes := make([]graph.Node, 0, len(g.UndirectedGraph.edges[n.ID()]))
for _, edge := range g.UndirectedGraph.edges[n.ID()] {
if edge.To().ID() == tid {
nodes = append(nodes, g.UndirectedGraph.nodes[edge.From().ID()])
}
}
return nodes
}

View File

@ -0,0 +1,62 @@
// 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 simple
import (
"math"
"testing"
"k8s.io/kubernetes/third_party/forked/gonum/graph"
)
var _ graph.Graph = &DirectedAcyclicGraph{}
var _ graph.Directed = &DirectedAcyclicGraph{}
// Tests Issue #27
func TestAcyclicEdgeOvercounting(t *testing.T) {
g := generateDummyAcyclicGraph()
if neigh := g.From(Node(Node(2))); len(neigh) != 2 {
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
}
}
func generateDummyAcyclicGraph() *DirectedAcyclicGraph {
nodes := [4]struct{ srcID, targetID int }{
{2, 1},
{1, 0},
{0, 2},
{2, 0},
}
g := NewDirectedAcyclicGraph(0, math.Inf(1))
for _, n := range nodes {
g.SetEdge(Edge{F: Node(n.srcID), T: Node(n.targetID), W: 1})
}
return g
}
// Test for issue #123 https://github.com/gonum/graph/issues/123
func TestAcyclicIssue123DirectedGraph(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Errorf("unexpected panic: %v", r)
}
}()
g := NewDirectedAcyclicGraph(0, math.Inf(1))
n0 := Node(g.NewNodeID())
g.AddNode(n0)
n1 := Node(g.NewNodeID())
g.AddNode(n1)
g.RemoveNode(n0)
n2 := Node(g.NewNodeID())
g.AddNode(n2)
}