diff --git a/graph/encoding/dot/decode.go b/graph/encoding/dot/decode.go index f1f5e12f..ccc2868d 100644 --- a/graph/encoding/dot/decode.go +++ b/graph/encoding/dot/decode.go @@ -167,21 +167,23 @@ func (gen *generator) addStmt(dst encoding.Builder, stmt ast.Stmt) { } // addEdgeStmt adds the given edge statement to the graph. -func (gen *generator) addEdgeStmt(dst encoding.Builder, e *ast.EdgeStmt) { - fs := gen.addVertex(dst, e.From) - ts := gen.addEdge(dst, e.To) +func (gen *generator) addEdgeStmt(dst encoding.Builder, stmt *ast.EdgeStmt) { + fs := gen.addVertex(dst, stmt.From) + ts := gen.addEdge(dst, stmt.To) for _, f := range fs { for _, t := range ts { - edge, ok := dst.NewEdge(f, t).(encoding.AttributeSetter) + edge := dst.NewEdge(f, t) + dst.SetEdge(edge) + e, ok := edge.(encoding.AttributeSetter) if !ok { continue } - for _, attr := range e.Attrs { + for _, attr := range stmt.Attrs { a := encoding.Attribute{ Key: attr.Key, Value: attr.Val, } - if err := edge.SetAttribute(a); err != nil { + if err := e.SetAttribute(a); err != nil { panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value)) } } @@ -216,7 +218,8 @@ func (gen *generator) addEdge(dst encoding.Builder, to *ast.Edge) []graph.Node { ts := gen.addEdge(dst, to.To) for _, f := range fs { for _, t := range ts { - dst.NewEdge(f, t) + edge := dst.NewEdge(f, t) + dst.SetEdge(edge) } } } diff --git a/graph/encoding/dot/decode_test.go b/graph/encoding/dot/decode_test.go index 26028116..0416ecd4 100644 --- a/graph/encoding/dot/decode_test.go +++ b/graph/encoding/dot/decode_test.go @@ -118,15 +118,9 @@ func (g *dotDirectedGraph) NewNode() graph.Node { return &dotNode{Node: g.DirectedGraph.NewNode()} } -// NewEdge adds a new edge from the source to the destination node to the graph, -// or returns the existing edge if already present. +// NewEdge returns a new Edge from the source to the destination node. func (g *dotDirectedGraph) NewEdge(from, to graph.Node) graph.Edge { - if e := g.Edge(from, to); e != nil { - return e - } - e := &dotEdge{Edge: simple.Edge{F: from, T: to}} - g.SetEdge(e) - return e + return &dotEdge{Edge: g.DirectedGraph.NewEdge(from, to)} } // DOTAttributers implements the dot.Attributers interface. @@ -159,15 +153,9 @@ func (g *dotUndirectedGraph) NewNode() graph.Node { return &dotNode{Node: g.UndirectedGraph.NewNode()} } -// NewEdge adds a new edge from the source to the destination node to the graph, -// or returns the existing edge if already present. +// NewEdge returns a new Edge from the source to the destination node. func (g *dotUndirectedGraph) NewEdge(from, to graph.Node) graph.Edge { - if e := g.Edge(from, to); e != nil { - return e - } - e := &dotEdge{Edge: simple.Edge{F: from, T: to}} - g.SetEdge(e) - return e + return &dotEdge{Edge: g.UndirectedGraph.NewEdge(from, to)} } // DOTAttributers implements the dot.Attributers interface. @@ -222,7 +210,7 @@ func (n *dotNode) Attributes() []encoding.Attribute { // dotEdge extends simple.Edge with a label field to test round-trip encoding and // decoding of edge DOT label attributes. type dotEdge struct { - simple.Edge + graph.Edge // Edge label. Label string } diff --git a/graph/encoding/encoding.go b/graph/encoding/encoding.go index b352637c..0f99ac73 100644 --- a/graph/encoding/encoding.go +++ b/graph/encoding/encoding.go @@ -10,9 +10,6 @@ import "gonum.org/v1/gonum/graph" type Builder interface { graph.Graph graph.Builder - // NewEdge adds a new edge from the source to the destination node to the - // graph, or returns the existing edge if already present. - NewEdge(from, to graph.Node) graph.Edge } // AttributeSetter is implemented by types that can set an encoded graph diff --git a/graph/encoding/graphql/decode_test.go b/graph/encoding/graphql/decode_test.go index e713a665..84ba00d8 100644 --- a/graph/encoding/graphql/decode_test.go +++ b/graph/encoding/graphql/decode_test.go @@ -163,12 +163,7 @@ func (g *directedGraph) NewNode() graph.Node { } func (g *directedGraph) NewEdge(from, to graph.Node) graph.Edge { - if e := g.Edge(from, to); e != nil { - return e - } - e := &edge{Edge: simple.Edge{F: from, T: to}} - g.SetEdge(e) - return e + return &edge{Edge: g.DirectedGraph.NewEdge(from, to)} } type node struct { @@ -189,7 +184,7 @@ func (n *node) SetIDFromString(uid string) error { } type edge struct { - simple.Edge + graph.Edge label string } diff --git a/graph/graph.go b/graph/graph.go index 8c284cad..1898d01b 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -93,13 +93,16 @@ type NodeRemover interface { RemoveNode(Node) } -// EdgeSetter is an interface for adding edges to a graph. -type EdgeSetter interface { +// EdgeAdder is an interface for adding edges to a graph. +type EdgeAdder interface { + // NewEdge returns a new Edge from the source to the destination node. + NewEdge(from, to Node) Edge + // SetEdge adds an edge from one node to another. // If the graph supports node addition the nodes // will be added if they do not exist, otherwise // SetEdge will panic. - // The behavior of an EdgeSetter when the IDs + // The behavior of an EdgeAdder when the IDs // returned by e.From and e.To are equal is // implementation-dependent. SetEdge(e Edge) @@ -116,7 +119,7 @@ type EdgeRemover interface { // Builder is a graph that can have nodes and edges added. type Builder interface { NodeAdder - EdgeSetter + EdgeAdder } // UndirectedBuilder is an undirected graph builder. diff --git a/graph/path/internal/testgraphs/shortest.go b/graph/path/internal/testgraphs/shortest.go index cddb67f1..8d79f939 100644 --- a/graph/path/internal/testgraphs/shortest.go +++ b/graph/path/internal/testgraphs/shortest.go @@ -25,7 +25,7 @@ func init() { // dynamic shortest path routine in path/dynamic: DStarLite. var ShortestPathTests = []struct { Name string - Graph func() graph.EdgeSetter + Graph func() graph.EdgeAdder Edges []simple.Edge HasNegativeWeight bool HasNegativeCycle bool @@ -40,7 +40,7 @@ var ShortestPathTests = []struct { // Positive weighted graphs. { Name: "empty directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)}, Weight: math.Inf(1), @@ -49,7 +49,7 @@ var ShortestPathTests = []struct { }, { Name: "empty undirected", - Graph: func() graph.EdgeSetter { return simple.NewUndirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewUndirectedGraph(0, math.Inf(1)) }, Query: simple.Edge{F: simple.Node(0), T: simple.Node(1)}, Weight: math.Inf(1), @@ -58,7 +58,7 @@ var ShortestPathTests = []struct { }, { Name: "one edge directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(1), W: 1}, }, @@ -74,7 +74,7 @@ var ShortestPathTests = []struct { }, { Name: "one edge self directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(1), W: 1}, }, @@ -90,7 +90,7 @@ var ShortestPathTests = []struct { }, { Name: "one edge undirected", - Graph: func() graph.EdgeSetter { return simple.NewUndirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewUndirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(1), W: 1}, }, @@ -106,7 +106,7 @@ var ShortestPathTests = []struct { }, { Name: "two paths directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(2), W: 2}, {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -125,7 +125,7 @@ var ShortestPathTests = []struct { }, { Name: "two paths undirected", - Graph: func() graph.EdgeSetter { return simple.NewUndirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewUndirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(2), W: 2}, {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -144,7 +144,7 @@ var ShortestPathTests = []struct { }, { Name: "confounding paths directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->5 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -178,7 +178,7 @@ var ShortestPathTests = []struct { }, { Name: "confounding paths undirected", - Graph: func() graph.EdgeSetter { return simple.NewUndirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewUndirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->5 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -212,7 +212,7 @@ var ShortestPathTests = []struct { }, { Name: "confounding paths directed 2-step", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->5 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -247,7 +247,7 @@ var ShortestPathTests = []struct { }, { Name: "confounding paths undirected 2-step", - Graph: func() graph.EdgeSetter { return simple.NewUndirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewUndirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->5 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -282,7 +282,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight cycle directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->4 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -306,7 +306,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight cycle^2 directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->4 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -333,7 +333,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight cycle^2 confounding directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->4 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -363,7 +363,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight cycle^3 directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->4 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -393,7 +393,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight 3·cycle^2 confounding directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->4 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -429,7 +429,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight reversed 3·cycle^2 confounding directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ // Add a path from 0->4 of weight 4 {F: simple.Node(0), T: simple.Node(1), W: 1}, @@ -465,7 +465,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight |V|·cycle^(n/|V|) directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: func() []simple.Edge { e := []simple.Edge{ // Add a path from 0->4 of weight 4 @@ -498,7 +498,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight n·cycle directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: func() []simple.Edge { e := []simple.Edge{ // Add a path from 0->4 of weight 4 @@ -531,7 +531,7 @@ var ShortestPathTests = []struct { }, { Name: "zero-weight bi-directional tree with single exit directed", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: func() []simple.Edge { e := []simple.Edge{ // Add a path from 0->4 of weight 4 @@ -579,7 +579,7 @@ var ShortestPathTests = []struct { // Negative weighted graphs. { Name: "one edge directed negative", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(1), W: -1}, }, @@ -596,7 +596,7 @@ var ShortestPathTests = []struct { }, { Name: "one edge undirected negative", - Graph: func() graph.EdgeSetter { return simple.NewUndirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewUndirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(1), W: -1}, }, @@ -607,7 +607,7 @@ var ShortestPathTests = []struct { }, { Name: "wp graph negative", // http://en.wikipedia.org/w/index.php?title=Johnson%27s_algorithm&oldid=564595231 - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node('w'), T: simple.Node('z'), W: 2}, {F: simple.Node('x'), T: simple.Node('w'), W: 6}, @@ -630,7 +630,7 @@ var ShortestPathTests = []struct { }, { Name: "roughgarden negative", - Graph: func() graph.EdgeSetter { return simple.NewDirectedGraph(0, math.Inf(1)) }, + Graph: func() graph.EdgeAdder { return simple.NewDirectedGraph(0, math.Inf(1)) }, Edges: []simple.Edge{ {F: simple.Node('a'), T: simple.Node('b'), W: -2}, {F: simple.Node('b'), T: simple.Node('c'), W: -1}, diff --git a/graph/simple/directed.go b/graph/simple/directed.go index 4172a8ff..1b84f677 100644 --- a/graph/simple/directed.go +++ b/graph/simple/directed.go @@ -80,6 +80,11 @@ func (g *DirectedGraph) RemoveNode(n graph.Node) { g.nodeIDs.release(n.ID()) } +// NewEdge returns a new Edge from the source to the destination node. +func (g *DirectedGraph) NewEdge(from, to graph.Node) graph.Edge { + return &Edge{F: from, T: to} +} + // SetEdge adds e, an edge from one node to another. If the nodes do not exist, they are added. // It will panic if the IDs of the e.From and e.To are equal. func (g *DirectedGraph) SetEdge(e graph.Edge) { diff --git a/graph/simple/undirected.go b/graph/simple/undirected.go index 37a76e16..3b99ca25 100644 --- a/graph/simple/undirected.go +++ b/graph/simple/undirected.go @@ -72,6 +72,11 @@ func (g *UndirectedGraph) RemoveNode(n graph.Node) { g.nodeIDs.release(n.ID()) } +// NewEdge returns a new Edge from the source to the destination node. +func (g *UndirectedGraph) NewEdge(from, to graph.Node) graph.Edge { + return &Edge{F: from, T: to} +} + // SetEdge adds e, an edge from one node to another. If the nodes do not exist, they are added. // It will panic if the IDs of the e.From and e.To are equal. func (g *UndirectedGraph) SetEdge(e graph.Edge) {