From ec1d79da191c33e5b16b58e808880d522d3313d2 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Mon, 22 May 2017 21:15:14 -0400 Subject: [PATCH] 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). --- third_party/forked/gonum/graph/simple/BUILD | 6 +- .../gonum/graph/simple/directed_acyclic.go | 55 ++++++++++++++++ .../graph/simple/directed_acyclic_test.go | 62 +++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 third_party/forked/gonum/graph/simple/directed_acyclic.go create mode 100644 third_party/forked/gonum/graph/simple/directed_acyclic_test.go diff --git a/third_party/forked/gonum/graph/simple/BUILD b/third_party/forked/gonum/graph/simple/BUILD index a5c9b446a4b..b4f8db1897b 100644 --- a/third_party/forked/gonum/graph/simple/BUILD +++ b/third_party/forked/gonum/graph/simple/BUILD @@ -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", ], diff --git a/third_party/forked/gonum/graph/simple/directed_acyclic.go b/third_party/forked/gonum/graph/simple/directed_acyclic.go new file mode 100644 index 00000000000..20ed2f658a9 --- /dev/null +++ b/third_party/forked/gonum/graph/simple/directed_acyclic.go @@ -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 +} diff --git a/third_party/forked/gonum/graph/simple/directed_acyclic_test.go b/third_party/forked/gonum/graph/simple/directed_acyclic_test.go new file mode 100644 index 00000000000..0f3454d8a71 --- /dev/null +++ b/third_party/forked/gonum/graph/simple/directed_acyclic_test.go @@ -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) +}