graph: add node, edge and line iterators

This commit is contained in:
Dan Kortschak
2018-09-23 09:25:56 +09:30
committed by GitHub
parent 20644dbe1a
commit b73a2c92ff
72 changed files with 1521 additions and 576 deletions

View File

@@ -26,7 +26,7 @@ func KCliqueCommunities(k int, g graph.Undirected) [][]graph.Node {
} }
switch k { switch k {
case 1: case 1:
return [][]graph.Node{g.Nodes()} return [][]graph.Node{graph.NodesOf(g.Nodes())}
case 2: case 2:
return topo.ConnectedComponents(g) return topo.ConnectedComponents(g)
default: default:

View File

@@ -95,11 +95,11 @@ func Modularize(g graph.Graph, resolution float64, src rand.Source) ReducedGraph
// Multiplex is a multiplex graph. // Multiplex is a multiplex graph.
type Multiplex interface { type Multiplex interface {
// Nodes returns the slice of nodes // Nodes returns the nodes
// for the multiplex graph. // for the multiplex graph.
// All layers must refer to the same // All layers must refer to the same
// set of nodes. // set of nodes.
Nodes() []graph.Node Nodes() graph.Nodes
// Depth returns the number of layers // Depth returns the number of layers
// in the multiplex graph. // in the multiplex graph.

View File

@@ -13,6 +13,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set" "gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
) )
// qDirected returns the modularity Q score of the graph g subdivided into the // qDirected returns the modularity Q score of the graph g subdivided into the
@@ -24,7 +25,7 @@ import (
// Q = 1/m \sum_{ij} [ A_{ij} - (\gamma k_i^in k_j^out)/m ] \delta(c_i,c_j) // Q = 1/m \sum_{ij} [ A_{ij} - (\gamma k_i^in k_j^out)/m ] \delta(c_i,c_j)
// //
func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) float64 { func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
weight := positiveWeightFuncFor(g) weight := positiveWeightFuncFor(g)
// Calculate the total edge weight of the graph // Calculate the total edge weight of the graph
@@ -35,14 +36,16 @@ func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64)
var wOut float64 var wOut float64
u := n u := n
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { to := g.From(uid)
wOut += weight(uid, v.ID()) for to.Next() {
wOut += weight(uid, to.Node().ID())
} }
var wIn float64 var wIn float64
v := n v := n
vid := v.ID() vid := v.ID()
for _, u := range g.To(vid) { from := g.To(vid)
wIn += weight(u.ID(), vid) for from.Next() {
wIn += weight(from.Node().ID(), vid)
} }
id := n.ID() id := n.ID()
w := weight(id, id) w := weight(id, id)
@@ -177,7 +180,7 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
return r return r
} }
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only // TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the // for testing. In practice we would not be using the
// community provided by the user for a Q calculation. // community provided by the user for a Q calculation.
@@ -210,8 +213,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
var out []int var out []int
u := n u := n
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { to := g.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
if vcid != id { if vcid != id {
out = append(out, vcid) out = append(out, vcid)
@@ -223,8 +227,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
var in []int var in []int
v := n v := n
vid := v.ID() vid := v.ID()
for _, u := range g.To(vid) { from := g.To(vid)
uid := u.ID() for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid] ucid := communityOf[uid]
if ucid != id { if ucid != id {
in = append(in, ucid) in = append(in, ucid)
@@ -285,8 +290,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
r.nodes[id].weight += weight(uid, v.ID()) r.nodes[id].weight += weight(uid, v.ID())
} }
for _, v := range g.From(uid) { to := g.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
found := false found := false
for _, e := range out { for _, e := range out {
@@ -305,8 +311,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
v := n v := n
vid := v.ID() vid := v.ID()
for _, u := range g.To(vid) { from := g.To(vid)
uid := u.ID() for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid] ucid := communityOf[uid]
found := false found := false
for _, e := range in { for _, e := range in {
@@ -335,32 +342,32 @@ func (g *ReducedDirected) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *ReducedDirected) Nodes() []graph.Node { func (g *ReducedDirected) Nodes() graph.Nodes {
nodes := make([]graph.Node, len(g.nodes)) nodes := make([]graph.Node, len(g.nodes))
for i := range g.nodes { for i := range g.nodes {
nodes[i] = node(i) nodes[i] = node(i)
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// From returns all nodes in g that can be reached directly from u. // From returns all nodes in g that can be reached directly from u.
func (g *ReducedDirected) From(uid int64) []graph.Node { func (g *ReducedDirected) From(uid int64) graph.Nodes {
out := g.edgesFrom[uid] out := g.edgesFrom[uid]
nodes := make([]graph.Node, len(out)) nodes := make([]graph.Node, len(out))
for i, vid := range out { for i, vid := range out {
nodes[i] = g.nodes[vid] nodes[i] = g.nodes[vid]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// To returns all nodes in g that can reach directly to v. // To returns all nodes in g that can reach directly to v.
func (g *ReducedDirected) To(vid int64) []graph.Node { func (g *ReducedDirected) To(vid int64) graph.Nodes {
in := g.edgesTo[vid] in := g.edgesTo[vid]
nodes := make([]graph.Node, len(in)) nodes := make([]graph.Node, len(in))
for i, uid := range in { for i, uid := range in {
nodes[i] = g.nodes[uid] nodes[i] = g.nodes[uid]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -473,7 +480,7 @@ type directedWeights struct {
// nodes. // nodes.
// If g has a zero edge weight sum, nil is returned. // If g has a zero edge weight sum, nil is returned.
func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resolution float64) *directedLocalMover { func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resolution float64) *directedLocalMover {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
l := directedLocalMover{ l := directedLocalMover{
g: g, g: g,
nodes: nodes, nodes: nodes,
@@ -490,15 +497,17 @@ func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resol
u := n u := n
var wOut float64 var wOut float64
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { to := g.From(uid)
wOut += l.weight(uid, v.ID()) for to.Next() {
wOut += l.weight(uid, to.Node().ID())
} }
v := n v := n
var wIn float64 var wIn float64
vid := v.ID() vid := v.ID()
for _, u := range g.To(vid) { from := g.To(vid)
wIn += l.weight(u.ID(), vid) for from.Next() {
wIn += l.weight(from.Node().ID(), vid)
} }
id := n.ID() id := n.ID()

View File

@@ -14,6 +14,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set" "gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
) )
// DirectedMultiplex is a directed multiplex graph. // DirectedMultiplex is a directed multiplex graph.
@@ -43,7 +44,7 @@ type DirectedMultiplex interface {
// Note that Q values for multiplex graphs are not scaled by the total layer edge weight. // Note that Q values for multiplex graphs are not scaled by the total layer edge weight.
func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 { func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
q := make([]float64, g.Depth()) q := make([]float64, g.Depth())
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
layerWeight := 1.0 layerWeight := 1.0
layerResolution := 1.0 layerResolution := 1.0
if len(resolutions) == 1 { if len(resolutions) == 1 {
@@ -78,14 +79,16 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights
var wOut float64 var wOut float64
u := n u := n
uid := u.ID() uid := u.ID()
for _, v := range layer.From(uid) { to := layer.From(uid)
wOut += weight(uid, v.ID()) for to.Next() {
wOut += weight(uid, to.Node().ID())
} }
var wIn float64 var wIn float64
v := n v := n
vid := v.ID() vid := v.ID()
for _, u := range layer.To(vid) { from := layer.To(vid)
wIn += weight(u.ID(), vid) for from.Next() {
wIn += weight(from.Node().ID(), vid)
} }
id := n.ID() id := n.ID()
w := weight(id, id) w := weight(id, id)
@@ -132,13 +135,15 @@ func NewDirectedLayers(layers ...graph.Directed) (DirectedLayers, error) {
return nil, nil return nil, nil
} }
base := make(set.Int64s) base := make(set.Int64s)
for _, n := range layers[0].Nodes() { nodes := layers[0].Nodes()
base.Add(n.ID()) for nodes.Next() {
base.Add(nodes.Node().ID())
} }
for i, l := range layers[1:] { for i, l := range layers[1:] {
next := make(set.Int64s) next := make(set.Int64s)
for _, n := range l.Nodes() { nodes := l.Nodes()
next.Add(n.ID()) for nodes.Next() {
next.Add(nodes.Node().ID())
} }
if !set.Int64sEqual(base, next) { if !set.Int64sEqual(base, next) {
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1) return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
@@ -148,7 +153,7 @@ func NewDirectedLayers(layers ...graph.Directed) (DirectedLayers, error) {
} }
// Nodes returns the nodes of the receiver. // Nodes returns the nodes of the receiver.
func (g DirectedLayers) Nodes() []graph.Node { func (g DirectedLayers) Nodes() graph.Nodes {
if len(g) == 0 { if len(g) == 0 {
return nil return nil
} }
@@ -219,12 +224,12 @@ var (
) )
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *ReducedDirectedMultiplex) Nodes() []graph.Node { func (g *ReducedDirectedMultiplex) Nodes() graph.Nodes {
nodes := make([]graph.Node, len(g.nodes)) nodes := make([]graph.Node, len(g.nodes))
for i := range g.nodes { for i := range g.nodes {
nodes[i] = node(i) nodes[i] = node(i)
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Depth returns the number of layers in the multiplex graph. // Depth returns the number of layers in the multiplex graph.
@@ -288,7 +293,7 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
return r return r
} }
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only // TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the // for testing. In practice we would not be using the
// community provided by the user for a Q calculation. // community provided by the user for a Q calculation.
@@ -339,8 +344,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
var out []int var out []int
u := n u := n
uid := u.ID() uid := u.ID()
for _, v := range layer.From(uid) { to := layer.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
if vcid != id { if vcid != id {
out = append(out, vcid) out = append(out, vcid)
@@ -352,8 +358,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
var in []int var in []int
v := n v := n
vid := v.ID() vid := v.ID()
for _, u := range layer.To(vid) { from := layer.To(vid)
uid := u.ID() for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid] ucid := communityOf[uid]
if ucid != id { if ucid != id {
in = append(in, ucid) in = append(in, ucid)
@@ -433,8 +440,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
r.nodes[id].weights[l] += sign * weight(uid, v.ID()) r.nodes[id].weights[l] += sign * weight(uid, v.ID())
} }
for _, v := range layer.From(uid) { to := layer.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
found := false found := false
for _, e := range out { for _, e := range out {
@@ -453,8 +461,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
v := n v := n
vid := v.ID() vid := v.ID()
for _, u := range layer.To(vid) { from := layer.To(vid)
uid := u.ID() for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid] ucid := communityOf[uid]
found := false found := false
for _, e := range in { for _, e := range in {
@@ -497,32 +506,32 @@ func (g directedLayerHandle) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g directedLayerHandle) Nodes() []graph.Node { func (g directedLayerHandle) Nodes() graph.Nodes {
nodes := make([]graph.Node, len(g.multiplex.nodes)) nodes := make([]graph.Node, len(g.multiplex.nodes))
for i := range g.multiplex.nodes { for i := range g.multiplex.nodes {
nodes[i] = node(i) nodes[i] = node(i)
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// From returns all nodes in g that can be reached directly from u. // From returns all nodes in g that can be reached directly from u.
func (g directedLayerHandle) From(uid int64) []graph.Node { func (g directedLayerHandle) From(uid int64) graph.Nodes {
out := g.multiplex.layers[g.layer].edgesFrom[uid] out := g.multiplex.layers[g.layer].edgesFrom[uid]
nodes := make([]graph.Node, len(out)) nodes := make([]graph.Node, len(out))
for i, vid := range out { for i, vid := range out {
nodes[i] = g.multiplex.nodes[vid] nodes[i] = g.multiplex.nodes[vid]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// To returns all nodes in g that can reach directly to v. // To returns all nodes in g that can reach directly to v.
func (g directedLayerHandle) To(vid int64) []graph.Node { func (g directedLayerHandle) To(vid int64) graph.Nodes {
in := g.multiplex.layers[g.layer].edgesTo[vid] in := g.multiplex.layers[g.layer].edgesTo[vid]
nodes := make([]graph.Node, len(in)) nodes := make([]graph.Node, len(in))
for i, uid := range in { for i, uid := range in {
nodes[i] = g.multiplex.nodes[uid] nodes[i] = g.multiplex.nodes[uid]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -643,7 +652,7 @@ type directedMultiplexLocalMover struct {
// node IDs of g must be contiguous in [0,n) where n is the number of nodes. // node IDs of g must be contiguous in [0,n) where n is the number of nodes.
// If g has a zero edge weight sum, nil is returned. // If g has a zero edge weight sum, nil is returned.
func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *directedMultiplexLocalMover { func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *directedMultiplexLocalMover {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
l := directedMultiplexLocalMover{ l := directedMultiplexLocalMover{
g: g, g: g,
nodes: nodes, nodes: nodes,
@@ -684,15 +693,17 @@ func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][
u := n u := n
uid := u.ID() uid := u.ID()
var wOut float64 var wOut float64
for _, v := range layer.From(uid) { to := layer.From(uid)
wOut += weight(uid, v.ID()) for to.Next() {
wOut += weight(uid, to.Node().ID())
} }
v := n v := n
vid := v.ID() vid := v.ID()
var wIn float64 var wIn float64
for _, u := range layer.To(vid) { from := layer.To(vid)
wIn += weight(u.ID(), vid) for from.Next() {
wIn += weight(from.Node().ID(), vid)
} }
id := n.ID() id := n.ID()

View File

@@ -244,7 +244,7 @@ func init() {
// such that every edge dupGraph is replaced // such that every edge dupGraph is replaced
// with an edge that flows from the low node // with an edge that flows from the low node
// ID to the high node ID. // ID to the high node ID.
for _, e := range dupGraph.Edges() { for _, e := range graph.EdgesOf(dupGraph.Edges()) {
if e.To().ID() < e.From().ID() { if e.To().ID() < e.From().ID() {
se := e.(simple.Edge) se := e.(simple.Edge)
se.F, se.T = se.T, se.F se.F, se.T = se.T, se.F

View File

@@ -13,6 +13,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set" "gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
) )
// qUndirected returns the modularity Q score of the graph g subdivided into the // qUndirected returns the modularity Q score of the graph g subdivided into the
@@ -26,7 +27,7 @@ import (
// graph.Undirect may be used as a shim to allow calculation of Q for // graph.Undirect may be used as a shim to allow calculation of Q for
// directed graphs. // directed graphs.
func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution float64) float64 { func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution float64) float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
weight := positiveWeightFuncFor(g) weight := positiveWeightFuncFor(g)
// Calculate the total edge weight of the graph // Calculate the total edge weight of the graph
@@ -36,8 +37,9 @@ func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution floa
for _, u := range nodes { for _, u := range nodes {
uid := u.ID() uid := u.ID()
w := weight(uid, uid) w := weight(uid, uid)
for _, v := range g.From(uid) { to := g.From(uid)
w += weight(uid, v.ID()) for to.Next() {
w += weight(uid, to.Node().ID())
} }
m2 += w m2 += w
k[uid] = w k[uid] = w
@@ -175,7 +177,7 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
return r return r
} }
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only // TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the // for testing. In practice we would not be using the
// community provided by the user for a Q calculation. // community provided by the user for a Q calculation.
@@ -205,8 +207,9 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
uid := u.ID() uid := u.ID()
ucid := communityOf[uid] ucid := communityOf[uid]
var out []int var out []int
for _, v := range g.From(uid) { to := g.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
if vcid != ucid { if vcid != ucid {
out = append(out, vcid) out = append(out, vcid)
@@ -268,8 +271,9 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
for _, v := range comm[i+1:] { for _, v := range comm[i+1:] {
r.nodes[ucid].weight += 2 * weight(uid, v.ID()) r.nodes[ucid].weight += 2 * weight(uid, v.ID())
} }
for _, v := range g.From(uid) { to := g.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
found := false found := false
for _, e := range out { for _, e := range out {
@@ -298,22 +302,22 @@ func (g *ReducedUndirected) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *ReducedUndirected) Nodes() []graph.Node { func (g *ReducedUndirected) Nodes() graph.Nodes {
nodes := make([]graph.Node, len(g.nodes)) nodes := make([]graph.Node, len(g.nodes))
for i := range g.nodes { for i := range g.nodes {
nodes[i] = node(i) nodes[i] = node(i)
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// From returns all nodes in g that can be reached directly from u. // From returns all nodes in g that can be reached directly from u.
func (g *ReducedUndirected) From(uid int64) []graph.Node { func (g *ReducedUndirected) From(uid int64) graph.Nodes {
out := g.edges[uid] out := g.edges[uid]
nodes := make([]graph.Node, len(out)) nodes := make([]graph.Node, len(out))
for i, vid := range out { for i, vid := range out {
nodes[i] = g.nodes[vid] nodes[i] = g.nodes[vid]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -427,7 +431,7 @@ type undirectedLocalMover struct {
// node IDs of g must be contiguous in [0,n) where n is the number of nodes. // node IDs of g must be contiguous in [0,n) where n is the number of nodes.
// If g has a zero edge weight sum, nil is returned. // If g has a zero edge weight sum, nil is returned.
func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, resolution float64) *undirectedLocalMover { func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, resolution float64) *undirectedLocalMover {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
l := undirectedLocalMover{ l := undirectedLocalMover{
g: g, g: g,
nodes: nodes, nodes: nodes,
@@ -443,8 +447,9 @@ func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, r
for _, u := range l.nodes { for _, u := range l.nodes {
uid := u.ID() uid := u.ID()
w := l.weight(uid, uid) w := l.weight(uid, uid)
for _, v := range g.From(uid) { to := g.From(uid)
w += l.weight(uid, v.ID()) for to.Next() {
w += l.weight(uid, to.Node().ID())
} }
l.edgeWeightOf[uid] = w l.edgeWeightOf[uid] = w
l.m2 += w l.m2 += w

View File

@@ -14,6 +14,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set" "gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
) )
// UndirectedMultiplex is an undirected multiplex graph. // UndirectedMultiplex is an undirected multiplex graph.
@@ -46,7 +47,7 @@ type UndirectedMultiplex interface {
// directed graphs. // directed graphs.
func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 { func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
q := make([]float64, g.Depth()) q := make([]float64, g.Depth())
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
layerWeight := 1.0 layerWeight := 1.0
layerResolution := 1.0 layerResolution := 1.0
if len(resolutions) == 1 { if len(resolutions) == 1 {
@@ -80,8 +81,9 @@ func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, wei
for _, u := range nodes { for _, u := range nodes {
uid := u.ID() uid := u.ID()
w := weight(uid, uid) w := weight(uid, uid)
for _, v := range layer.From(uid) { to := layer.From(uid)
w += weight(uid, v.ID()) for to.Next() {
w += weight(uid, to.Node().ID())
} }
m2 += w m2 += w
k[uid] = w k[uid] = w
@@ -129,13 +131,15 @@ func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
return nil, nil return nil, nil
} }
base := make(set.Int64s) base := make(set.Int64s)
for _, n := range layers[0].Nodes() { nodes := layers[0].Nodes()
base.Add(n.ID()) for nodes.Next() {
base.Add(nodes.Node().ID())
} }
for i, l := range layers[1:] { for i, l := range layers[1:] {
next := make(set.Int64s) next := make(set.Int64s)
for _, n := range l.Nodes() { nodes := l.Nodes()
next.Add(n.ID()) for nodes.Next() {
next.Add(nodes.Node().ID())
} }
if !set.Int64sEqual(next, base) { if !set.Int64sEqual(next, base) {
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1) return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
@@ -145,7 +149,7 @@ func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
} }
// Nodes returns the nodes of the receiver. // Nodes returns the nodes of the receiver.
func (g UndirectedLayers) Nodes() []graph.Node { func (g UndirectedLayers) Nodes() graph.Nodes {
if len(g) == 0 { if len(g) == 0 {
return nil return nil
} }
@@ -216,12 +220,12 @@ var (
) )
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *ReducedUndirectedMultiplex) Nodes() []graph.Node { func (g *ReducedUndirectedMultiplex) Nodes() graph.Nodes {
nodes := make([]graph.Node, len(g.nodes)) nodes := make([]graph.Node, len(g.nodes))
for i := range g.nodes { for i := range g.nodes {
nodes[i] = node(i) nodes[i] = node(i)
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Depth returns the number of layers in the multiplex graph. // Depth returns the number of layers in the multiplex graph.
@@ -285,7 +289,7 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
return r return r
} }
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only // TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the // for testing. In practice we would not be using the
// community provided by the user for a Q calculation. // community provided by the user for a Q calculation.
@@ -333,8 +337,9 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
var out []int var out []int
uid := u.ID() uid := u.ID()
ucid := communityOf[uid] ucid := communityOf[uid]
for _, v := range layer.From(uid) { to := layer.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
if vcid != ucid { if vcid != ucid {
out = append(out, vcid) out = append(out, vcid)
@@ -415,8 +420,9 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
for _, v := range comm[i+1:] { for _, v := range comm[i+1:] {
r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID()) r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID())
} }
for _, v := range layer.From(uid) { to := layer.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid] vcid := communityOf[vid]
found := false found := false
for _, e := range out { for _, e := range out {
@@ -458,22 +464,22 @@ func (g undirectedLayerHandle) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g undirectedLayerHandle) Nodes() []graph.Node { func (g undirectedLayerHandle) Nodes() graph.Nodes {
nodes := make([]graph.Node, len(g.multiplex.nodes)) nodes := make([]graph.Node, len(g.multiplex.nodes))
for i := range g.multiplex.nodes { for i := range g.multiplex.nodes {
nodes[i] = node(i) nodes[i] = node(i)
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// From returns all nodes in g that can be reached directly from u. // From returns all nodes in g that can be reached directly from u.
func (g undirectedLayerHandle) From(uid int64) []graph.Node { func (g undirectedLayerHandle) From(uid int64) graph.Nodes {
out := g.multiplex.layers[g.layer].edges[uid] out := g.multiplex.layers[g.layer].edges[uid]
nodes := make([]graph.Node, len(out)) nodes := make([]graph.Node, len(out))
for i, vid := range out { for i, vid := range out {
nodes[i] = g.multiplex.nodes[vid] nodes[i] = g.multiplex.nodes[vid]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -597,7 +603,7 @@ type undirectedMultiplexLocalMover struct {
// node IDs of g must be contiguous in [0,n) where n is the number of nodes. // node IDs of g must be contiguous in [0,n) where n is the number of nodes.
// If g has a zero edge weight sum, nil is returned. // If g has a zero edge weight sum, nil is returned.
func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *undirectedMultiplexLocalMover { func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *undirectedMultiplexLocalMover {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
l := undirectedMultiplexLocalMover{ l := undirectedMultiplexLocalMover{
g: g, g: g,
nodes: nodes, nodes: nodes,
@@ -637,8 +643,9 @@ func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities
for _, u := range l.nodes { for _, u := range l.nodes {
uid := u.ID() uid := u.ID()
w := weight(uid, uid) w := weight(uid, uid)
for _, v := range layer.From(uid) { to := layer.From(uid)
w += weight(uid, v.ID()) for to.Next() {
w += weight(uid, to.Node().ID())
} }
l.edgeWeightOf[i][uid] = w l.edgeWeightOf[i][uid] = w
l.m2[i] += w l.m2[i] += w

View File

@@ -109,7 +109,7 @@ type edge struct {
} }
func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error { func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
sort.Sort(ordered.ByID(nodes)) sort.Sort(ordered.ByID(nodes))
p.buf.WriteString(p.prefix) p.buf.WriteString(p.prefix)
@@ -156,7 +156,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
if s, ok := n.(Subgrapher); ok { if s, ok := n.(Subgrapher); ok {
// If the node is not linked to any other node // If the node is not linked to any other node
// the graph needs to be written now. // the graph needs to be written now.
if len(g.From(n.ID())) == 0 { if g.From(n.ID()).Len() == 0 {
g := s.Subgraph() g := s.Subgraph()
_, subIsDirected := g.(graph.Directed) _, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected { if subIsDirected != isDirected {
@@ -188,7 +188,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
havePrintedEdgeHeader := false havePrintedEdgeHeader := false
for _, n := range nodes { for _, n := range nodes {
nid := n.ID() nid := n.ID()
to := g.From(nid) to := graph.NodesOf(g.From(nid))
sort.Sort(ordered.ByID(to)) sort.Sort(ordered.ByID(to))
for _, t := range to { for _, t := range to {
tid := t.ID() tid := t.ID()

View File

@@ -6,6 +6,7 @@ package main
import ( import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/simple"
) )
@@ -83,7 +84,7 @@ func (g *GraphNode) has(id int64, visited map[int64]struct{}) bool {
return false return false
} }
func (g *GraphNode) Nodes() []graph.Node { func (g *GraphNode) Nodes() graph.Nodes {
toReturn := []graph.Node{g} toReturn := []graph.Node{g}
visited := map[int64]struct{}{g.id: {}} visited := map[int64]struct{}{g.id: {}}
@@ -103,7 +104,7 @@ func (g *GraphNode) Nodes() []graph.Node {
} }
} }
return toReturn return iterator.NewOrderedNodes(toReturn)
} }
func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph.Node { func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph.Node {
@@ -131,9 +132,9 @@ func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph
return list return list
} }
func (g *GraphNode) From(id int64) []graph.Node { func (g *GraphNode) From(id int64) graph.Nodes {
if id == g.ID() { if id == g.ID() {
return g.neighbors return iterator.NewOrderedNodes(g.neighbors)
} }
visited := map[int64]struct{}{g.id: {}} visited := map[int64]struct{}{g.id: {}}
@@ -141,7 +142,7 @@ func (g *GraphNode) From(id int64) []graph.Node {
visited[root.ID()] = struct{}{} visited[root.ID()] = struct{}{}
if result := root.findNeighbors(id, visited); result != nil { if result := root.findNeighbors(id, visited); result != nil {
return result return iterator.NewOrderedNodes(result)
} }
} }
@@ -150,7 +151,7 @@ func (g *GraphNode) From(id int64) []graph.Node {
if gn, ok := neigh.(*GraphNode); ok { if gn, ok := neigh.(*GraphNode); ok {
if result := gn.findNeighbors(id, visited); result != nil { if result := gn.findNeighbors(id, visited); result != nil {
return result return iterator.NewOrderedNodes(result)
} }
} }
} }

View File

@@ -32,11 +32,11 @@ type Graph interface {
Has(id int64) bool Has(id int64) bool
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
Nodes() []Node Nodes() Nodes
// From returns all nodes that can be reached directly // From returns all nodes that can be reached directly
// from the node with the given ID. // from the node with the given ID.
From(id int64) []Node From(id int64) Nodes
// HasEdgeBetween returns whether an edge exists between // HasEdgeBetween returns whether an edge exists between
// nodes with IDs xid and yid without considering direction. // nodes with IDs xid and yid without considering direction.
@@ -99,7 +99,7 @@ type Directed interface {
// To returns all nodes that can reach directly // To returns all nodes that can reach directly
// to the node with the given ID. // to the node with the given ID.
To(id int64) []Node To(id int64) Nodes
} }
// WeightedDirected is a weighted directed graph. // WeightedDirected is a weighted directed graph.
@@ -113,7 +113,7 @@ type WeightedDirected interface {
// To returns all nodes that can reach directly // To returns all nodes that can reach directly
// to the node with the given ID. // to the node with the given ID.
To(id int64) []Node To(id int64) Nodes
} }
// NodeAdder is an interface for adding arbitrary nodes to a graph. // NodeAdder is an interface for adding arbitrary nodes to a graph.
@@ -219,11 +219,15 @@ type DirectedWeightedBuilder interface {
// be present in the destination after the copy is complete. // be present in the destination after the copy is complete.
func Copy(dst Builder, src Graph) { func Copy(dst Builder, src Graph) {
nodes := src.Nodes() nodes := src.Nodes()
for _, n := range nodes { for nodes.Next() {
dst.AddNode(n) dst.AddNode(nodes.Node())
} }
for _, u := range nodes { nodes.Reset()
for _, v := range src.From(u.ID()) { for nodes.Next() {
u := nodes.Node()
to := src.From(u.ID())
for to.Next() {
v := to.Node()
dst.SetEdge(dst.NewEdge(u, v)) dst.SetEdge(dst.NewEdge(u, v))
} }
} }
@@ -242,11 +246,15 @@ func Copy(dst Builder, src Graph) {
// to resolve such conflicts, an UndirectWeighted may be used to do this. // to resolve such conflicts, an UndirectWeighted may be used to do this.
func CopyWeighted(dst WeightedBuilder, src Weighted) { func CopyWeighted(dst WeightedBuilder, src Weighted) {
nodes := src.Nodes() nodes := src.Nodes()
for _, n := range nodes { for nodes.Next() {
dst.AddNode(n) dst.AddNode(nodes.Node())
} }
for _, u := range nodes { nodes.Reset()
for _, v := range src.From(u.ID()) { for nodes.Next() {
u := nodes.Node()
to := src.From(u.ID())
for to.Next() {
v := to.Node()
dst.SetWeightedEdge(dst.NewWeightedEdge(u, v, src.WeightedEdge(u.ID(), v.ID()).Weight())) dst.SetWeightedEdge(dst.NewWeightedEdge(u, v, src.WeightedEdge(u.ID(), v.ID()).Weight()))
} }
} }

View File

@@ -262,8 +262,8 @@ func TestCopyWeighted(t *testing.T) {
} }
func same(a, b graph.Graph) bool { func same(a, b graph.Graph) bool {
aNodes := a.Nodes() aNodes := graph.NodesOf(a.Nodes())
bNodes := b.Nodes() bNodes := graph.NodesOf(b.Nodes())
sort.Sort(ordered.ByID(aNodes)) sort.Sort(ordered.ByID(aNodes))
sort.Sort(ordered.ByID(bNodes)) sort.Sort(ordered.ByID(bNodes))
for i, na := range aNodes { for i, na := range aNodes {
@@ -272,9 +272,9 @@ func same(a, b graph.Graph) bool {
return false return false
} }
} }
for _, u := range a.Nodes() { for _, u := range graph.NodesOf(a.Nodes()) {
aFromU := a.From(u.ID()) aFromU := graph.NodesOf(a.From(u.ID()))
bFromU := b.From(u.ID()) bFromU := graph.NodesOf(b.From(u.ID()))
if len(aFromU) != len(bFromU) { if len(aFromU) != len(bFromU) {
return false return false
} }

View File

@@ -185,15 +185,16 @@ func TestPowerLawUndirected(t *testing.T) {
} }
nodes := g.Nodes() nodes := g.Nodes()
if len(nodes) != n { if nodes.Len() != n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes)) t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
} }
for _, u := range nodes { for nodes.Next() {
u := nodes.Node()
uid := u.ID() uid := u.ID()
var lines int var lines int
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
lines += len(g.Lines(uid, v.ID())) lines += g.Lines(uid, v.ID()).Len()
} }
if lines < d { if lines < d {
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
@@ -214,15 +215,16 @@ func TestPowerLawDirected(t *testing.T) {
} }
nodes := g.Nodes() nodes := g.Nodes()
if len(nodes) != n { if nodes.Len() != n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes)) t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
} }
for _, u := range nodes { for nodes.Next() {
u := nodes.Node()
uid := u.ID() uid := u.ID()
var lines int var lines int
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
lines += len(g.Lines(uid, v.ID())) lines += g.Lines(uid, v.ID()).Len()
} }
if lines < d { if lines < d {
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
@@ -243,8 +245,8 @@ func TestBipartitePowerLawUndirected(t *testing.T) {
} }
nodes := g.Nodes() nodes := g.Nodes()
if len(nodes) != 2*n { if nodes.Len() != 2*n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes)) t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
} }
if len(p1) != n { if len(p1) != n {
t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1)) t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1))
@@ -266,11 +268,12 @@ func TestBipartitePowerLawUndirected(t *testing.T) {
t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o)) t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o))
} }
for _, u := range nodes { for nodes.Next() {
u := nodes.Node()
uid := u.ID() uid := u.ID()
var lines int var lines int
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
lines += len(g.Lines(uid, v.ID())) lines += g.Lines(uid, v.ID()).Len()
} }
if lines < d { if lines < d {
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
@@ -291,8 +294,8 @@ func TestBipartitePowerLawDirected(t *testing.T) {
} }
nodes := g.Nodes() nodes := g.Nodes()
if len(nodes) != 2*n { if nodes.Len() != 2*n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes)) t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
} }
if len(p1) != n { if len(p1) != n {
t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1)) t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1))
@@ -314,11 +317,12 @@ func TestBipartitePowerLawDirected(t *testing.T) {
t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o)) t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o))
} }
for _, u := range nodes { for nodes.Next() {
u := nodes.Node()
uid := u.ID() uid := u.ID()
var lines int var lines int
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
lines += len(g.Lines(uid, v.ID())) lines += g.Lines(uid, v.ID()).Len()
} }
if lines < d { if lines < d {
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)

View File

@@ -56,7 +56,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
rndN = r.Intn rndN = r.Intn
} }
nodes := dst.Nodes() nodes := graph.NodesOf(dst.Nodes())
sort.Sort(ordered.ByID(nodes)) sort.Sort(ordered.ByID(nodes))
if len(nodes) == 0 { if len(nodes) == 0 {
n-- n--
@@ -76,7 +76,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
// into the rest of the graph. // into the rest of the graph.
for { for {
// Add edges to parent's neighbours. // Add edges to parent's neighbours.
to := dst.From(u.ID()) to := graph.NodesOf(dst.From(u.ID()))
sort.Sort(ordered.ByID(to)) sort.Sort(ordered.ByID(to))
for _, v := range to { for _, v := range to {
vid := v.ID() vid := v.ID()
@@ -119,7 +119,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
} }
} }
if len(dst.From(did)) != 0 { if dst.From(did).Len() != 0 {
break break
} }
} }

View File

@@ -69,7 +69,7 @@ func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64
for i := 0; i < m; i++ { for i := 0; i < m; i++ {
// Triad formation. // Triad formation.
if i != 0 && rnd() < p { if i != 0 && rnd() < p {
for _, w := range permute(dst.From(int64(u)), rndN) { for _, w := range permute(graph.NodesOf(dst.From(int64(u))), rndN) {
wid := w.ID() wid := w.ID()
if wid == int64(v) || dst.HasEdgeBetween(wid, int64(v)) { if wid == int64(v) || dst.HasEdgeBetween(wid, int64(v)) {
continue continue

9
graph/iterator/doc.go Normal file
View File

@@ -0,0 +1,9 @@
// Copyright ©2018 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 iterator provides node, edge and line iterators.
//
// The iterators provided satisfy the graph.Nodes, graph.Edges and
// graph.Lines interfaces.
package iterator

131
graph/iterator/edges.go Normal file
View File

@@ -0,0 +1,131 @@
// Copyright ©2018 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 iterator
import "gonum.org/v1/gonum/graph"
// OrderedEdges implements the graph.Edges and graph.EdgeSlicer interfaces.
// The iteration order of OrderedEdges is the order of edges passed to
// NewEdgeIterator.
type OrderedEdges struct {
idx int
edges []graph.Edge
}
// NewOrderedEdges returns an OrderedEdges initialized with the provided edges.
func NewOrderedEdges(edges []graph.Edge) *OrderedEdges {
return &OrderedEdges{idx: -1, edges: edges}
}
// Len returns the remaining number of edges to be iterated over.
func (e *OrderedEdges) Len() int {
if e.idx >= len(e.edges) {
return 0
}
if e.idx <= 0 {
return len(e.edges)
}
return len(e.edges[e.idx:])
}
// Next returns whether the next call of Edge will return a valid edge.
func (e *OrderedEdges) Next() bool {
if uint(e.idx)+1 < uint(len(e.edges)) {
e.idx++
return true
}
e.idx = len(e.edges)
return false
}
// Edge returns the current edge of the iterator. Next must have been
// called prior to a call to Edge.
func (e *OrderedEdges) Edge() graph.Edge {
if e.idx >= len(e.edges) || e.idx < 0 {
return nil
}
return e.edges[e.idx]
}
// EdgeSlice returns all the remaining edges in the iterator and advances
// the iterator.
func (e *OrderedEdges) EdgeSlice() []graph.Edge {
if e.idx >= len(e.edges) {
return nil
}
idx := e.idx
if idx == -1 {
idx = 0
}
e.idx = len(e.edges)
return e.edges[idx:]
}
// Reset returns the iterator to its initial state.
func (e *OrderedEdges) Reset() {
e.idx = -1
}
// OrderedWeightedEdges implements the graph.Edges and graph.EdgeSlicer interfaces.
// The iteration order of OrderedWeightedEdges is the order of edges passed to
// NewEdgeIterator.
type OrderedWeightedEdges struct {
idx int
edges []graph.WeightedEdge
}
// NewOrderedWeightedEdges returns an OrderedWeightedEdges initialized with the provided edges.
func NewOrderedWeightedEdges(edges []graph.WeightedEdge) *OrderedWeightedEdges {
return &OrderedWeightedEdges{idx: -1, edges: edges}
}
// Len returns the remaining number of edges to be iterated over.
func (e *OrderedWeightedEdges) Len() int {
if e.idx >= len(e.edges) {
return 0
}
if e.idx <= 0 {
return len(e.edges)
}
return len(e.edges[e.idx:])
}
// Next returns whether the next call of WeightedEdge will return a valid edge.
func (e *OrderedWeightedEdges) Next() bool {
if uint(e.idx)+1 < uint(len(e.edges)) {
e.idx++
return true
}
e.idx = len(e.edges)
return false
}
// WeightedEdge returns the current edge of the iterator. Next must have been
// called prior to a call to WeightedEdge.
func (e *OrderedWeightedEdges) WeightedEdge() graph.WeightedEdge {
if e.idx >= len(e.edges) || e.idx < 0 {
return nil
}
return e.edges[e.idx]
}
// WeightedEdgeSlice returns all the remaining edges in the iterator and advances
// the iterator.
func (e *OrderedWeightedEdges) WeightedEdgeSlice() []graph.WeightedEdge {
if e.idx >= len(e.edges) {
return nil
}
idx := e.idx
if idx == -1 {
idx = 0
}
e.idx = len(e.edges)
return e.edges[idx:]
}
// Reset returns the iterator to its initial state.
func (e *OrderedWeightedEdges) Reset() {
e.idx = -1
}

View File

@@ -0,0 +1,114 @@
// Copyright ©2018 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 iterator_test
import (
"reflect"
"testing"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple"
)
type edge struct{ f, t int }
func (e edge) From() graph.Node { return simple.Node(e.f) }
func (e edge) To() graph.Node { return simple.Node(e.t) }
var orderedEdgesTests = []struct {
edges []graph.Edge
}{
{edges: nil},
{edges: []graph.Edge{edge{f: 1, t: 2}}},
{edges: []graph.Edge{edge{f: 1, t: 2}, edge{f: 2, t: 3}, edge{f: 3, t: 4}, edge{f: 4, t: 5}}},
{edges: []graph.Edge{edge{f: 5, t: 4}, edge{f: 4, t: 3}, edge{f: 3, t: 2}, edge{f: 2, t: 1}}},
}
func TestOrderedEdgesIterate(t *testing.T) {
for _, test := range orderedEdgesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedEdges(test.edges)
if it.Len() != len(test.edges) {
t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.edges))
}
var got []graph.Edge
for it.Next() {
got = append(got, it.Edge())
}
want := test.edges
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
func TestOrderedEdgesSlice(t *testing.T) {
for _, test := range orderedEdgesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedEdges(test.edges)
got := it.EdgeSlice()
want := test.edges
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
type weightedEdge struct {
f, t int
w float64
}
func (e weightedEdge) From() graph.Node { return simple.Node(e.f) }
func (e weightedEdge) To() graph.Node { return simple.Node(e.t) }
func (e weightedEdge) Weight() float64 { return e.w }
var orderedWeightedEdgesTests = []struct {
edges []graph.WeightedEdge
}{
{edges: nil},
{edges: []graph.WeightedEdge{weightedEdge{f: 1, t: 2, w: 1}}},
{edges: []graph.WeightedEdge{weightedEdge{f: 1, t: 2, w: 1}, weightedEdge{f: 2, t: 3, w: 2}, weightedEdge{f: 3, t: 4, w: 3}, weightedEdge{f: 4, t: 5, w: 4}}},
{edges: []graph.WeightedEdge{weightedEdge{f: 5, t: 4, w: 4}, weightedEdge{f: 4, t: 3, w: 3}, weightedEdge{f: 3, t: 2, w: 2}, weightedEdge{f: 2, t: 1, w: 1}}},
}
func TestOrderedWeightedEdgesIterate(t *testing.T) {
for _, test := range orderedWeightedEdgesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedWeightedEdges(test.edges)
if it.Len() != len(test.edges) {
t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.edges))
}
var got []graph.WeightedEdge
for it.Next() {
got = append(got, it.WeightedEdge())
}
want := test.edges
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
func TestOrderedWeightedEdgesSlice(t *testing.T) {
for _, test := range orderedWeightedEdgesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedWeightedEdges(test.edges)
got := it.WeightedEdgeSlice()
want := test.edges
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}

131
graph/iterator/lines.go Normal file
View File

@@ -0,0 +1,131 @@
// Copyright ©2018 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 iterator
import "gonum.org/v1/gonum/graph"
// OrderedLines implements the graph.Lines and graph.LineSlicer interfaces.
// The iteration order of OrderedLines is the order of lines passed to
// NewLineIterator.
type OrderedLines struct {
idx int
lines []graph.Line
}
// NewOrderedLines returns an OrderedLines initialized with the provided lines.
func NewOrderedLines(lines []graph.Line) *OrderedLines {
return &OrderedLines{idx: -1, lines: lines}
}
// Len returns the remaining number of lines to be iterated over.
func (e *OrderedLines) Len() int {
if e.idx >= len(e.lines) {
return 0
}
if e.idx <= 0 {
return len(e.lines)
}
return len(e.lines[e.idx:])
}
// Next returns whether the next call of Line will return a valid line.
func (e *OrderedLines) Next() bool {
if uint(e.idx)+1 < uint(len(e.lines)) {
e.idx++
return true
}
e.idx = len(e.lines)
return false
}
// Line returns the current line of the iterator. Next must have been
// called prior to a call to Line.
func (e *OrderedLines) Line() graph.Line {
if e.idx >= len(e.lines) || e.idx < 0 {
return nil
}
return e.lines[e.idx]
}
// LineSlice returns all the remaining lines in the iterator and advances
// the iterator.
func (e *OrderedLines) LineSlice() []graph.Line {
if e.idx >= len(e.lines) {
return nil
}
idx := e.idx
if idx == -1 {
idx = 0
}
e.idx = len(e.lines)
return e.lines[idx:]
}
// Reset returns the iterator to its initial state.
func (e *OrderedLines) Reset() {
e.idx = -1
}
// OrderedWeightedLines implements the graph.Lines and graph.LineSlicer interfaces.
// The iteration order of OrderedWeightedLines is the order of lines passed to
// NewLineIterator.
type OrderedWeightedLines struct {
idx int
lines []graph.WeightedLine
}
// NewWeightedLineIterator returns an OrderedWeightedLines initialized with the provided lines.
func NewOrderedWeightedLines(lines []graph.WeightedLine) *OrderedWeightedLines {
return &OrderedWeightedLines{idx: -1, lines: lines}
}
// Len returns the remaining number of lines to be iterated over.
func (e *OrderedWeightedLines) Len() int {
if e.idx >= len(e.lines) {
return 0
}
if e.idx <= 0 {
return len(e.lines)
}
return len(e.lines[e.idx:])
}
// Next returns whether the next call of WeightedLine will return a valid line.
func (e *OrderedWeightedLines) Next() bool {
if uint(e.idx)+1 < uint(len(e.lines)) {
e.idx++
return true
}
e.idx = len(e.lines)
return false
}
// WeightedLine returns the current line of the iterator. Next must have been
// called prior to a call to WeightedLine.
func (e *OrderedWeightedLines) WeightedLine() graph.WeightedLine {
if e.idx >= len(e.lines) || e.idx < 0 {
return nil
}
return e.lines[e.idx]
}
// WeightedLineSlice returns all the remaining lines in the iterator and advances
// the iterator.
func (e *OrderedWeightedLines) WeightedLineSlice() []graph.WeightedLine {
if e.idx >= len(e.lines) {
return nil
}
idx := e.idx
if idx == -1 {
idx = 0
}
e.idx = len(e.lines)
return e.lines[idx:]
}
// Reset returns the iterator to its initial state.
func (e *OrderedWeightedLines) Reset() {
e.idx = -1
}

View File

@@ -0,0 +1,116 @@
// Copyright ©2018 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 iterator_test
import (
"reflect"
"testing"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple"
)
type line struct{ f, t int }
func (l line) From() graph.Node { return simple.Node(l.f) }
func (l line) To() graph.Node { return simple.Node(l.t) }
func (l line) ID() int64 { return 1 }
var orderedLinesTests = []struct {
lines []graph.Line
}{
{lines: nil},
{lines: []graph.Line{line{f: 1, t: 2}}},
{lines: []graph.Line{line{f: 1, t: 2}, line{f: 2, t: 3}, line{f: 3, t: 4}, line{f: 4, t: 5}}},
{lines: []graph.Line{line{f: 5, t: 4}, line{f: 4, t: 3}, line{f: 3, t: 2}, line{f: 2, t: 1}}},
}
func TestOrderedLinesIterate(t *testing.T) {
for _, test := range orderedLinesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedLines(test.lines)
if it.Len() != len(test.lines) {
t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.lines))
}
var got []graph.Line
for it.Next() {
got = append(got, it.Line())
}
want := test.lines
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
func TestOrderedLinesSlice(t *testing.T) {
for _, test := range orderedLinesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedLines(test.lines)
got := it.LineSlice()
want := test.lines
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
type weightedLine struct {
f, t int
w float64
}
func (l weightedLine) From() graph.Node { return simple.Node(l.f) }
func (l weightedLine) To() graph.Node { return simple.Node(l.t) }
func (l weightedLine) Weight() float64 { return l.w }
func (l weightedLine) ID() int64 { return 1 }
var orderedWeightedLinesTests = []struct {
lines []graph.WeightedLine
}{
{lines: nil},
{lines: []graph.WeightedLine{weightedLine{f: 1, t: 2, w: 1}}},
{lines: []graph.WeightedLine{weightedLine{f: 1, t: 2, w: 1}, weightedLine{f: 2, t: 3, w: 2}, weightedLine{f: 3, t: 4, w: 3}, weightedLine{f: 4, t: 5, w: 4}}},
{lines: []graph.WeightedLine{weightedLine{f: 5, t: 4, w: 4}, weightedLine{f: 4, t: 3, w: 3}, weightedLine{f: 3, t: 2, w: 2}, weightedLine{f: 2, t: 1, w: 1}}},
}
func TestOrderedWeightedLinesIterate(t *testing.T) {
for _, test := range orderedWeightedLinesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedWeightedLines(test.lines)
if it.Len() != len(test.lines) {
t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.lines))
}
var got []graph.WeightedLine
for it.Next() {
got = append(got, it.WeightedLine())
}
want := test.lines
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
func TestOrderedWeightedLinesSlice(t *testing.T) {
for _, test := range orderedWeightedLinesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedWeightedLines(test.lines)
got := it.WeightedLineSlice()
want := test.lines
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}

115
graph/iterator/nodes.go Normal file
View File

@@ -0,0 +1,115 @@
// Copyright ©2018 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 iterator
import "gonum.org/v1/gonum/graph"
// OrderedNodes implements the graph.Nodes and graph.NodeSlicer interfaces.
// The iteration order of OrderedNodes is the order of nodes passed to
// NewNodeIterator.
type OrderedNodes struct {
idx int
nodes []graph.Node
}
// NewOrderedNodes returns a OrderedNodes initialized with the provided nodes.
func NewOrderedNodes(nodes []graph.Node) *OrderedNodes {
return &OrderedNodes{idx: -1, nodes: nodes}
}
// Len returns the remaining number of nodes to be iterated over.
func (n *OrderedNodes) Len() int {
if n.idx >= len(n.nodes) {
return 0
}
if n.idx <= 0 {
return len(n.nodes)
}
return len(n.nodes[n.idx:])
}
// Next returns whether the next call of Node will return a valid node.
func (n *OrderedNodes) Next() bool {
if uint(n.idx)+1 < uint(len(n.nodes)) {
n.idx++
return true
}
n.idx = len(n.nodes)
return false
}
// Node returns the current node of the iterator. Next must have been
// called prior to a call to Node.
func (n *OrderedNodes) Node() graph.Node {
if n.idx >= len(n.nodes) || n.idx < 0 {
return nil
}
return n.nodes[n.idx]
}
// NodeSlice returns all the remaining nodes in the iterator and advances
// the iterator.
func (n *OrderedNodes) NodeSlice() []graph.Node {
if n.idx >= len(n.nodes) {
return nil
}
idx := n.idx
if idx == -1 {
idx = 0
}
n.idx = len(n.nodes)
return n.nodes[idx:]
}
// Reset returns the iterator to its initial state.
func (n *OrderedNodes) Reset() {
n.idx = -1
}
// ImplicitNodes implements the graph.Nodes interface for a set of nodes over
// a contiguous ID range.
type ImplicitNodes struct {
beg, end int
curr int
newNode func(id int) graph.Node
}
// NewImplicitNodes returns a new implicit node iterator spanning nodes in [beg,end).
// The provided new func maps the id to a graph.Node. NewImplicitNodes will panic
// if beg is greater than end.
func NewImplicitNodes(beg, end int, new func(id int) graph.Node) *ImplicitNodes {
if beg > end {
panic("iterator: invalid range")
}
return &ImplicitNodes{beg: beg, end: end, curr: beg - 1, newNode: new}
}
// Len returns the remaining number of nodes to be iterated over.
func (n *ImplicitNodes) Len() int {
return n.end - n.curr - 1
}
// Next returns whether the next call of Node will return a valid node.
func (n *ImplicitNodes) Next() bool {
if n.curr == n.end {
return false
}
n.curr++
return n.curr < n.end
}
// Node returns the current node of the iterator. Next must have been
// called prior to a call to Node.
func (n *ImplicitNodes) Node() graph.Node {
if n.Len() == -1 || n.curr < n.beg {
return nil
}
return n.newNode(n.curr)
}
// Reset returns the iterator to its initial state.
func (n *ImplicitNodes) Reset() {
n.curr = n.beg - 1
}

View File

@@ -0,0 +1,99 @@
// Copyright ©2018 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 iterator_test
import (
"reflect"
"testing"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple"
)
var orderedNodesTests = []struct {
nodes []graph.Node
}{
{nodes: nil},
{nodes: []graph.Node{simple.Node(1)}},
{nodes: []graph.Node{simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(5)}},
{nodes: []graph.Node{simple.Node(5), simple.Node(3), simple.Node(2), simple.Node(1)}},
}
func TestOrderedNodesIterate(t *testing.T) {
for _, test := range orderedNodesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedNodes(test.nodes)
if it.Len() != len(test.nodes) {
t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.nodes))
}
var got []graph.Node
for it.Next() {
got = append(got, it.Node())
}
want := test.nodes
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
func TestOrderedNodesSlice(t *testing.T) {
for _, test := range orderedNodesTests {
for i := 0; i < 2; i++ {
it := iterator.NewOrderedNodes(test.nodes)
got := it.NodeSlice()
want := test.nodes
if !reflect.DeepEqual(got, want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
}
it.Reset()
}
}
}
var implicitNodesTests = []struct {
beg, end int
new func(int) graph.Node
want []graph.Node
}{
{
beg: 1, end: 1,
want: nil,
},
{
beg: 1, end: 2,
new: newSimpleNode,
want: []graph.Node{simple.Node(1)},
},
{
beg: 1, end: 5,
new: newSimpleNode,
want: []graph.Node{simple.Node(1), simple.Node(2), simple.Node(3), simple.Node(4)},
},
}
func newSimpleNode(id int) graph.Node { return simple.Node(id) }
func TestImplicitNodesIterate(t *testing.T) {
for _, test := range implicitNodesTests {
for i := 0; i < 2; i++ {
it := iterator.NewImplicitNodes(test.beg, test.end, test.new)
if it.Len() != len(test.want) {
t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.want))
}
var got []graph.Node
for it.Next() {
got = append(got, it.Node())
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, test.want)
}
it.Reset()
}
}
}

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
var ( var (
@@ -154,7 +155,7 @@ func (g *DirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *DirectedGraph) Nodes() []graph.Node { func (g *DirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -164,12 +165,12 @@ func (g *DirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. Each edge in the returned slice // Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.Edge. // is a multi.Edge.
func (g *DirectedGraph) Edges() []graph.Edge { func (g *DirectedGraph) Edges() graph.Edges {
var edges []graph.Edge var edges []graph.Edge
for _, u := range g.nodes { for _, u := range g.nodes {
for _, e := range g.from[u.ID()] { for _, e := range g.from[u.ID()] {
@@ -182,11 +183,11 @@ func (g *DirectedGraph) Edges() []graph.Edge {
} }
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *DirectedGraph) From(id int64) []graph.Node { func (g *DirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -197,11 +198,11 @@ func (g *DirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid] from[i] = g.nodes[vid]
i++ i++
} }
return from return iterator.NewOrderedNodes(from)
} }
// To returns all nodes in g that can reach directly to n. // To returns all nodes in g that can reach directly to n.
func (g *DirectedGraph) To(id int64) []graph.Node { func (g *DirectedGraph) To(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -212,7 +213,7 @@ func (g *DirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid] to[i] = g.nodes[uid]
i++ i++
} }
return to return iterator.NewOrderedNodes(to)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y without // HasEdgeBetween returns whether an edge exists between nodes x and y without
@@ -229,7 +230,7 @@ func (g *DirectedGraph) HasEdgeBetween(xid, yid int64) bool {
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
// The returned graph.Edge is a multi.Edge if an edge exists. // The returned graph.Edge is a multi.Edge if an edge exists.
func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge { func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge {
lines := g.Lines(uid, vid) lines := graph.LinesOf(g.Lines(uid, vid))
if len(lines) == 0 { if len(lines) == 0 {
return nil return nil
} }
@@ -238,7 +239,7 @@ func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge {
// Lines returns the lines from u to v if such any such lines exists and nil otherwise. // Lines returns the lines from u to v if such any such lines exists and nil otherwise.
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
func (g *DirectedGraph) Lines(uid, vid int64) []graph.Line { func (g *DirectedGraph) Lines(uid, vid int64) graph.Lines {
edge := g.from[uid][vid] edge := g.from[uid][vid]
if len(edge) == 0 { if len(edge) == 0 {
return nil return nil
@@ -247,7 +248,7 @@ func (g *DirectedGraph) Lines(uid, vid int64) []graph.Line {
for _, l := range edge { for _, l := range edge {
lines = append(lines, l) lines = append(lines, l)
} }
return lines return iterator.NewOrderedLines(lines)
} }
// HasEdgeFromTo returns whether an edge exists in the graph from u to v. // HasEdgeFromTo returns whether an edge exists in the graph from u to v.
@@ -255,18 +256,3 @@ func (g *DirectedGraph) HasEdgeFromTo(uid, vid int64) bool {
_, ok := g.from[uid][vid] _, ok := g.from[uid][vid]
return ok return ok
} }
// Degree returns the in+out degree of n in g.
func (g *DirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
var deg int
for _, e := range g.from[id] {
deg += len(e)
}
for _, e := range g.to[id] {
deg += len(e)
}
return deg
}

View File

@@ -21,7 +21,7 @@ var (
func TestEdgeOvercounting(t *testing.T) { func TestEdgeOvercounting(t *testing.T) {
g := generateDummyGraph() g := generateDummyGraph()
if neigh := g.From(int64(2)); len(neigh) != 2 { if neigh := graph.NodesOf(g.From(int64(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)) t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
} }
} }

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
var ( var (
@@ -143,7 +144,7 @@ func (g *UndirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *UndirectedGraph) Nodes() []graph.Node { func (g *UndirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -153,12 +154,12 @@ func (g *UndirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. Each edge in the returned slice // Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.Edge. // is a multi.Edge.
func (g *UndirectedGraph) Edges() []graph.Edge { func (g *UndirectedGraph) Edges() graph.Edges {
if len(g.lines) == 0 { if len(g.lines) == 0 {
return nil return nil
} }
@@ -180,11 +181,11 @@ func (g *UndirectedGraph) Edges() []graph.Edge {
} }
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *UndirectedGraph) From(id int64) []graph.Node { func (g *UndirectedGraph) From(id int64) graph.Nodes {
if !g.Has(id) { if !g.Has(id) {
return nil return nil
} }
@@ -195,7 +196,7 @@ func (g *UndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from] nodes[i] = g.nodes[from]
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -213,7 +214,7 @@ func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
// The returned graph.Edge is a multi.Edge if an edge exists. // The returned graph.Edge is a multi.Edge if an edge exists.
func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge { func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge {
lines := g.LinesBetween(uid, vid) lines := graph.LinesOf(g.LinesBetween(uid, vid))
if len(lines) == 0 { if len(lines) == 0 {
return nil return nil
} }
@@ -222,27 +223,15 @@ func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge {
// Lines returns the lines from u to v if such an edge exists and nil otherwise. // Lines returns the lines from u to v if such an edge exists and nil otherwise.
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
func (g *UndirectedGraph) Lines(uid, vid int64) []graph.Line { func (g *UndirectedGraph) Lines(uid, vid int64) graph.Lines {
return g.LinesBetween(uid, vid) return g.LinesBetween(uid, vid)
} }
// LinesBetween returns the lines between nodes x and y. // LinesBetween returns the lines between nodes x and y.
func (g *UndirectedGraph) LinesBetween(xid, yid int64) []graph.Line { func (g *UndirectedGraph) LinesBetween(xid, yid int64) graph.Lines {
var lines []graph.Line var lines []graph.Line
for _, l := range g.lines[xid][yid] { for _, l := range g.lines[xid][yid] {
lines = append(lines, l) lines = append(lines, l)
} }
return lines return iterator.NewOrderedLines(lines)
}
// Degree returns the degree of n in g.
func (g *UndirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
var deg int
for _, e := range g.lines[id] {
deg += len(e)
}
return deg
} }

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
var ( var (
@@ -160,7 +161,7 @@ func (g *WeightedDirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *WeightedDirectedGraph) Nodes() []graph.Node { func (g *WeightedDirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -171,12 +172,12 @@ func (g *WeightedDirectedGraph) Nodes() []graph.Node {
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. Each edge in the returned slice // Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.WeightedEdge. // is a multi.WeightedEdge.
func (g *WeightedDirectedGraph) Edges() []graph.Edge { func (g *WeightedDirectedGraph) Edges() graph.Edges {
var edges []graph.Edge var edges []graph.Edge
for _, u := range g.nodes { for _, u := range g.nodes {
for _, e := range g.from[u.ID()] { for _, e := range g.from[u.ID()] {
@@ -190,12 +191,12 @@ func (g *WeightedDirectedGraph) Edges() []graph.Edge {
} }
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// WeightedEdges returns all the edges in the graph. Each edge in the returned slice // WeightedEdges returns all the edges in the graph. Each edge in the returned slice
// is a multi.WeightedEdge. // is a multi.WeightedEdge.
func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge { func (g *WeightedDirectedGraph) WeightedEdges() graph.WeightedEdges {
var edges []graph.WeightedEdge var edges []graph.WeightedEdge
for _, u := range g.nodes { for _, u := range g.nodes {
for _, e := range g.from[u.ID()] { for _, e := range g.from[u.ID()] {
@@ -209,11 +210,11 @@ func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge {
} }
} }
} }
return edges return iterator.NewOrderedWeightedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *WeightedDirectedGraph) From(id int64) []graph.Node { func (g *WeightedDirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -224,11 +225,11 @@ func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid] from[i] = g.nodes[vid]
i++ i++
} }
return from return iterator.NewOrderedNodes(from)
} }
// To returns all nodes in g that can reach directly to n. // To returns all nodes in g that can reach directly to n.
func (g *WeightedDirectedGraph) To(id int64) []graph.Node { func (g *WeightedDirectedGraph) To(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -239,7 +240,7 @@ func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid] to[i] = g.nodes[uid]
i++ i++
} }
return to return iterator.NewOrderedNodes(to)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y without // HasEdgeBetween returns whether an edge exists between nodes x and y without
@@ -271,7 +272,7 @@ func (g *WeightedDirectedGraph) Edge(uid, vid int64) graph.Edge {
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
// The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists. // The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
lines := g.WeightedLines(uid, vid) lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
if len(lines) == 0 { if len(lines) == 0 {
return nil return nil
} }
@@ -280,7 +281,7 @@ func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge
// Lines returns the lines from u to v if such any such lines exists and nil otherwise. // Lines returns the lines from u to v if such any such lines exists and nil otherwise.
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
func (g *WeightedDirectedGraph) Lines(uid, vid int64) []graph.Line { func (g *WeightedDirectedGraph) Lines(uid, vid int64) graph.Lines {
edge := g.from[uid][vid] edge := g.from[uid][vid]
if len(edge) == 0 { if len(edge) == 0 {
return nil return nil
@@ -289,12 +290,12 @@ func (g *WeightedDirectedGraph) Lines(uid, vid int64) []graph.Line {
for _, l := range edge { for _, l := range edge {
lines = append(lines, l) lines = append(lines, l)
} }
return lines return iterator.NewOrderedLines(lines)
} }
// WeightedLines returns the weighted lines from u to v if such any such lines exists // WeightedLines returns the weighted lines from u to v if such any such lines exists
// and nil otherwise. The node v must be directly reachable from u as defined by the From method. // and nil otherwise. The node v must be directly reachable from u as defined by the From method.
func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLine { func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) graph.WeightedLines {
edge := g.from[uid][vid] edge := g.from[uid][vid]
if len(edge) == 0 { if len(edge) == 0 {
return nil return nil
@@ -303,27 +304,12 @@ func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLi
for _, l := range edge { for _, l := range edge {
lines = append(lines, l) lines = append(lines, l)
} }
return lines return iterator.NewOrderedWeightedLines(lines)
} }
// Weight returns the weight for the lines between x and y summarised by the receiver's // Weight returns the weight for the lines between x and y summarised by the receiver's
// EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise. // EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
func (g *WeightedDirectedGraph) Weight(uid, vid int64) (w float64, ok bool) { func (g *WeightedDirectedGraph) Weight(uid, vid int64) (w float64, ok bool) {
lines := g.WeightedLines(uid, vid) lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0 return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0
} }
// Degree returns the in+out degree of n in g.
func (g *WeightedDirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
var deg int
for _, e := range g.from[id] {
deg += len(e)
}
for _, e := range g.to[id] {
deg += len(e)
}
return deg
}

View File

@@ -22,7 +22,7 @@ var (
func TestWeightedEdgeOvercounting(t *testing.T) { func TestWeightedEdgeOvercounting(t *testing.T) {
g := generateDummyWeightedGraph() g := generateDummyWeightedGraph()
if neigh := g.From(int64(2)); len(neigh) != 2 { if neigh := graph.NodesOf(g.From(int64(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)) t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
} }
} }

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
var ( var (
@@ -147,7 +148,7 @@ func (g *WeightedUndirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *WeightedUndirectedGraph) Nodes() []graph.Node { func (g *WeightedUndirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -157,12 +158,12 @@ func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. Each edge in the returned slice // Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.Edge. // is a multi.Edge.
func (g *WeightedUndirectedGraph) Edges() []graph.Edge { func (g *WeightedUndirectedGraph) Edges() graph.Edges {
if len(g.lines) == 0 { if len(g.lines) == 0 {
return nil return nil
} }
@@ -184,11 +185,11 @@ func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
} }
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *WeightedUndirectedGraph) From(id int64) []graph.Node { func (g *WeightedUndirectedGraph) From(id int64) graph.Nodes {
if !g.Has(id) { if !g.Has(id) {
return nil return nil
} }
@@ -199,7 +200,7 @@ func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from] nodes[i] = g.nodes[from]
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -210,12 +211,12 @@ func (g *WeightedUndirectedGraph) HasEdgeBetween(xid, yid int64) bool {
// Lines returns the lines from u to v if such an edge exists and nil otherwise. // Lines returns the lines from u to v if such an edge exists and nil otherwise.
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
func (g *WeightedUndirectedGraph) Lines(uid, vid int64) []graph.Line { func (g *WeightedUndirectedGraph) Lines(uid, vid int64) graph.Lines {
return g.LinesBetween(uid, vid) return g.LinesBetween(uid, vid)
} }
// LinesBetween returns the lines between nodes x and y. // LinesBetween returns the lines between nodes x and y.
func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) []graph.Line { func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) graph.Lines {
edge := g.lines[xid][yid] edge := g.lines[xid][yid]
if len(edge) == 0 { if len(edge) == 0 {
return nil return nil
@@ -230,7 +231,7 @@ func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) []graph.Line {
seen[lid] = struct{}{} seen[lid] = struct{}{}
lines = append(lines, l) lines = append(lines, l)
} }
return lines return iterator.NewOrderedLines(lines)
} }
// Edge returns the edge from u to v if such an edge exists and nil otherwise. // Edge returns the edge from u to v if such an edge exists and nil otherwise.
@@ -249,7 +250,7 @@ func (g *WeightedUndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
// The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists. // The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
lines := g.WeightedLines(uid, vid) lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
if len(lines) == 0 { if len(lines) == 0 {
return nil return nil
} }
@@ -263,12 +264,12 @@ func (g *WeightedUndirectedGraph) WeightedEdgeBetween(xid, yid int64) graph.Weig
// WeightedLines returns the lines from u to v if such an edge exists and nil otherwise. // WeightedLines returns the lines from u to v if such an edge exists and nil otherwise.
// The node v must be directly reachable from u as defined by the From method. // The node v must be directly reachable from u as defined by the From method.
func (g *WeightedUndirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLine { func (g *WeightedUndirectedGraph) WeightedLines(uid, vid int64) graph.WeightedLines {
return g.WeightedLinesBetween(uid, vid) return g.WeightedLinesBetween(uid, vid)
} }
// WeightedLinesBetween returns the lines between nodes x and y. // WeightedLinesBetween returns the lines between nodes x and y.
func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) []graph.WeightedLine { func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) graph.WeightedLines {
edge := g.lines[xid][yid] edge := g.lines[xid][yid]
if len(edge) == 0 { if len(edge) == 0 {
return nil return nil
@@ -283,24 +284,12 @@ func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) []graph.W
seen[lid] = struct{}{} seen[lid] = struct{}{}
lines = append(lines, l) lines = append(lines, l)
} }
return lines return iterator.NewOrderedWeightedLines(lines)
} }
// Weight returns the weight for the lines between x and y summarised by the receiver's // Weight returns the weight for the lines between x and y summarised by the receiver's
// EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise. // EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) { func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
lines := g.WeightedLines(xid, yid) lines := graph.WeightedLinesOf(g.WeightedLines(xid, yid))
return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0 return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0
} }
// Degree returns the degree of n in g.
func (g *WeightedUndirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
var deg int
for _, e := range g.lines[id] {
deg += len(e)
}
return deg
}

View File

@@ -24,11 +24,11 @@ type Multigraph interface {
Has(id int64) bool Has(id int64) bool
// Nodes returns all the nodes in the multigraph. // Nodes returns all the nodes in the multigraph.
Nodes() []Node Nodes() Nodes
// From returns all nodes that can be reached directly // From returns all nodes that can be reached directly
// from the node with the given ID. // from the node with the given ID.
From(id int64) []Node From(id int64) Nodes
// HasEdgeBetween returns whether an edge exists between // HasEdgeBetween returns whether an edge exists between
// nodes with IDs xid and yid without considering direction. // nodes with IDs xid and yid without considering direction.
@@ -38,7 +38,7 @@ type Multigraph interface {
// vid, if any such lines exist and nil otherwise. The // vid, if any such lines exist and nil otherwise. The
// node v must be directly reachable from u as defined by // node v must be directly reachable from u as defined by
// the From method. // the From method.
Lines(uid, vid int64) []Line Lines(uid, vid int64) Lines
} }
// WeightedMultigraph is a weighted multigraph. // WeightedMultigraph is a weighted multigraph.
@@ -49,7 +49,7 @@ type WeightedMultigraph interface {
// with IDs uid and vid if any such lines exist and nil // with IDs uid and vid if any such lines exist and nil
// otherwise. The node v must be directly reachable // otherwise. The node v must be directly reachable
// from u as defined by the From method. // from u as defined by the From method.
WeightedLines(uid, vid int64) []WeightedLine WeightedLines(uid, vid int64) WeightedLines
} }
// UndirectedMultigraph is an undirected multigraph. // UndirectedMultigraph is an undirected multigraph.
@@ -58,7 +58,7 @@ type UndirectedMultigraph interface {
// LinesBetween returns the lines between nodes x and y // LinesBetween returns the lines between nodes x and y
// with IDs xid and yid. // with IDs xid and yid.
LinesBetween(xid, yid int64) []Line LinesBetween(xid, yid int64) Lines
} }
// WeightedUndirectedMultigraph is a weighted undirected multigraph. // WeightedUndirectedMultigraph is a weighted undirected multigraph.
@@ -67,7 +67,7 @@ type WeightedUndirectedMultigraph interface {
// WeightedLinesBetween returns the lines between nodes // WeightedLinesBetween returns the lines between nodes
// x and y with IDs xid and yid. // x and y with IDs xid and yid.
WeightedLinesBetween(xid, yid int64) []WeightedLine WeightedLinesBetween(xid, yid int64) WeightedLines
} }
// DirectedMultigraph is a directed multigraph. // DirectedMultigraph is a directed multigraph.
@@ -81,7 +81,7 @@ type DirectedMultigraph interface {
// To returns all nodes that can reach directly // To returns all nodes that can reach directly
// to the node with the given ID. // to the node with the given ID.
To(id int64) []Node To(id int64) Nodes
} }
// WeightedDirectedMultigraph is a weighted directed multigraph. // WeightedDirectedMultigraph is a weighted directed multigraph.
@@ -95,7 +95,7 @@ type WeightedDirectedMultigraph interface {
// To returns all nodes that can reach directly // To returns all nodes that can reach directly
// to the node with the given ID. // to the node with the given ID.
To(id int64) []Node To(id int64) Nodes
} }
// LineAdder is an interface for adding lines to a multigraph. // LineAdder is an interface for adding lines to a multigraph.

View File

@@ -90,7 +90,7 @@ func EdgeBetweenness(g graph.Graph) map[[2]int64]float64 {
// the accumulation loop provided by the accumulate closure. // the accumulation loop provided by the accumulate closure.
func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack, p map[int64][]graph.Node, delta, sigma map[int64]float64)) { func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack, p map[int64][]graph.Node, delta, sigma map[int64]float64)) {
var ( var (
nodes = g.Nodes() nodes = graph.NodesOf(g.Nodes())
stack linear.NodeStack stack linear.NodeStack
p = make(map[int64][]graph.Node, len(nodes)) p = make(map[int64][]graph.Node, len(nodes))
sigma = make(map[int64]float64, len(nodes)) sigma = make(map[int64]float64, len(nodes))
@@ -117,7 +117,7 @@ func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack
v := queue.Dequeue() v := queue.Dequeue()
vid := v.ID() vid := v.ID()
stack.Push(v) stack.Push(v)
for _, w := range g.From(vid) { for _, w := range graph.NodesOf(g.From(vid)) {
wid := w.ID() wid := w.ID()
// w found for the first time? // w found for the first time?
if d[wid] < 0 { if d[wid] < 0 {
@@ -151,7 +151,7 @@ func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack
func BetweennessWeighted(g graph.Weighted, p path.AllShortest) map[int64]float64 { func BetweennessWeighted(g graph.Weighted, p path.AllShortest) map[int64]float64 {
cb := make(map[int64]float64) cb := make(map[int64]float64)
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
for i, s := range nodes { for i, s := range nodes {
sid := s.ID() sid := s.ID()
for j, t := range nodes { for j, t := range nodes {
@@ -205,7 +205,7 @@ func EdgeBetweennessWeighted(g graph.Weighted, p path.AllShortest) map[[2]int64]
cb := make(map[[2]int64]float64) cb := make(map[[2]int64]float64)
_, isUndirected := g.(graph.Undirected) _, isUndirected := g.(graph.Undirected)
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
for i, s := range nodes { for i, s := range nodes {
sid := s.ID() sid := s.ID()
for j, t := range nodes { for j, t := range nodes {

View File

@@ -113,7 +113,7 @@ type Laplacian struct {
// degree of each node and A is the graph adjacency matrix of the input graph. // degree of each node and A is the graph adjacency matrix of the input graph.
// If g contains self edges, NewLaplacian will panic. // If g contains self edges, NewLaplacian will panic.
func NewLaplacian(g graph.Undirected) Laplacian { func NewLaplacian(g graph.Undirected) Laplacian {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
id := n.ID() id := n.ID()
@@ -123,7 +123,7 @@ func NewLaplacian(g graph.Undirected) Laplacian {
l := mat.NewSymDense(len(nodes), nil) l := mat.NewSymDense(len(nodes), nil)
for j, u := range nodes { for j, u := range nodes {
uid := u.ID() uid := u.ID()
to := g.From(uid) to := graph.NodesOf(g.From(uid))
l.SetSym(j, j, float64(len(to))) l.SetSym(j, j, float64(len(to)))
for _, v := range to { for _, v := range to {
vid := v.ID() vid := v.ID()
@@ -146,7 +146,7 @@ func NewLaplacian(g graph.Undirected) Laplacian {
// matrix of the input graph. // matrix of the input graph.
// If g contains self edges, NewSymNormLaplacian will panic. // If g contains self edges, NewSymNormLaplacian will panic.
func NewSymNormLaplacian(g graph.Undirected) Laplacian { func NewSymNormLaplacian(g graph.Undirected) Laplacian {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
id := n.ID() id := n.ID()
@@ -156,7 +156,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
l := mat.NewSymDense(len(nodes), nil) l := mat.NewSymDense(len(nodes), nil)
for j, u := range nodes { for j, u := range nodes {
uid := u.ID() uid := u.ID()
to := g.From(uid) to := graph.NodesOf(g.From(uid))
if len(to) == 0 { if len(to) == 0 {
continue continue
} }
@@ -168,7 +168,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
panic("network: self edge in graph") panic("network: self edge in graph")
} }
if uid < vid { if uid < vid {
l.SetSym(indexOf[vid], j, -1/(squdeg*math.Sqrt(float64(len(g.From(vid)))))) l.SetSym(indexOf[vid], j, -1/(squdeg*math.Sqrt(float64(g.From(vid).Len()))))
} }
} }
} }
@@ -183,7 +183,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
// graph. // graph.
// If g contains self edges, NewRandomWalkLaplacian will panic. // If g contains self edges, NewRandomWalkLaplacian will panic.
func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian { func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
id := n.ID() id := n.ID()
@@ -193,7 +193,7 @@ func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
l := mat.NewDense(len(nodes), len(nodes), nil) l := mat.NewDense(len(nodes), len(nodes), nil)
for j, u := range nodes { for j, u := range nodes {
uid := u.ID() uid := u.ID()
to := g.From(uid) to := graph.NodesOf(g.From(uid))
if len(to) == 0 { if len(to) == 0 {
continue continue
} }

View File

@@ -12,6 +12,7 @@ import (
"gonum.org/v1/gonum/floats" "gonum.org/v1/gonum/floats"
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/simple"
"gonum.org/v1/gonum/mat" "gonum.org/v1/gonum/mat"
) )
@@ -304,10 +305,10 @@ type sortedNodeGraph struct {
graph.Graph graph.Graph
} }
func (g sortedNodeGraph) Nodes() []graph.Node { func (g sortedNodeGraph) Nodes() graph.Nodes {
n := g.Graph.Nodes() n := graph.NodesOf(g.Graph.Nodes())
sort.Sort(ordered.ByID(n)) sort.Sort(ordered.ByID(n))
return n return iterator.NewOrderedNodes(n)
} }
var diffuseToEquilibriumTests = []struct { var diffuseToEquilibriumTests = []struct {

View File

@@ -19,7 +19,7 @@ import (
// For directed graphs the incoming paths are used. Infinite distances are // For directed graphs the incoming paths are used. Infinite distances are
// not considered. // not considered.
func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 { func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
c := make(map[int64]float64, len(nodes)) c := make(map[int64]float64, len(nodes))
for _, u := range nodes { for _, u := range nodes {
uid := u.ID() uid := u.ID()
@@ -48,7 +48,7 @@ func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 {
// For directed graphs the incoming paths are used. Infinite distances are // For directed graphs the incoming paths are used. Infinite distances are
// not considered. // not considered.
func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 { func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
f := make(map[int64]float64, len(nodes)) f := make(map[int64]float64, len(nodes))
for _, u := range nodes { for _, u := range nodes {
uid := u.ID() uid := u.ID()
@@ -77,7 +77,7 @@ func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 {
// For directed graphs the incoming paths are used. Infinite distances are // For directed graphs the incoming paths are used. Infinite distances are
// not considered. // not considered.
func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 { func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
h := make(map[int64]float64, len(nodes)) h := make(map[int64]float64, len(nodes))
for i, u := range nodes { for i, u := range nodes {
uid := u.ID() uid := u.ID()
@@ -108,7 +108,7 @@ func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 {
// For directed graphs the incoming paths are used. Infinite distances are // For directed graphs the incoming paths are used. Infinite distances are
// not considered. // not considered.
func Residual(g graph.Graph, p path.AllShortest) map[int64]float64 { func Residual(g graph.Graph, p path.AllShortest) map[int64]float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
r := make(map[int64]float64, len(nodes)) r := make(map[int64]float64, len(nodes))
for i, u := range nodes { for i, u := range nodes {
uid := u.ID() uid := u.ID()

View File

@@ -22,7 +22,7 @@ type HubAuthority struct {
// vector difference between iterations is below tol. The returned map is // vector difference between iterations is below tol. The returned map is
// keyed on the graph node IDs. // keyed on the graph node IDs.
func HITS(g graph.Directed, tol float64) map[int64]HubAuthority { func HITS(g graph.Directed, tol float64) map[int64]HubAuthority {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// Make a topological copy of g with dense node IDs. // Make a topological copy of g with dense node IDs.
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
@@ -33,10 +33,10 @@ func HITS(g graph.Directed, tol float64) map[int64]HubAuthority {
nodesLinkedFrom := make([][]int, len(nodes)) nodesLinkedFrom := make([][]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
id := n.ID() id := n.ID()
for _, u := range g.To(id) { for _, u := range graph.NodesOf(g.To(id)) {
nodesLinkingTo[i] = append(nodesLinkingTo[i], indexOf[u.ID()]) nodesLinkingTo[i] = append(nodesLinkingTo[i], indexOf[u.ID()])
} }
for _, v := range g.From(id) { for _, v := range graph.NodesOf(g.From(id)) {
nodesLinkedFrom[i] = append(nodesLinkedFrom[i], indexOf[v.ID()]) nodesLinkedFrom[i] = append(nodesLinkedFrom[i], indexOf[v.ID()])
} }
} }

View File

@@ -51,7 +51,7 @@ func edgeWeightedPageRank(g graph.WeightedDirected, damp, tol float64) map[int64
// //
// http://www.ams.org/samplings/feature-column/fcarc-pagerank // http://www.ams.org/samplings/feature-column/fcarc-pagerank
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
indexOf[n.ID()] = i indexOf[n.ID()] = i
@@ -60,7 +60,7 @@ func edgeWeightedPageRank(g graph.WeightedDirected, damp, tol float64) map[int64
m := mat.NewDense(len(nodes), len(nodes), nil) m := mat.NewDense(len(nodes), len(nodes), nil)
dangling := damp / float64(len(nodes)) dangling := damp / float64(len(nodes))
for j, u := range nodes { for j, u := range nodes {
to := g.From(u.ID()) to := graph.NodesOf(g.From(u.ID()))
var z float64 var z float64
for _, v := range to { for _, v := range to {
if w, ok := g.Weight(u.ID(), v.ID()); ok { if w, ok := g.Weight(u.ID(), v.ID()); ok {
@@ -134,7 +134,7 @@ func edgeWeightedPageRankSparse(g graph.WeightedDirected, damp, tol float64) map
// //
// http://www.ams.org/samplings/feature-column/fcarc-pagerank // http://www.ams.org/samplings/feature-column/fcarc-pagerank
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
indexOf[n.ID()] = i indexOf[n.ID()] = i
@@ -144,7 +144,7 @@ func edgeWeightedPageRankSparse(g graph.WeightedDirected, damp, tol float64) map
var dangling compressedRow var dangling compressedRow
df := damp / float64(len(nodes)) df := damp / float64(len(nodes))
for j, u := range nodes { for j, u := range nodes {
to := g.From(u.ID()) to := graph.NodesOf(g.From(u.ID()))
var z float64 var z float64
for _, v := range to { for _, v := range to {
if w, ok := g.Weight(u.ID(), v.ID()); ok { if w, ok := g.Weight(u.ID(), v.ID()); ok {
@@ -215,7 +215,7 @@ func pageRank(g graph.Directed, damp, tol float64) map[int64]float64 {
// //
// http://www.ams.org/samplings/feature-column/fcarc-pagerank // http://www.ams.org/samplings/feature-column/fcarc-pagerank
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
indexOf[n.ID()] = i indexOf[n.ID()] = i
@@ -224,7 +224,7 @@ func pageRank(g graph.Directed, damp, tol float64) map[int64]float64 {
m := mat.NewDense(len(nodes), len(nodes), nil) m := mat.NewDense(len(nodes), len(nodes), nil)
dangling := damp / float64(len(nodes)) dangling := damp / float64(len(nodes))
for j, u := range nodes { for j, u := range nodes {
to := g.From(u.ID()) to := graph.NodesOf(g.From(u.ID()))
f := damp / float64(len(to)) f := damp / float64(len(to))
for _, v := range to { for _, v := range to {
m.Set(indexOf[v.ID()], j, f) m.Set(indexOf[v.ID()], j, f)
@@ -288,7 +288,7 @@ func pageRankSparse(g graph.Directed, damp, tol float64) map[int64]float64 {
// //
// http://www.ams.org/samplings/feature-column/fcarc-pagerank // http://www.ams.org/samplings/feature-column/fcarc-pagerank
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes)) indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes { for i, n := range nodes {
indexOf[n.ID()] = i indexOf[n.ID()] = i
@@ -298,7 +298,7 @@ func pageRankSparse(g graph.Directed, damp, tol float64) map[int64]float64 {
var dangling compressedRow var dangling compressedRow
df := damp / float64(len(nodes)) df := damp / float64(len(nodes))
for j, u := range nodes { for j, u := range nodes {
to := g.From(u.ID()) to := graph.NodesOf(g.From(u.ID()))
f := damp / float64(len(to)) f := damp / float64(len(to))
for _, v := range to { for _, v := range to {
m.addTo(indexOf[v.ID()], j, f) m.addTo(indexOf[v.ID()], j, f)

209
graph/nodes_edges.go Normal file
View File

@@ -0,0 +1,209 @@
// Copyright ©2018 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
// Iterator is an item iterator.
type Iterator interface {
// Next advances the iterator and returns whether
// the next call to the item method will return a
// non-nil item.
//
// Next should be called prior to any call to the
// iterator's item retrieval method after the
// iterator has been obtained or reset.
//
// The order of iteration is implementation
// dependent.
Next() bool
// Len returns the number of items remaining in the
// iterator.
Len() int
// Reset returns the iterator to its start position.
Reset()
}
// Nodes is a Node iterator.
type Nodes interface {
Iterator
// Node returns the current Node from the iterator.
Node() Node
}
// NodeSlicer wraps the NodeSlice method.
type NodeSlicer interface {
// NodeSlice returns the set of nodes remaining
// to be iterated by a Nodes iterator.
// The holder of the iterator may arbitrarily
// change elements in the returned slice, but
// those changes may be reflected to other
// iterators.
NodeSlice() []Node
}
// NodesOf returns it.Len() nodes from it. If it is a NodeSlicer, the NodeSlice method
// is used to obtain the nodes. It is safe to pass a nil Nodes to NodesOf.
func NodesOf(it Nodes) []Node {
if it == nil {
return nil
}
switch it := it.(type) {
case NodeSlicer:
return it.NodeSlice()
}
n := make([]Node, 0, it.Len())
for it.Next() {
n = append(n, it.Node())
}
return n
}
// Edges is an Edge iterator.
type Edges interface {
Iterator
// Edge returns the current Edge from the iterator.
Edge() Edge
}
// EdgeSlicer wraps the EdgeSlice method.
type EdgeSlicer interface {
// EdgeSlice returns the set of edges remaining
// to be iterated by an Edges iterator.
// The holder of the iterator may arbitrarily
// change elements in the returned slice, but
// those changes may be reflected to other
// iterators.
EdgeSlice() []Edge
}
// EdgesOf returns it.Len() nodes from it. If it is an EdgeSlicer, the EdgeSlice method is used
// to obtain the edges. It is safe to pass a nil Edges to EdgesOf.
func EdgesOf(it Edges) []Edge {
if it == nil {
return nil
}
switch it := it.(type) {
case EdgeSlicer:
return it.EdgeSlice()
}
n := make([]Edge, 0, it.Len())
for it.Next() {
n = append(n, it.Edge())
}
return n
}
// WeightedEdges is a WeightedEdge iterator.
type WeightedEdges interface {
Iterator
// Edge returns the current Edge from the iterator.
WeightedEdge() WeightedEdge
}
// WeightedEdgeSlicer wraps the WeightedEdgeSlice method.
type WeightedEdgeSlicer interface {
// EdgeSlice returns the set of edges remaining
// to be iterated by an Edges iterator.
// The holder of the iterator may arbitrarily
// change elements in the returned slice, but
// those changes may be reflected to other
// iterators.
WeightedEdgeSlice() []WeightedEdge
}
// WeightedEdgesOf returns it.Len() weighted edge from it. If it is a WeightedEdgeSlicer, the
// WeightedEdgeSlice method is used to obtain the edges. It is safe to pass a nil WeightedEdges
// to WeightedEdgesOf.
func WeightedEdgesOf(it WeightedEdges) []WeightedEdge {
if it == nil {
return nil
}
switch it := it.(type) {
case WeightedEdgeSlicer:
return it.WeightedEdgeSlice()
}
n := make([]WeightedEdge, 0, it.Len())
for it.Next() {
n = append(n, it.WeightedEdge())
}
return n
}
// Lines is a Line iterator.
type Lines interface {
Iterator
// Line returns the current Line from the iterator.
Line() Line
}
// LineSlicer wraps the LineSlice method.
type LineSlicer interface {
// LineSlice returns the set of lines remaining
// to be iterated by an Lines iterator.
// The holder of the iterator may arbitrarily
// change elements in the returned slice, but
// those changes may be reflected to other
// iterators.
LineSlice() []Line
}
// LinesOf returns it.Len() nodes from it. If it is a LineSlicer, the LineSlice method is used
// to obtain the lines. It is safe to pass a nil Lines to LinesOf.
func LinesOf(it Lines) []Line {
if it == nil {
return nil
}
switch it := it.(type) {
case LineSlicer:
return it.LineSlice()
}
n := make([]Line, 0, it.Len())
for it.Next() {
n = append(n, it.Line())
}
return n
}
// WeightedLines is a WeightedLine iterator.
type WeightedLines interface {
Iterator
// Line returns the current Line from the iterator.
WeightedLine() WeightedLine
}
// WeightedLineSlicer wraps the WeightedLineSlice method.
type WeightedLineSlicer interface {
// LineSlice returns the set of lines remaining
// to be iterated by an Lines iterator.
// The holder of the iterator may arbitrarily
// change elements in the returned slice, but
// those changes may be reflected to other
// iterators.
WeightedLineSlice() []WeightedLine
}
// WeightedLinesOf returns it.Len() weighted line from it. If it is a WeightedLineSlicer, the
// WeightedLineSlice method is used to obtain the lines. It is safe to pass a nil WeightedLines
// to WeightedLinesOf.
func WeightedLinesOf(it WeightedLines) []WeightedLine {
if it == nil {
return nil
}
switch it := it.(type) {
case WeightedLineSlicer:
return it.WeightedLineSlice()
}
n := make([]WeightedLine, 0, it.Len())
for it.Next() {
n = append(n, it.WeightedLine())
}
return n
}

View File

@@ -41,7 +41,7 @@ func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded
} }
} }
path = newShortestFrom(s, g.Nodes()) path = newShortestFrom(s, graph.NodesOf(g.Nodes()))
tid := t.ID() tid := t.ID()
visited := make(set.Int64s) visited := make(set.Int64s)
@@ -59,7 +59,7 @@ func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded
} }
visited.Add(uid) visited.Add(uid)
for _, v := range g.From(u.node.ID()) { for _, v := range graph.NodesOf(g.From(u.node.ID())) {
vid := v.ID() vid := v.ID()
if visited.Has(vid) { if visited.Has(vid) {
continue continue

View File

@@ -192,8 +192,8 @@ func TestExhaustiveAStar(t *testing.T) {
} }
ps := DijkstraAllPaths(g) ps := DijkstraAllPaths(g)
for _, start := range g.Nodes() { for _, start := range graph.NodesOf(g.Nodes()) {
for _, goal := range g.Nodes() { for _, goal := range graph.NodesOf(g.Nodes()) {
pt, _ := AStar(start, goal, g, heuristic) pt, _ := AStar(start, goal, g, heuristic)
gotPath, gotWeight := pt.To(goal.ID()) gotPath, gotWeight := pt.To(goal.ID())
wantPath, wantWeight, _ := ps.Between(start.ID(), goal.ID()) wantPath, wantWeight, _ := ps.Between(start.ID(), goal.ID())
@@ -226,8 +226,8 @@ func (e weightedEdge) To() graph.Node { return e.to }
func (e weightedEdge) Weight() float64 { return e.cost } func (e weightedEdge) Weight() float64 { return e.cost }
func isMonotonic(g UndirectedWeightLister, h Heuristic) (ok bool, at graph.Edge, goal graph.Node) { func isMonotonic(g UndirectedWeightLister, h Heuristic) (ok bool, at graph.Edge, goal graph.Node) {
for _, goal := range g.Nodes() { for _, goal := range graph.NodesOf(g.Nodes()) {
for _, edge := range g.WeightedEdges() { for _, edge := range graph.WeightedEdgesOf(g.WeightedEdges()) {
from := edge.From() from := edge.From()
to := edge.To() to := edge.To()
w, ok := g.Weight(from.ID(), to.ID()) w, ok := g.Weight(from.ID(), to.ID())

View File

@@ -22,7 +22,7 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
weight = UniformCost(g) weight = UniformCost(g)
} }
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
path = newShortestFrom(u, nodes) path = newShortestFrom(u, nodes)
path.dist[path.indexOf[u.ID()]] = 0 path.dist[path.indexOf[u.ID()]] = 0
@@ -33,7 +33,7 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
changed := false changed := false
for j, u := range nodes { for j, u := range nodes {
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID() vid := v.ID()
k := path.indexOf[vid] k := path.indexOf[vid]
w, ok := weight(uid, vid) w, ok := weight(uid, vid)
@@ -54,7 +54,7 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
for j, u := range nodes { for j, u := range nodes {
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID() vid := v.ID()
k := path.indexOf[vid] k := path.indexOf[vid]
w, ok := weight(uid, vid) w, ok := weight(uid, vid)

View File

@@ -19,6 +19,7 @@ import (
"gonum.org/v1/gonum/graph/encoding" "gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/encoding/dot" "gonum.org/v1/gonum/graph/encoding/dot"
"gonum.org/v1/gonum/graph/graphs/gen" "gonum.org/v1/gonum/graph/graphs/gen"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/simple"
"gonum.org/v1/gonum/graph/topo" "gonum.org/v1/gonum/graph/topo"
) )
@@ -240,7 +241,7 @@ func duplication(n int, delta, alpha, sigma float64) func() *simple.DirectedGrap
if err != nil { if err != nil {
panic(err) panic(err)
} }
for _, e := range g.Edges() { for _, e := range graph.EdgesOf(g.Edges()) {
if rnd.Intn(2) == 0 { if rnd.Intn(2) == 0 {
g.RemoveEdge(e.From().ID(), e.To().ID()) g.RemoveEdge(e.From().ID(), e.To().ID())
} }
@@ -253,8 +254,10 @@ type undirected struct {
*simple.DirectedGraph *simple.DirectedGraph
} }
func (g undirected) From(id int64) []graph.Node { func (g undirected) From(id int64) graph.Nodes {
return append(g.DirectedGraph.From(id), g.DirectedGraph.To(id)...) return iterator.NewOrderedNodes(append(
graph.NodesOf(g.DirectedGraph.From(id)),
graph.NodesOf(g.DirectedGraph.To(id))...))
} }
func (g undirected) HasEdgeBetween(xid, yid int64) bool { func (g undirected) HasEdgeBetween(xid, yid int64) bool {

View File

@@ -140,7 +140,7 @@ func (lt *lengauerTarjan) dfs(g graph.Directed, v graph.Node) {
ltv.label = ltv ltv.label = ltv
lt.nodes = append(lt.nodes, ltv) lt.nodes = append(lt.nodes, ltv)
for _, w := range g.From(v.ID()) { for _, w := range graph.NodesOf(g.From(v.ID())) {
wid := w.ID() wid := w.ID()
idx, ok := lt.indexOf[wid] idx, ok := lt.indexOf[wid]

View File

@@ -162,7 +162,7 @@ func (lt *sLengauerTarjan) dfs(g graph.Directed, v graph.Node) {
ltv.label = ltv ltv.label = ltv
lt.nodes = append(lt.nodes, ltv) lt.nodes = append(lt.nodes, ltv)
for _, w := range g.From(v.ID()) { for _, w := range graph.NodesOf(g.From(v.ID())) {
wid := w.ID() wid := w.ID()
idx, ok := lt.indexOf[wid] idx, ok := lt.indexOf[wid]

View File

@@ -25,7 +25,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
if !h.Has(u.ID()) { if !h.Has(u.ID()) {
return Shortest{from: u} return Shortest{from: u}
} }
path = newShortestFrom(u, h.Nodes()) path = newShortestFrom(u, graph.NodesOf(h.Nodes()))
} else { } else {
if g.From(u.ID()) == nil { if g.From(u.ID()) == nil {
return Shortest{from: u} return Shortest{from: u}
@@ -58,7 +58,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
continue continue
} }
mnid := mid.node.ID() mnid := mid.node.ID()
for _, v := range g.From(mnid) { for _, v := range graph.NodesOf(g.From(mnid)) {
vid := v.ID() vid := v.ID()
j, ok := path.indexOf[vid] j, ok := path.indexOf[vid]
if !ok { if !ok {
@@ -88,7 +88,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
// //
// The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|). // The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|).
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) { func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
paths = newAllShortest(g.Nodes(), false) paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
dijkstraAllPaths(g, paths) dijkstraAllPaths(g, paths)
return paths return paths
} }
@@ -123,7 +123,7 @@ func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
paths.dist.Set(i, k, mid.dist) paths.dist.Set(i, k, mid.dist)
} }
mnid := mid.node.ID() mnid := mid.node.ID()
for _, v := range g.From(mnid) { for _, v := range graph.NodesOf(g.From(mnid)) {
vid := v.ID() vid := v.ID()
j := paths.indexOf[vid] j := paths.indexOf[vid]
w, ok := weight(mnid, vid) w, ok := weight(mnid, vid)

View File

@@ -88,7 +88,7 @@ func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel
d.queue.insert(d.t, key{d.heuristic(s, t), 0}) d.queue.insert(d.t, key{d.heuristic(s, t), 0})
for _, n := range g.Nodes() { for _, n := range graph.NodesOf(g.Nodes()) {
switch n.ID() { switch n.ID() {
case d.s.ID(): case d.s.ID():
d.model.AddNode(d.s) d.model.AddNode(d.s)
@@ -98,9 +98,9 @@ func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel
d.model.AddNode(newDStarLiteNode(n)) d.model.AddNode(newDStarLiteNode(n))
} }
} }
for _, u := range d.model.Nodes() { for _, u := range graph.NodesOf(d.model.Nodes()) {
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID() vid := v.ID()
w := edgeWeight(d.weight, uid, vid) w := edgeWeight(d.weight, uid, vid)
if w < 0 { if w < 0 {
@@ -196,7 +196,7 @@ func (d *DStarLite) findShortestPath() {
case u.g > u.rhs: case u.g > u.rhs:
u.g = u.rhs u.g = u.rhs
d.queue.remove(u) d.queue.remove(u)
for _, _s := range d.model.To(uid) { for _, _s := range graph.NodesOf(d.model.To(uid)) {
s := _s.(*dStarLiteNode) s := _s.(*dStarLiteNode)
sid := s.ID() sid := s.ID()
if sid != d.t.ID() { if sid != d.t.ID() {
@@ -207,13 +207,13 @@ func (d *DStarLite) findShortestPath() {
default: default:
gOld := u.g gOld := u.g
u.g = math.Inf(1) u.g = math.Inf(1)
for _, _s := range append(d.model.To(uid), u) { for _, _s := range append(graph.NodesOf(d.model.To(uid)), u) {
s := _s.(*dStarLiteNode) s := _s.(*dStarLiteNode)
sid := s.ID() sid := s.ID()
if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld { if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld {
if s.ID() != d.t.ID() { if s.ID() != d.t.ID() {
s.rhs = math.Inf(1) s.rhs = math.Inf(1)
for _, t := range d.model.From(sid) { for _, t := range graph.NodesOf(d.model.From(sid)) {
tid := t.ID() tid := t.ID()
s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, tid)+t.(*dStarLiteNode).g) s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, tid)+t.(*dStarLiteNode).g)
} }
@@ -250,7 +250,7 @@ func (d *DStarLite) Step() bool {
var next *dStarLiteNode var next *dStarLiteNode
dsid := d.s.ID() dsid := d.s.ID()
for _, _s := range d.model.From(dsid) { for _, _s := range graph.NodesOf(d.model.From(dsid)) {
s := _s.(*dStarLiteNode) s := _s.(*dStarLiteNode)
w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g
if w < min || (w == min && s.rhs < rhs) { if w < min || (w == min && s.rhs < rhs) {
@@ -320,7 +320,7 @@ func (d *DStarLite) UpdateWorld(changes []graph.Edge) {
} else if u.rhs == cOld+v.g { } else if u.rhs == cOld+v.g {
if uid != d.t.ID() { if uid != d.t.ID() {
u.rhs = math.Inf(1) u.rhs = math.Inf(1)
for _, t := range d.model.From(uid) { for _, t := range graph.NodesOf(d.model.From(uid)) {
u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, uid, t.ID())+t.(*dStarLiteNode).g) u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, uid, t.ID())+t.(*dStarLiteNode).g)
} }
} }
@@ -365,7 +365,7 @@ func (d *DStarLite) Path() (p []graph.Node, weight float64) {
cost float64 cost float64
) )
uid := u.ID() uid := u.ID()
for _, _v := range d.model.From(uid) { for _, _v := range graph.NodesOf(d.model.From(uid)) {
v := _v.(*dStarLiteNode) v := _v.(*dStarLiteNode)
vid := v.ID() vid := v.ID()
w := edgeWeight(d.model.Weight, uid, vid) w := edgeWeight(d.model.Weight, uid, vid)

View File

@@ -387,7 +387,7 @@ var dynamicDStarLiteTests = []struct {
modify: func(l *testgraphs.LimitedVisionGrid) { modify: func(l *testgraphs.LimitedVisionGrid) {
all := l.Grid.AllVisible all := l.Grid.AllVisible
l.Grid.AllVisible = false l.Grid.AllVisible = false
for _, n := range l.Nodes() { for _, n := range graph.NodesOf(l.Nodes()) {
id := n.ID() id := n.ID()
l.Known[id] = !l.Grid.Has(id) l.Known[id] = !l.Grid.Has(id)
} }
@@ -400,9 +400,9 @@ var dynamicDStarLiteTests = []struct {
l.Known[l.NodeAt(wallRow, wallCol).ID()] = false l.Known[l.NodeAt(wallRow, wallCol).ID()] = false
// Check we have a correctly modified representation. // Check we have a correctly modified representation.
for _, u := range l.Nodes() { for _, u := range graph.NodesOf(l.Nodes()) {
uid := u.ID() uid := u.ID()
for _, v := range l.Nodes() { for _, v := range graph.NodesOf(l.Nodes()) {
vid := v.ID() vid := v.ID()
if l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) { if l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) {
ur, uc := l.RowCol(uid) ur, uc := l.RowCol(uid)
@@ -598,7 +598,7 @@ func TestDStarLiteDynamic(t *testing.T) {
} }
dp.dump(true) dp.dump(true)
dp.printEdges("Initial world knowledge: %s\n\n", simpleWeightedEdgesOf(l, world.Edges())) dp.printEdges("Initial world knowledge: %s\n\n", simpleWeightedEdgesOf(l, graph.EdgesOf(world.Edges())))
for d.Step() { for d.Step() {
changes, _ := l.MoveTo(d.Here()) changes, _ := l.MoveTo(d.Here())
got = append(got, l.Location) got = append(got, l.Location)

View File

@@ -19,13 +19,14 @@ func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) {
weight = UniformCost(g) weight = UniformCost(g)
} }
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
paths = newAllShortest(nodes, true) paths = newAllShortest(nodes, true)
for i, u := range nodes { for i, u := range nodes {
paths.dist.Set(i, i, 0) paths.dist.Set(i, i, 0)
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { to := g.From(uid)
vid := v.ID() for to.Next() {
vid := to.Node().ID()
j := paths.indexOf[vid] j := paths.indexOf[vid]
w, ok := weight(uid, vid) w, ok := weight(uid, vid)
if !ok { if !ok {

View File

@@ -10,6 +10,7 @@ import (
"math" "math"
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/simple"
) )
@@ -91,14 +92,14 @@ func NewGridFrom(rows ...string) *Grid {
// Nodes returns all the open nodes in the grid if AllVisible is // Nodes returns all the open nodes in the grid if AllVisible is
// false, otherwise all nodes are returned. // false, otherwise all nodes are returned.
func (g *Grid) Nodes() []graph.Node { func (g *Grid) Nodes() graph.Nodes {
var nodes []graph.Node var nodes []graph.Node
for id, ok := range g.open { for id, ok := range g.open {
if ok || g.AllVisible { if ok || g.AllVisible {
nodes = append(nodes, simple.Node(id)) nodes = append(nodes, simple.Node(id))
} }
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Has returns whether n is a node in the grid. The state of // Has returns whether n is a node in the grid. The state of
@@ -158,7 +159,7 @@ func (g *Grid) NodeAt(r, c int) graph.Node {
// From returns all the nodes reachable from u. Reachabilty requires that both // From returns all the nodes reachable from u. Reachabilty requires that both
// ends of an edge must be open. // ends of an edge must be open.
func (g *Grid) From(uid int64) []graph.Node { func (g *Grid) From(uid int64) graph.Nodes {
if !g.HasOpen(uid) { if !g.HasOpen(uid) {
return nil return nil
} }
@@ -171,7 +172,7 @@ func (g *Grid) From(uid int64) []graph.Node {
} }
} }
} }
return to return iterator.NewOrderedNodes(to)
} }
// HasEdgeBetween returns whether there is an edge between u and v. // HasEdgeBetween returns whether there is an edge between u and v.

View File

@@ -249,7 +249,7 @@ func TestGrid(t *testing.T) {
} }
for _, test := range reach { for _, test := range reach {
g.AllowDiagonal = test.diagonal g.AllowDiagonal = test.diagonal
got := g.From(test.from.ID()) got := graph.NodesOf(g.From(test.from.ID()))
if !reflect.DeepEqual(got, test.to) { if !reflect.DeepEqual(got, test.to) {
t.Fatalf("unexpected nodes from %d with allow diagonal=%t:\ngot: %v\nwant:%v", t.Fatalf("unexpected nodes from %d with allow diagonal=%t:\ngot: %v\nwant:%v",
test.from, test.diagonal, got, test.to) test.from, test.diagonal, got, test.to)

View File

@@ -9,6 +9,7 @@ import (
"math" "math"
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/graph/simple"
) )
@@ -147,12 +148,12 @@ func (l *LimitedVisionGrid) XY(id int64) (x, y float64) {
} }
// Nodes returns all the nodes in the grid. // Nodes returns all the nodes in the grid.
func (l *LimitedVisionGrid) Nodes() []graph.Node { func (l *LimitedVisionGrid) Nodes() graph.Nodes {
nodes := make([]graph.Node, 0, len(l.Grid.open)) nodes := make([]graph.Node, 0, len(l.Grid.open))
for id := range l.Grid.open { for id := range l.Grid.open {
nodes = append(nodes, simple.Node(id)) nodes = append(nodes, simple.Node(id))
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// NodeAt returns the node at (r, c). The returned node may be open or closed. // NodeAt returns the node at (r, c). The returned node may be open or closed.
@@ -166,7 +167,7 @@ func (l *LimitedVisionGrid) Has(id int64) bool {
} }
// From returns nodes that are optimistically reachable from u. // From returns nodes that are optimistically reachable from u.
func (l *LimitedVisionGrid) From(uid int64) []graph.Node { func (l *LimitedVisionGrid) From(uid int64) graph.Nodes {
if !l.Has(uid) { if !l.Has(uid) {
return nil return nil
} }
@@ -180,7 +181,7 @@ func (l *LimitedVisionGrid) From(uid int64) []graph.Node {
} }
} }
} }
return to return iterator.NewOrderedNodes(to)
} }
// HasEdgeBetween optimistically returns whether an edge is exists between u and v. // HasEdgeBetween optimistically returns whether an edge is exists between u and v.

View File

@@ -1166,11 +1166,11 @@ func TestLimitedVisionGrid(t *testing.T) {
l.Grid.AllowDiagonal = test.diag l.Grid.AllowDiagonal = test.diag
x, y := l.XY(test.path[0].ID()) x, y := l.XY(test.path[0].ID())
for _, u := range l.Nodes() { for _, u := range graph.NodesOf(l.Nodes()) {
uid := u.ID() uid := u.ID()
ux, uy := l.XY(uid) ux, uy := l.XY(uid)
uNear := math.Hypot(x-ux, y-uy) <= test.radius uNear := math.Hypot(x-ux, y-uy) <= test.radius
for _, v := range l.Nodes() { for _, v := range graph.NodesOf(l.Nodes()) {
vid := v.ID() vid := v.ID()
vx, vy := l.XY(vid) vx, vy := l.XY(vid)
vNear := math.Hypot(x-vx, y-vy) <= test.radius vNear := math.Hypot(x-vx, y-vy) <= test.radius

View File

@@ -29,7 +29,7 @@ func JohnsonAllPaths(g graph.Graph) (paths AllShortest, ok bool) {
jg.weight = UniformCost(g) jg.weight = UniformCost(g)
} }
paths = newAllShortest(g.Nodes(), false) paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
sign := int64(-1) sign := int64(-1)
for { for {
@@ -69,7 +69,7 @@ type johnsonWeightAdjuster struct {
q int64 q int64
g graph.Graph g graph.Graph
from func(id int64) []graph.Node from func(id int64) graph.Nodes
edgeTo func(uid, vid int64) graph.Edge edgeTo func(uid, vid int64) graph.Edge
weight Weighting weight Weighting
@@ -93,14 +93,14 @@ func (g johnsonWeightAdjuster) Has(id int64) bool {
panic("path: unintended use of johnsonWeightAdjuster") panic("path: unintended use of johnsonWeightAdjuster")
} }
func (g johnsonWeightAdjuster) Nodes() []graph.Node { func (g johnsonWeightAdjuster) Nodes() graph.Nodes {
if g.bellmanFord { if g.bellmanFord {
return append(g.g.Nodes(), johnsonGraphNode(g.q)) return newJohnsonNodeIterator(g.q, g.g.Nodes())
} }
return g.g.Nodes() return g.g.Nodes()
} }
func (g johnsonWeightAdjuster) From(id int64) []graph.Node { func (g johnsonWeightAdjuster) From(id int64) graph.Nodes {
if g.bellmanFord && id == g.q { if g.bellmanFord && id == g.q {
return g.g.Nodes() return g.g.Nodes()
} }
@@ -140,3 +140,59 @@ func (johnsonWeightAdjuster) HasEdgeBetween(_, _ int64) bool {
type johnsonGraphNode int64 type johnsonGraphNode int64
func (n johnsonGraphNode) ID() int64 { return int64(n) } func (n johnsonGraphNode) ID() int64 { return int64(n) }
func newJohnsonNodeIterator(q int64, nodes graph.Nodes) *johnsonNodeIterator {
return &johnsonNodeIterator{q: q, nodes: nodes}
}
type johnsonNodeIterator struct {
q int64
nodes graph.Nodes
qUsed, qOK bool
}
func (it *johnsonNodeIterator) Len() int {
var len int
if it.nodes != nil {
len = it.nodes.Len()
}
if !it.qUsed {
len++
}
return len
}
func (it *johnsonNodeIterator) Next() bool {
if it.nodes != nil {
ok := it.nodes.Next()
if ok {
return true
}
}
if !it.qUsed {
it.qOK = true
it.qUsed = true
return true
}
it.qOK = false
return false
}
func (it *johnsonNodeIterator) Node() graph.Node {
if it.qOK {
return johnsonGraphNode(it.q)
}
if it.nodes == nil {
return nil
}
return it.nodes.Node()
}
func (it *johnsonNodeIterator) Reset() {
it.qOK = false
it.qUsed = false
if it.nodes == nil {
return
}
it.nodes.Reset()
}

View File

@@ -36,8 +36,9 @@ func ExampleBellmanFordFrom_negativecycles() {
} }
// Add a zero-cost path to all nodes from a new node Q. // Add a zero-cost path to all nodes from a new node Q.
for _, n := range g.Nodes() { nodes := g.Nodes()
g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node('Q'), T: n}) for nodes.Next() {
g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node('Q'), T: nodes.Node()})
} }
// Find the shortest path to each node from Q. // Find the shortest path to each node from Q.

View File

@@ -32,7 +32,7 @@ type WeightedBuilder interface {
// //
// If dst has nodes that exist in g, Prim will panic. // If dst has nodes that exist in g, Prim will panic.
func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 { func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
if len(nodes) == 0 { if len(nodes) == 0 {
return 0 return 0
} }
@@ -49,7 +49,7 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
u := nodes[0] u := nodes[0]
uid := u.ID() uid := u.ID()
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
w, ok := g.Weight(uid, v.ID()) w, ok := g.Weight(uid, v.ID())
if !ok { if !ok {
panic("prim: unexpected invalid weight") panic("prim: unexpected invalid weight")
@@ -67,7 +67,7 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
u = e.From() u = e.From()
uid := u.ID() uid := u.ID()
for _, n := range g.From(uid) { for _, n := range graph.NodesOf(g.From(uid)) {
if key, ok := q.key(n); ok { if key, ok := q.key(n); ok {
w, ok := g.Weight(uid, n.ID()) w, ok := g.Weight(uid, n.ID())
if !ok { if !ok {
@@ -146,7 +146,7 @@ func (q *primQueue) update(u, v graph.Node, key float64) {
// the set of edges in the graph. // the set of edges in the graph.
type UndirectedWeightLister interface { type UndirectedWeightLister interface {
graph.WeightedUndirected graph.WeightedUndirected
WeightedEdges() []graph.WeightedEdge WeightedEdges() graph.WeightedEdges
} }
// Kruskal generates a minimum spanning tree of g by greedy tree coalescence, placing // Kruskal generates a minimum spanning tree of g by greedy tree coalescence, placing
@@ -162,11 +162,11 @@ type UndirectedWeightLister interface {
// //
// If dst has nodes that exist in g, Kruskal will panic. // If dst has nodes that exist in g, Kruskal will panic.
func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 { func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 {
edges := g.WeightedEdges() edges := graph.WeightedEdgesOf(g.WeightedEdges())
sort.Sort(byWeight(edges)) sort.Sort(byWeight(edges))
ds := newDisjointSet() ds := newDisjointSet()
for _, node := range g.Nodes() { for _, node := range graph.NodesOf(g.Nodes()) {
dst.AddNode(node) dst.AddNode(node)
ds.makeSet(node.ID()) ds.makeSet(node.ID())
} }

View File

@@ -28,7 +28,7 @@ func init() {
type spanningGraph interface { type spanningGraph interface {
graph.WeightedBuilder graph.WeightedBuilder
graph.WeightedUndirected graph.WeightedUndirected
WeightedEdges() []graph.WeightedEdge WeightedEdges() graph.WeightedEdges
} }
var spanningTreeTests = []struct { var spanningTreeTests = []struct {
@@ -254,7 +254,7 @@ func testMinumumSpanning(mst func(dst WeightedBuilder, g spanningGraph) float64,
test.name, w, test.want) test.name, w, test.want)
} }
var got float64 var got float64
for _, e := range dst.WeightedEdges() { for _, e := range graph.WeightedEdgesOf(dst.WeightedEdges()) {
got += e.Weight() got += e.Weight()
} }
if got != test.want { if got != test.want {
@@ -262,7 +262,7 @@ func testMinumumSpanning(mst func(dst WeightedBuilder, g spanningGraph) float64,
test.name, got, test.want) test.name, got, test.want)
} }
gotEdges := dst.Edges() gotEdges := graph.EdgesOf(dst.Edges())
if len(gotEdges) != len(test.treeEdges) { if len(gotEdges) != len(test.treeEdges) {
t.Errorf("unexpected number of spanning tree edges for %q: got: %d want: %d", t.Errorf("unexpected number of spanning tree edges for %q: got: %d want: %d",
test.name, len(gotEdges), len(test.treeEdges)) test.name, len(gotEdges), len(test.treeEdges))

View File

@@ -8,6 +8,7 @@ import (
"sort" "sort"
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
) )
// YenKShortestPaths returns the k-shortest loopless paths from s to t in g. // YenKShortestPaths returns the k-shortest loopless paths from s to t in g.
@@ -116,8 +117,8 @@ type yenKSPAdjuster struct {
visitedEdges map[[2]int64]struct{} visitedEdges map[[2]int64]struct{}
} }
func (g yenKSPAdjuster) From(id int64) []graph.Node { func (g yenKSPAdjuster) From(id int64) graph.Nodes {
nodes := g.Graph.From(id) nodes := graph.NodesOf(g.Graph.From(id))
for i := 0; i < len(nodes); { for i := 0; i < len(nodes); {
if g.canWalk(id, nodes[i].ID()) { if g.canWalk(id, nodes[i].ID()) {
i++ i++
@@ -126,7 +127,7 @@ func (g yenKSPAdjuster) From(id int64) []graph.Node {
nodes[i] = nodes[len(nodes)-1] nodes[i] = nodes[len(nodes)-1]
nodes = nodes[:len(nodes)-1] nodes = nodes[:len(nodes)-1]
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
func (g yenKSPAdjuster) canWalk(u, v int64) bool { func (g yenKSPAdjuster) canWalk(u, v int64) bool {

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/mat" "gonum.org/v1/gonum/mat"
) )
@@ -85,22 +86,18 @@ func (g *DirectedMatrix) has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *DirectedMatrix) Nodes() []graph.Node { func (g *DirectedMatrix) Nodes() graph.Nodes {
if g.nodes != nil { if g.nodes != nil {
nodes := make([]graph.Node, len(g.nodes)) nodes := make([]graph.Node, len(g.nodes))
copy(nodes, g.nodes) copy(nodes, g.nodes)
return nodes return iterator.NewOrderedNodes(nodes)
} }
r, _ := g.mat.Dims() r, _ := g.mat.Dims()
nodes := make([]graph.Node, r) return iterator.NewImplicitNodes(0, r, newSimpleNode)
for i := 0; i < r; i++ {
nodes[i] = Node(i)
}
return nodes
} }
// Edges returns all the edges in the graph. // Edges returns all the edges in the graph.
func (g *DirectedMatrix) Edges() []graph.Edge { func (g *DirectedMatrix) Edges() graph.Edges {
var edges []graph.Edge var edges []graph.Edge
r, _ := g.mat.Dims() r, _ := g.mat.Dims()
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
@@ -113,15 +110,15 @@ func (g *DirectedMatrix) Edges() []graph.Edge {
} }
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *DirectedMatrix) From(id int64) []graph.Node { func (g *DirectedMatrix) From(id int64) graph.Nodes {
if !g.has(id) { if !g.has(id) {
return nil return nil
} }
var neighbors []graph.Node var nodes []graph.Node
_, c := g.mat.Dims() _, c := g.mat.Dims()
for j := 0; j < c; j++ { for j := 0; j < c; j++ {
if int64(j) == id { if int64(j) == id {
@@ -129,18 +126,18 @@ func (g *DirectedMatrix) From(id int64) []graph.Node {
} }
// id is not greater than maximum int by this point. // id is not greater than maximum int by this point.
if !isSame(g.mat.At(int(id), j), g.absent) { if !isSame(g.mat.At(int(id), j), g.absent) {
neighbors = append(neighbors, g.Node(int64(j))) nodes = append(nodes, g.Node(int64(j)))
} }
} }
return neighbors return iterator.NewOrderedNodes(nodes)
} }
// To returns all nodes in g that can reach directly to n. // To returns all nodes in g that can reach directly to n.
func (g *DirectedMatrix) To(id int64) []graph.Node { func (g *DirectedMatrix) To(id int64) graph.Nodes {
if !g.has(id) { if !g.has(id) {
return nil return nil
} }
var neighbors []graph.Node var nodes []graph.Node
r, _ := g.mat.Dims() r, _ := g.mat.Dims()
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
if int64(i) == id { if int64(i) == id {
@@ -148,10 +145,10 @@ func (g *DirectedMatrix) To(id int64) []graph.Node {
} }
// id is not greater than maximum int by this point. // id is not greater than maximum int by this point.
if !isSame(g.mat.At(i, int(id)), g.absent) { if !isSame(g.mat.At(i, int(id)), g.absent) {
neighbors = append(neighbors, g.Node(int64(i))) nodes = append(nodes, g.Node(int64(i)))
} }
} }
return neighbors return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y without // HasEdgeBetween returns whether an edge exists between nodes x and y without
@@ -251,34 +248,6 @@ func (g *DirectedMatrix) RemoveEdge(fid, tid int64) {
g.mat.Set(int(fid), int(tid), g.absent) g.mat.Set(int(fid), int(tid), g.absent)
} }
// Degree returns the in+out degree of n in g.
func (g *DirectedMatrix) Degree(id int64) int {
if !g.has(id) {
return 0
}
var deg int
r, c := g.mat.Dims()
for i := 0; i < r; i++ {
if int64(i) == id {
continue
}
// id is not greater than maximum int by this point.
if !isSame(g.mat.At(int(id), i), g.absent) {
deg++
}
}
for i := 0; i < c; i++ {
if int64(i) == id {
continue
}
// id is not greater than maximum int by this point.
if !isSame(g.mat.At(i, int(id)), g.absent) {
deg++
}
}
return deg
}
// Matrix returns the mat.Matrix representation of the graph. The orientation // Matrix returns the mat.Matrix representation of the graph. The orientation
// of the matrix is such that the matrix entry at G_{ij} is the weight of the edge // of the matrix is such that the matrix entry at G_{ij} is the weight of the edge
// from node i to node j. // from node i to node j.

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/mat" "gonum.org/v1/gonum/mat"
) )
@@ -85,22 +86,18 @@ func (g *UndirectedMatrix) has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *UndirectedMatrix) Nodes() []graph.Node { func (g *UndirectedMatrix) Nodes() graph.Nodes {
if g.nodes != nil { if g.nodes != nil {
nodes := make([]graph.Node, len(g.nodes)) nodes := make([]graph.Node, len(g.nodes))
copy(nodes, g.nodes) copy(nodes, g.nodes)
return nodes return iterator.NewOrderedNodes(nodes)
} }
r := g.mat.Symmetric() r := g.mat.Symmetric()
nodes := make([]graph.Node, r) return iterator.NewImplicitNodes(0, r, newSimpleNode)
for i := 0; i < r; i++ {
nodes[i] = Node(i)
}
return nodes
} }
// Edges returns all the edges in the graph. // Edges returns all the edges in the graph.
func (g *UndirectedMatrix) Edges() []graph.Edge { func (g *UndirectedMatrix) Edges() graph.Edges {
var edges []graph.Edge var edges []graph.Edge
r, _ := g.mat.Dims() r, _ := g.mat.Dims()
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
@@ -110,15 +107,15 @@ func (g *UndirectedMatrix) Edges() []graph.Edge {
} }
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *UndirectedMatrix) From(id int64) []graph.Node { func (g *UndirectedMatrix) From(id int64) graph.Nodes {
if !g.has(id) { if !g.has(id) {
return nil return nil
} }
var neighbors []graph.Node var nodes []graph.Node
r := g.mat.Symmetric() r := g.mat.Symmetric()
for i := 0; i < r; i++ { for i := 0; i < r; i++ {
if int64(i) == id { if int64(i) == id {
@@ -126,10 +123,10 @@ func (g *UndirectedMatrix) From(id int64) []graph.Node {
} }
// id is not greater than maximum int by this point. // id is not greater than maximum int by this point.
if !isSame(g.mat.At(int(id), i), g.absent) { if !isSame(g.mat.At(int(id), i), g.absent) {
neighbors = append(neighbors, g.Node(int64(i))) nodes = append(nodes, g.Node(int64(i)))
} }
} }
return neighbors return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -226,25 +223,6 @@ func (g *UndirectedMatrix) RemoveEdge(fid, tid int64) {
g.mat.SetSym(int(fid), int(tid), g.absent) g.mat.SetSym(int(fid), int(tid), g.absent)
} }
// Degree returns the degree of n in g.
func (g *UndirectedMatrix) Degree(id int64) int {
if !g.has(id) {
return 0
}
var deg int
r := g.mat.Symmetric()
for i := 0; i < r; i++ {
if int64(i) == id {
continue
}
// id is not greater than maximum int by this point.
if !isSame(g.mat.At(int(id), i), g.absent) {
deg++
}
}
return deg
}
// Matrix returns the mat.Matrix representation of the graph. // Matrix returns the mat.Matrix representation of the graph.
func (g *UndirectedMatrix) Matrix() mat.Matrix { func (g *UndirectedMatrix) Matrix() mat.Matrix {
// Prevent alteration of dimensions of the returned matrix. // Prevent alteration of dimensions of the returned matrix.

View File

@@ -38,7 +38,7 @@ func TestBasicDenseImpassable(t *testing.T) {
t.Errorf("Node that should exist doesn't: %d", i) t.Errorf("Node that should exist doesn't: %d", i)
} }
if degree := dg.Degree(int64(i)); degree != 0 { if degree := dg.From(int64(i)).Len(); degree != 0 {
t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree) t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree)
} }
} }
@@ -61,7 +61,7 @@ func TestBasicDensePassable(t *testing.T) {
t.Errorf("Node that should exist doesn't: %d", i) t.Errorf("Node that should exist doesn't: %d", i)
} }
if degree := dg.Degree(int64(i)); degree != 4 { if degree := dg.From(int64(i)).Len(); degree != 4 {
t.Errorf("Node in passable graph missing neighbors. Node: %d Degree: %d", i, degree) t.Errorf("Node in passable graph missing neighbors. Node: %d Degree: %d", i, degree)
} }
} }
@@ -77,18 +77,18 @@ func TestDirectedDenseAddRemove(t *testing.T) {
dg := NewDirectedMatrix(10, math.Inf(1), 0, math.Inf(1)) dg := NewDirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
dg.SetWeightedEdge(WeightedEdge{F: Node(0), T: Node(2), W: 1}) dg.SetWeightedEdge(WeightedEdge{F: Node(0), T: Node(2), W: 1})
if neighbors := dg.From(int64(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 || if neighbors := graph.NodesOf(dg.From(int64(0))); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
dg.Edge(int64(0), int64(2)) == nil { dg.Edge(int64(0), int64(2)) == nil {
t.Errorf("Adding edge didn't create successor") t.Errorf("Adding edge didn't create successor")
} }
dg.RemoveEdge(int64(0), int64(2)) dg.RemoveEdge(int64(0), int64(2))
if neighbors := dg.From(int64(0)); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil { if neighbors := graph.NodesOf(dg.From(int64(0))); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil {
t.Errorf("Removing edge didn't properly remove successor") t.Errorf("Removing edge didn't properly remove successor")
} }
if neighbors := dg.To(int64(2)); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil { if neighbors := graph.NodesOf(dg.To(int64(2))); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil {
t.Errorf("Removing directed edge wrongly kept predecessor") t.Errorf("Removing directed edge wrongly kept predecessor")
} }
@@ -109,12 +109,12 @@ func TestUndirectedDenseAddRemove(t *testing.T) {
dg := NewUndirectedMatrix(10, math.Inf(1), 0, math.Inf(1)) dg := NewUndirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
dg.SetEdge(Edge{F: Node(0), T: Node(2)}) dg.SetEdge(Edge{F: Node(0), T: Node(2)})
if neighbors := dg.From(int64(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 || if neighbors := graph.NodesOf(dg.From(int64(0))); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
dg.EdgeBetween(int64(0), int64(2)) == nil { dg.EdgeBetween(int64(0), int64(2)) == nil {
t.Errorf("Couldn't add neighbor") t.Errorf("Couldn't add neighbor")
} }
if neighbors := dg.From(int64(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 || if neighbors := graph.NodesOf(dg.From(int64(2))); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
dg.EdgeBetween(int64(2), int64(0)) == nil { dg.EdgeBetween(int64(2), int64(0)) == nil {
t.Errorf("Adding an undirected neighbor didn't add it reciprocally") t.Errorf("Adding an undirected neighbor didn't add it reciprocally")
} }
@@ -122,27 +122,27 @@ func TestUndirectedDenseAddRemove(t *testing.T) {
func TestDenseLists(t *testing.T) { func TestDenseLists(t *testing.T) {
dg := NewDirectedMatrix(15, 1, 0, math.Inf(1)) dg := NewDirectedMatrix(15, 1, 0, math.Inf(1))
nodes := dg.Nodes() nodes := graph.NodesOf(dg.Nodes())
if len(nodes) != 15 { if len(nodes) != 15 {
t.Fatalf("Wrong number of nodes") t.Fatalf("Wrong number of nodes: got:%v want:%v", len(nodes), 15)
} }
sort.Sort(ordered.ByID(nodes)) sort.Sort(ordered.ByID(nodes))
for i, node := range dg.Nodes() { for i, node := range graph.NodesOf(dg.Nodes()) {
if int64(i) != node.ID() { if int64(i) != node.ID() {
t.Errorf("Node list doesn't return properly id'd nodes") t.Errorf("Node list doesn't return properly id'd nodes")
} }
} }
edges := dg.Edges() edges := graph.EdgesOf(dg.Edges())
if len(edges) != 15*14 { if len(edges) != 15*14 {
t.Errorf("Improper number of edges for passable dense graph") t.Errorf("Improper number of edges for passable dense graph")
} }
dg.RemoveEdge(int64(12), int64(11)) dg.RemoveEdge(int64(12), int64(11))
edges = dg.Edges() edges = graph.EdgesOf(dg.Edges())
if len(edges) != (15*14)-1 { if len(edges) != (15*14)-1 {
t.Errorf("Removing edge didn't affect edge listing properly") t.Errorf("Removing edge didn't affect edge listing properly")
} }

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
// DirectedGraph implements a generalized directed graph. // DirectedGraph implements a generalized directed graph.
@@ -131,7 +132,7 @@ func (g *DirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *DirectedGraph) Nodes() []graph.Node { func (g *DirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -141,22 +142,22 @@ func (g *DirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. // Edges returns all the edges in the graph.
func (g *DirectedGraph) Edges() []graph.Edge { func (g *DirectedGraph) Edges() graph.Edges {
var edges []graph.Edge var edges []graph.Edge
for _, u := range g.nodes { for _, u := range g.nodes {
for _, e := range g.from[u.ID()] { for _, e := range g.from[u.ID()] {
edges = append(edges, e) edges = append(edges, e)
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *DirectedGraph) From(id int64) []graph.Node { func (g *DirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -167,11 +168,11 @@ func (g *DirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid] from[i] = g.nodes[vid]
i++ i++
} }
return from return iterator.NewOrderedNodes(from)
} }
// To returns all nodes in g that can reach directly to n. // To returns all nodes in g that can reach directly to n.
func (g *DirectedGraph) To(id int64) []graph.Node { func (g *DirectedGraph) To(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -182,7 +183,7 @@ func (g *DirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid] to[i] = g.nodes[uid]
i++ i++
} }
return to return iterator.NewOrderedNodes(to)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y without // HasEdgeBetween returns whether an edge exists between nodes x and y without
@@ -212,11 +213,3 @@ func (g *DirectedGraph) HasEdgeFromTo(uid, vid int64) bool {
} }
return true return true
} }
// Degree returns the in+out degree of n in g.
func (g *DirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
return len(g.from[id]) + len(g.to[id])
}

View File

@@ -21,7 +21,7 @@ var (
func TestEdgeOvercounting(t *testing.T) { func TestEdgeOvercounting(t *testing.T) {
g := generateDummyGraph() g := generateDummyGraph()
if neigh := g.From(int64(2)); len(neigh) != 2 { if neigh := graph.NodesOf(g.From(int64(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)) t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
} }
} }

View File

@@ -18,6 +18,10 @@ func (n Node) ID() int64 {
return int64(n) return int64(n)
} }
func newSimpleNode(id int) graph.Node {
return Node(id)
}
// Edge is a simple graph edge. // Edge is a simple graph edge.
type Edge struct { type Edge struct {
F, T graph.Node F, T graph.Node

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
// UndirectedGraph implements a generalized undirected graph. // UndirectedGraph implements a generalized undirected graph.
@@ -123,7 +124,7 @@ func (g *UndirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *UndirectedGraph) Nodes() []graph.Node { func (g *UndirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -133,11 +134,11 @@ func (g *UndirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. // Edges returns all the edges in the graph.
func (g *UndirectedGraph) Edges() []graph.Edge { func (g *UndirectedGraph) Edges() graph.Edges {
if len(g.edges) == 0 { if len(g.edges) == 0 {
return nil return nil
} }
@@ -155,11 +156,11 @@ func (g *UndirectedGraph) Edges() []graph.Edge {
edges = append(edges, e) edges = append(edges, e)
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *UndirectedGraph) From(id int64) []graph.Node { func (g *UndirectedGraph) From(id int64) graph.Nodes {
if !g.Has(id) { if !g.Has(id) {
return nil return nil
} }
@@ -170,7 +171,7 @@ func (g *UndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from] nodes[i] = g.nodes[from]
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -193,11 +194,3 @@ func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
} }
return edge return edge
} }
// Degree returns the degree of n in g.
func (g *UndirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
return len(g.edges[id])
}

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
// WeightedDirectedGraph implements a generalized weighted directed graph. // WeightedDirectedGraph implements a generalized weighted directed graph.
@@ -137,7 +138,7 @@ func (g *WeightedDirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *WeightedDirectedGraph) Nodes() []graph.Node { func (g *WeightedDirectedGraph) Nodes() graph.Nodes {
if len(g.from) == 0 { if len(g.from) == 0 {
return nil return nil
} }
@@ -147,18 +148,18 @@ func (g *WeightedDirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. // Edges returns all the edges in the graph.
func (g *WeightedDirectedGraph) Edges() []graph.Edge { func (g *WeightedDirectedGraph) Edges() graph.Edges {
var edges []graph.Edge var edges []graph.Edge
for _, u := range g.nodes { for _, u := range g.nodes {
for _, e := range g.from[u.ID()] { for _, e := range g.from[u.ID()] {
edges = append(edges, e) edges = append(edges, e)
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// WeightedEdges returns all the weighted edges in the graph. // WeightedEdges returns all the weighted edges in the graph.
@@ -173,7 +174,7 @@ func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge {
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *WeightedDirectedGraph) From(id int64) []graph.Node { func (g *WeightedDirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -184,11 +185,11 @@ func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid] from[i] = g.nodes[vid]
i++ i++
} }
return from return iterator.NewOrderedNodes(from)
} }
// To returns all nodes in g that can reach directly to n. // To returns all nodes in g that can reach directly to n.
func (g *WeightedDirectedGraph) To(id int64) []graph.Node { func (g *WeightedDirectedGraph) To(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok { if _, ok := g.from[id]; !ok {
return nil return nil
} }
@@ -199,7 +200,7 @@ func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid] to[i] = g.nodes[uid]
i++ i++
} }
return to return iterator.NewOrderedNodes(to)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y without // HasEdgeBetween returns whether an edge exists between nodes x and y without
@@ -251,11 +252,3 @@ func (g *WeightedDirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
} }
return g.absent, false return g.absent, false
} }
// Degree returns the in+out degree of n in g.
func (g *WeightedDirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
return len(g.from[id]) + len(g.to[id])
}

View File

@@ -23,7 +23,7 @@ var (
func TestWeightedEdgeOvercounting(t *testing.T) { func TestWeightedEdgeOvercounting(t *testing.T) {
g := generateDummyGraph() g := generateDummyGraph()
if neigh := g.From(int64(2)); len(neigh) != 2 { if neigh := graph.NodesOf(g.From(int64(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)) t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
} }
} }

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid" "gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
) )
// WeightedUndirectedGraph implements a generalized weighted undirected graph. // WeightedUndirectedGraph implements a generalized weighted undirected graph.
@@ -129,7 +130,7 @@ func (g *WeightedUndirectedGraph) Has(id int64) bool {
} }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g *WeightedUndirectedGraph) Nodes() []graph.Node { func (g *WeightedUndirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 { if len(g.nodes) == 0 {
return nil return nil
} }
@@ -139,11 +140,11 @@ func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
nodes[i] = n nodes[i] = n
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// Edges returns all the edges in the graph. // Edges returns all the edges in the graph.
func (g *WeightedUndirectedGraph) Edges() []graph.Edge { func (g *WeightedUndirectedGraph) Edges() graph.Edges {
if len(g.edges) == 0 { if len(g.edges) == 0 {
return nil return nil
} }
@@ -161,11 +162,11 @@ func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
edges = append(edges, e) edges = append(edges, e)
} }
} }
return edges return iterator.NewOrderedEdges(edges)
} }
// WeightedEdges returns all the weighted edges in the graph. // WeightedEdges returns all the weighted edges in the graph.
func (g *WeightedUndirectedGraph) WeightedEdges() []graph.WeightedEdge { func (g *WeightedUndirectedGraph) WeightedEdges() graph.WeightedEdges {
var edges []graph.WeightedEdge var edges []graph.WeightedEdge
seen := make(map[[2]int64]struct{}) seen := make(map[[2]int64]struct{})
for _, u := range g.edges { for _, u := range g.edges {
@@ -180,11 +181,11 @@ func (g *WeightedUndirectedGraph) WeightedEdges() []graph.WeightedEdge {
edges = append(edges, e) edges = append(edges, e)
} }
} }
return edges return iterator.NewOrderedWeightedEdges(edges)
} }
// From returns all nodes in g that can be reached directly from n. // From returns all nodes in g that can be reached directly from n.
func (g *WeightedUndirectedGraph) From(id int64) []graph.Node { func (g *WeightedUndirectedGraph) From(id int64) graph.Nodes {
if !g.Has(id) { if !g.Has(id) {
return nil return nil
} }
@@ -195,7 +196,7 @@ func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from] nodes[i] = g.nodes[from]
i++ i++
} }
return nodes return iterator.NewOrderedNodes(nodes)
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -245,11 +246,3 @@ func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
} }
return g.absent, false return g.absent, false
} }
// Degree returns the degree of n in g.
func (g *WeightedUndirectedGraph) Degree(id int64) int {
if _, ok := g.nodes[id]; !ok {
return 0
}
return len(g.edges[id])
}

View File

@@ -44,7 +44,7 @@ func KCore(k int, g graph.Undirected) []graph.Node {
// s, a set of relative offsets into l for each k-core, where k is an index // s, a set of relative offsets into l for each k-core, where k is an index
// into s. // into s.
func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) { func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// The algorithm used here is essentially as described at // The algorithm used here is essentially as described at
// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710 // http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
@@ -61,7 +61,7 @@ func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
) )
for _, n := range nodes { for _, n := range nodes {
id := n.ID() id := n.ID()
adj := g.From(id) adj := graph.NodesOf(g.From(id))
neighbours[id] = adj neighbours[id] = adj
dv[id] = len(adj) dv[id] = len(adj)
if len(adj) > maxDegree { if len(adj) > maxDegree {
@@ -133,7 +133,7 @@ func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
// BronKerbosch returns the set of maximal cliques of the undirected graph g. // BronKerbosch returns the set of maximal cliques of the undirected graph g.
func BronKerbosch(g graph.Undirected) [][]graph.Node { func BronKerbosch(g graph.Undirected) [][]graph.Node {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
// The algorithm used here is essentially BronKerbosch3 as described at // The algorithm used here is essentially BronKerbosch3 as described at
// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858 // http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
@@ -147,7 +147,7 @@ func BronKerbosch(g graph.Undirected) [][]graph.Node {
order, _ := degeneracyOrdering(g) order, _ := degeneracyOrdering(g)
ordered.Reverse(order) ordered.Reverse(order)
for _, v := range order { for _, v := range order {
neighbours := g.From(v.ID()) neighbours := graph.NodesOf(g.From(v.ID()))
nv := make(set.Nodes, len(neighbours)) nv := make(set.Nodes, len(neighbours))
for _, n := range neighbours { for _, n := range neighbours {
nv.Add(n) nv.Add(n)
@@ -177,7 +177,7 @@ func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p
continue continue
} }
vid := v.ID() vid := v.ID()
neighbours := g.From(vid) neighbours := graph.NodesOf(g.From(vid))
nv := make(set.Nodes, len(neighbours)) nv := make(set.Nodes, len(neighbours))
for _, n := range neighbours { for _, n := range neighbours {
nv.Add(n) nv.Add(n)
@@ -207,10 +207,10 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb
// compile time option. // compile time option.
if !tomitaTanakaTakahashi { if !tomitaTanakaTakahashi {
for _, n := range p { for _, n := range p {
return g.From(n.ID()) return graph.NodesOf(g.From(n.ID()))
} }
for _, n := range x { for _, n := range x {
return g.From(n.ID()) return graph.NodesOf(g.From(n.ID()))
} }
panic("bronKerbosch: empty set") panic("bronKerbosch: empty set")
} }
@@ -222,7 +222,7 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb
maxNeighbors := func(s set.Nodes) { maxNeighbors := func(s set.Nodes) {
outer: outer:
for _, u := range s { for _, u := range s {
nb := g.From(u.ID()) nb := graph.NodesOf(g.From(u.ID()))
c := len(nb) c := len(nb)
if c <= max { if c <= max {
continue continue

View File

@@ -10,6 +10,7 @@ import (
"gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered" "gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set" "gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
) )
// johnson implements Johnson's "Finding all the elementary // johnson implements Johnson's "Finding all the elementary
@@ -132,7 +133,7 @@ type johnsonGraph struct {
// johnsonGraphFrom returns a deep copy of the graph g. // johnsonGraphFrom returns a deep copy of the graph g.
func johnsonGraphFrom(g graph.Directed) johnsonGraph { func johnsonGraphFrom(g graph.Directed) johnsonGraph {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
sort.Sort(ordered.ByID(nodes)) sort.Sort(ordered.ByID(nodes))
c := johnsonGraph{ c := johnsonGraph{
orig: nodes, orig: nodes,
@@ -144,7 +145,7 @@ func johnsonGraphFrom(g graph.Directed) johnsonGraph {
for i, u := range nodes { for i, u := range nodes {
uid := u.ID() uid := u.ID()
c.index[uid] = i c.index[uid] = i
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
if c.succ[uid] == nil { if c.succ[uid] == nil {
c.succ[uid] = make(set.Int64s) c.succ[uid] = make(set.Int64s)
c.nodes.Add(uid) c.nodes.Add(uid)
@@ -239,16 +240,16 @@ func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
} }
// Nodes is required to satisfy Tarjan. // Nodes is required to satisfy Tarjan.
func (g johnsonGraph) Nodes() []graph.Node { func (g johnsonGraph) Nodes() graph.Nodes {
n := make([]graph.Node, 0, len(g.nodes)) n := make([]graph.Node, 0, len(g.nodes))
for id := range g.nodes { for id := range g.nodes {
n = append(n, johnsonGraphNode(id)) n = append(n, johnsonGraphNode(id))
} }
return n return iterator.NewOrderedNodes(n)
} }
// Successors is required to satisfy Tarjan. // Successors is required to satisfy Tarjan.
func (g johnsonGraph) From(id int64) []graph.Node { func (g johnsonGraph) From(id int64) graph.Nodes {
adj := g.succ[id] adj := g.succ[id]
if len(adj) == 0 { if len(adj) == 0 {
return nil return nil
@@ -257,7 +258,7 @@ func (g johnsonGraph) From(id int64) []graph.Node {
for id := range adj { for id := range adj {
succ = append(succ, johnsonGraphNode(id)) succ = append(succ, johnsonGraphNode(id))
} }
return succ return iterator.NewOrderedNodes(succ)
} }
func (johnsonGraph) Has(int64) bool { func (johnsonGraph) Has(int64) bool {
@@ -272,7 +273,7 @@ func (johnsonGraph) Edge(_, _ int64) graph.Edge {
func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool { func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool {
panic("topo: unintended use of johnsonGraph") panic("topo: unintended use of johnsonGraph")
} }
func (johnsonGraph) To(int64) []graph.Node { func (johnsonGraph) To(int64) graph.Nodes {
panic("topo: unintended use of johnsonGraph") panic("topo: unintended use of johnsonGraph")
} }

View File

@@ -19,7 +19,9 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
var cycles [][]graph.Node var cycles [][]graph.Node
done := make(set.Int64s) done := make(set.Int64s)
var tree linear.NodeStack var tree linear.NodeStack
for _, n := range g.Nodes() { nodes := g.Nodes()
for nodes.Next() {
n := nodes.Node()
id := n.ID() id := n.ID()
if done.Has(id) { if done.Has(id) {
continue continue
@@ -35,7 +37,7 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
u := tree.Pop() u := tree.Pop()
uid := u.ID() uid := u.ID()
adj := from[uid] adj := from[uid]
for _, v := range g.From(uid) { for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID() vid := v.ID()
switch { switch {
case uid == vid: case uid == vid:

View File

@@ -94,16 +94,18 @@ func TarjanSCC(g graph.Directed) [][]graph.Node {
} }
func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node { func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node {
nodes := g.Nodes() nodes := graph.NodesOf(g.Nodes())
var succ func(id int64) []graph.Node var succ func(id int64) []graph.Node
if order == nil { if order == nil {
succ = g.From succ = func(id int64) []graph.Node {
return graph.NodesOf(g.From(id))
}
} else { } else {
order(nodes) order(nodes)
ordered.Reverse(nodes) ordered.Reverse(nodes)
succ = func(id int64) []graph.Node { succ = func(id int64) []graph.Node {
to := g.From(id) to := graph.NodesOf(g.From(id))
order(to) order(to)
ordered.Reverse(to) ordered.Reverse(to)
return to return to

View File

@@ -16,7 +16,7 @@ var _ Graph = graph.Graph(nil)
type Graph interface { type Graph interface {
// From returns all nodes that can be reached directly // From returns all nodes that can be reached directly
// from the node with the given ID. // from the node with the given ID.
From(id int64) []graph.Node From(id int64) graph.Nodes
// Edge returns the edge from u to v, with IDs uid and vid, // Edge returns the edge from u to v, with IDs uid and vid,
// if such an edge exists and nil otherwise. The node v // if such an edge exists and nil otherwise. The node v
@@ -56,7 +56,9 @@ func (b *BreadthFirst) Walk(g Graph, from graph.Node, until func(n graph.Node, d
return t return t
} }
tid := t.ID() tid := t.ID()
for _, n := range g.From(tid) { to := g.From(tid)
for to.Next() {
n := to.Node()
nid := n.ID() nid := n.ID()
if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(tid, nid)) { if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(tid, nid)) {
continue continue
@@ -87,7 +89,9 @@ func (b *BreadthFirst) Walk(g Graph, from graph.Node, until func(n graph.Node, d
// during is called on each node as it is traversed. // during is called on each node as it is traversed.
func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) { func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
b.Reset() b.Reset()
for _, from := range g.Nodes() { nodes := g.Nodes()
for nodes.Next() {
from := nodes.Node()
if b.Visited(from) { if b.Visited(from) {
continue continue
} }
@@ -143,7 +147,9 @@ func (d *DepthFirst) Walk(g Graph, from graph.Node, until func(graph.Node) bool)
return t return t
} }
tid := t.ID() tid := t.ID()
for _, n := range g.From(tid) { to := g.From(tid)
for to.Next() {
n := to.Node()
nid := n.ID() nid := n.ID()
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(tid, nid)) { if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(tid, nid)) {
continue continue
@@ -168,7 +174,9 @@ func (d *DepthFirst) Walk(g Graph, from graph.Node, until func(graph.Node) bool)
// during is called on each node as it is traversed. // during is called on each node as it is traversed.
func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) { func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
d.Reset() d.Reset()
for _, from := range g.Nodes() { nodes := g.Nodes()
for nodes.Next() {
from := nodes.Node()
if d.Visited(from) { if d.Visited(from) {
continue continue
} }

View File

@@ -371,7 +371,7 @@ func gnpUndirected(n int, p float64) graph.Undirected {
} }
func benchmarkWalkAllBreadthFirst(b *testing.B, g graph.Undirected) { func benchmarkWalkAllBreadthFirst(b *testing.B, g graph.Undirected) {
n := len(g.Nodes()) n := g.Nodes().Len()
b.ResetTimer() b.ResetTimer()
var bft BreadthFirst var bft BreadthFirst
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
@@ -402,7 +402,7 @@ func BenchmarkWalkAllBreadthFirstGnp_1000_half(b *testing.B) {
} }
func benchmarkWalkAllDepthFirst(b *testing.B, g graph.Undirected) { func benchmarkWalkAllDepthFirst(b *testing.B, g graph.Undirected) {
n := len(g.Nodes()) n := g.Nodes().Len()
b.ResetTimer() b.ResetTimer()
var dft DepthFirst var dft DepthFirst
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@@ -15,25 +15,11 @@ var _ Undirected = Undirect{}
func (g Undirect) Has(id int64) bool { return g.G.Has(id) } func (g Undirect) Has(id int64) bool { return g.G.Has(id) }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g Undirect) Nodes() []Node { return g.G.Nodes() } func (g Undirect) Nodes() Nodes { return g.G.Nodes() }
// From returns all nodes in g that can be reached directly from u. // From returns all nodes in g that can be reached directly from u.
func (g Undirect) From(uid int64) []Node { func (g Undirect) From(uid int64) Nodes {
var nodes []Node return newNodeFilterIterator(g.G.From(uid), g.G.To(uid))
seen := make(map[int64]struct{})
for _, n := range g.G.From(uid) {
seen[n.ID()] = struct{}{}
nodes = append(nodes, n)
}
for _, n := range g.G.To(uid) {
id := n.ID()
if _, ok := seen[id]; ok {
continue
}
seen[n.ID()] = struct{}{}
nodes = append(nodes, n)
}
return nodes
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -94,25 +80,11 @@ var (
func (g UndirectWeighted) Has(id int64) bool { return g.G.Has(id) } func (g UndirectWeighted) Has(id int64) bool { return g.G.Has(id) }
// Nodes returns all the nodes in the graph. // Nodes returns all the nodes in the graph.
func (g UndirectWeighted) Nodes() []Node { return g.G.Nodes() } func (g UndirectWeighted) Nodes() Nodes { return g.G.Nodes() }
// From returns all nodes in g that can be reached directly from u. // From returns all nodes in g that can be reached directly from u.
func (g UndirectWeighted) From(uid int64) []Node { func (g UndirectWeighted) From(uid int64) Nodes {
var nodes []Node return newNodeFilterIterator(g.G.From(uid), g.G.To(uid))
seen := make(map[int64]struct{})
for _, n := range g.G.From(uid) {
seen[n.ID()] = struct{}{}
nodes = append(nodes, n)
}
for _, n := range g.G.To(uid) {
id := n.ID()
if _, ok := seen[id]; ok {
continue
}
seen[n.ID()] = struct{}{}
nodes = append(nodes, n)
}
return nodes
} }
// HasEdgeBetween returns whether an edge exists between nodes x and y. // HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -224,3 +196,54 @@ type WeightedEdgePair struct {
// Weight returns the merged edge weights of the two edges. // Weight returns the merged edge weights of the two edges.
func (e WeightedEdgePair) Weight() float64 { return e.W } func (e WeightedEdgePair) Weight() float64 { return e.W }
// nodeFilterIterator combines two Nodes to produce a single stream of
// unique nodes.
type nodeFilterIterator struct {
a, b Nodes
// unique indicates the node in b with the key ID is unique.
unique map[int64]bool
}
func newNodeFilterIterator(a, b Nodes) *nodeFilterIterator {
n := nodeFilterIterator{a: a, b: b, unique: make(map[int64]bool)}
for n.b.Next() {
n.unique[n.b.Node().ID()] = true
}
n.b.Reset()
for n.a.Next() {
n.unique[n.a.Node().ID()] = false
}
n.a.Reset()
return &n
}
func (n *nodeFilterIterator) Len() int {
return len(n.unique)
}
func (n *nodeFilterIterator) Next() bool {
n.Len()
if n.a.Next() {
return true
}
for n.b.Next() {
if n.unique[n.b.Node().ID()] {
return true
}
}
return false
}
func (n *nodeFilterIterator) Node() Node {
if n.a.Len() != 0 {
return n.a.Node()
}
return n.b.Node()
}
func (n *nodeFilterIterator) Reset() {
n.a.Reset()
n.b.Reset()
}

View File

@@ -121,9 +121,10 @@ func TestUndirect(t *testing.T) {
} }
src := graph.Undirect{G: g} src := graph.Undirect{G: g}
dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0) nodes := graph.NodesOf(src.Nodes())
for _, u := range src.Nodes() { dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0)
for _, v := range src.From(u.ID()) { for _, u := range nodes {
for _, v := range graph.NodesOf(src.From(u.ID())) {
dst.SetEdge(src.Edge(u.ID(), v.ID())) dst.SetEdge(src.Edge(u.ID(), v.ID()))
} }
} }
@@ -146,9 +147,10 @@ func TestUndirectWeighted(t *testing.T) {
} }
src := graph.UndirectWeighted{G: g, Absent: test.absent, Merge: test.merge} src := graph.UndirectWeighted{G: g, Absent: test.absent, Merge: test.merge}
dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0) nodes := graph.NodesOf(src.Nodes())
for _, u := range src.Nodes() { dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0)
for _, v := range src.From(u.ID()) { for _, u := range nodes {
for _, v := range graph.NodesOf(src.From(u.ID())) {
dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID())) dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID()))
} }
} }