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 {
case 1:
return [][]graph.Node{g.Nodes()}
return [][]graph.Node{graph.NodesOf(g.Nodes())}
case 2:
return topo.ConnectedComponents(g)
default:

View File

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

View File

@@ -13,6 +13,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered"
"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
@@ -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)
//
func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) float64 {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
weight := positiveWeightFuncFor(g)
// 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
u := n
uid := u.ID()
for _, v := range g.From(uid) {
wOut += weight(uid, v.ID())
to := g.From(uid)
for to.Next() {
wOut += weight(uid, to.Node().ID())
}
var wIn float64
v := n
vid := v.ID()
for _, u := range g.To(vid) {
wIn += weight(u.ID(), vid)
from := g.To(vid)
for from.Next() {
wIn += weight(from.Node().ID(), vid)
}
id := n.ID()
w := weight(id, id)
@@ -177,7 +180,7 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
return r
}
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the
// 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
u := n
uid := u.ID()
for _, v := range g.From(uid) {
vid := v.ID()
to := g.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
if vcid != id {
out = append(out, vcid)
@@ -223,8 +227,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
var in []int
v := n
vid := v.ID()
for _, u := range g.To(vid) {
uid := u.ID()
from := g.To(vid)
for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid]
if ucid != id {
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())
}
for _, v := range g.From(uid) {
vid := v.ID()
to := g.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
found := false
for _, e := range out {
@@ -305,8 +311,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
v := n
vid := v.ID()
for _, u := range g.To(vid) {
uid := u.ID()
from := g.To(vid)
for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid]
found := false
for _, e := range in {
@@ -335,32 +342,32 @@ func (g *ReducedDirected) Has(id int64) bool {
}
// 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))
for i := range g.nodes {
nodes[i] = node(i)
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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]
nodes := make([]graph.Node, len(out))
for i, vid := range out {
nodes[i] = g.nodes[vid]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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]
nodes := make([]graph.Node, len(in))
for i, uid := range in {
nodes[i] = g.nodes[uid]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// HasEdgeBetween returns whether an edge exists between nodes x and y.
@@ -473,7 +480,7 @@ type directedWeights struct {
// nodes.
// If g has a zero edge weight sum, nil is returned.
func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resolution float64) *directedLocalMover {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
l := directedLocalMover{
g: g,
nodes: nodes,
@@ -490,15 +497,17 @@ func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resol
u := n
var wOut float64
uid := u.ID()
for _, v := range g.From(uid) {
wOut += l.weight(uid, v.ID())
to := g.From(uid)
for to.Next() {
wOut += l.weight(uid, to.Node().ID())
}
v := n
var wIn float64
vid := v.ID()
for _, u := range g.To(vid) {
wIn += l.weight(u.ID(), vid)
from := g.To(vid)
for from.Next() {
wIn += l.weight(from.Node().ID(), vid)
}
id := n.ID()

View File

@@ -14,6 +14,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
)
// 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.
func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
q := make([]float64, g.Depth())
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
layerWeight := 1.0
layerResolution := 1.0
if len(resolutions) == 1 {
@@ -78,14 +79,16 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights
var wOut float64
u := n
uid := u.ID()
for _, v := range layer.From(uid) {
wOut += weight(uid, v.ID())
to := layer.From(uid)
for to.Next() {
wOut += weight(uid, to.Node().ID())
}
var wIn float64
v := n
vid := v.ID()
for _, u := range layer.To(vid) {
wIn += weight(u.ID(), vid)
from := layer.To(vid)
for from.Next() {
wIn += weight(from.Node().ID(), vid)
}
id := n.ID()
w := weight(id, id)
@@ -132,13 +135,15 @@ func NewDirectedLayers(layers ...graph.Directed) (DirectedLayers, error) {
return nil, nil
}
base := make(set.Int64s)
for _, n := range layers[0].Nodes() {
base.Add(n.ID())
nodes := layers[0].Nodes()
for nodes.Next() {
base.Add(nodes.Node().ID())
}
for i, l := range layers[1:] {
next := make(set.Int64s)
for _, n := range l.Nodes() {
next.Add(n.ID())
nodes := l.Nodes()
for nodes.Next() {
next.Add(nodes.Node().ID())
}
if !set.Int64sEqual(base, next) {
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.
func (g DirectedLayers) Nodes() []graph.Node {
func (g DirectedLayers) Nodes() graph.Nodes {
if len(g) == 0 {
return nil
}
@@ -219,12 +224,12 @@ var (
)
// 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))
for i := range g.nodes {
nodes[i] = node(i)
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Depth returns the number of layers in the multiplex graph.
@@ -288,7 +293,7 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
return r
}
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the
// community provided by the user for a Q calculation.
@@ -339,8 +344,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
var out []int
u := n
uid := u.ID()
for _, v := range layer.From(uid) {
vid := v.ID()
to := layer.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
if vcid != id {
out = append(out, vcid)
@@ -352,8 +358,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
var in []int
v := n
vid := v.ID()
for _, u := range layer.To(vid) {
uid := u.ID()
from := layer.To(vid)
for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid]
if ucid != id {
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())
}
for _, v := range layer.From(uid) {
vid := v.ID()
to := layer.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
found := false
for _, e := range out {
@@ -453,8 +461,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
v := n
vid := v.ID()
for _, u := range layer.To(vid) {
uid := u.ID()
from := layer.To(vid)
for from.Next() {
uid := from.Node().ID()
ucid := communityOf[uid]
found := false
for _, e := range in {
@@ -497,32 +506,32 @@ func (g directedLayerHandle) Has(id int64) bool {
}
// 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))
for i := range g.multiplex.nodes {
nodes[i] = node(i)
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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]
nodes := make([]graph.Node, len(out))
for i, vid := range out {
nodes[i] = g.multiplex.nodes[vid]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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]
nodes := make([]graph.Node, len(in))
for i, uid := range in {
nodes[i] = g.multiplex.nodes[uid]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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.
// If g has a zero edge weight sum, nil is returned.
func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *directedMultiplexLocalMover {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
l := directedMultiplexLocalMover{
g: g,
nodes: nodes,
@@ -684,15 +693,17 @@ func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][
u := n
uid := u.ID()
var wOut float64
for _, v := range layer.From(uid) {
wOut += weight(uid, v.ID())
to := layer.From(uid)
for to.Next() {
wOut += weight(uid, to.Node().ID())
}
v := n
vid := v.ID()
var wIn float64
for _, u := range layer.To(vid) {
wIn += weight(u.ID(), vid)
from := layer.To(vid)
for from.Next() {
wIn += weight(from.Node().ID(), vid)
}
id := n.ID()

View File

@@ -244,7 +244,7 @@ func init() {
// such that every edge dupGraph is replaced
// with an edge that flows from the low node
// 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() {
se := e.(simple.Edge)
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/internal/ordered"
"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
@@ -26,7 +27,7 @@ import (
// graph.Undirect may be used as a shim to allow calculation of Q for
// directed graphs.
func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution float64) float64 {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
weight := positiveWeightFuncFor(g)
// 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 {
uid := u.ID()
w := weight(uid, uid)
for _, v := range g.From(uid) {
w += weight(uid, v.ID())
to := g.From(uid)
for to.Next() {
w += weight(uid, to.Node().ID())
}
m2 += w
k[uid] = w
@@ -175,7 +177,7 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
return r
}
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the
// 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()
ucid := communityOf[uid]
var out []int
for _, v := range g.From(uid) {
vid := v.ID()
to := g.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
if vcid != ucid {
out = append(out, vcid)
@@ -268,8 +271,9 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
for _, v := range comm[i+1:] {
r.nodes[ucid].weight += 2 * weight(uid, v.ID())
}
for _, v := range g.From(uid) {
vid := v.ID()
to := g.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
found := false
for _, e := range out {
@@ -298,22 +302,22 @@ func (g *ReducedUndirected) Has(id int64) bool {
}
// 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))
for i := range g.nodes {
nodes[i] = node(i)
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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]
nodes := make([]graph.Node, len(out))
for i, vid := range out {
nodes[i] = g.nodes[vid]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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.
// If g has a zero edge weight sum, nil is returned.
func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, resolution float64) *undirectedLocalMover {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
l := undirectedLocalMover{
g: g,
nodes: nodes,
@@ -443,8 +447,9 @@ func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, r
for _, u := range l.nodes {
uid := u.ID()
w := l.weight(uid, uid)
for _, v := range g.From(uid) {
w += l.weight(uid, v.ID())
to := g.From(uid)
for to.Next() {
w += l.weight(uid, to.Node().ID())
}
l.edgeWeightOf[uid] = w
l.m2 += w

View File

@@ -14,6 +14,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/internal/set"
"gonum.org/v1/gonum/graph/iterator"
)
// UndirectedMultiplex is an undirected multiplex graph.
@@ -46,7 +47,7 @@ type UndirectedMultiplex interface {
// directed graphs.
func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
q := make([]float64, g.Depth())
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
layerWeight := 1.0
layerResolution := 1.0
if len(resolutions) == 1 {
@@ -80,8 +81,9 @@ func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, wei
for _, u := range nodes {
uid := u.ID()
w := weight(uid, uid)
for _, v := range layer.From(uid) {
w += weight(uid, v.ID())
to := layer.From(uid)
for to.Next() {
w += weight(uid, to.Node().ID())
}
m2 += w
k[uid] = w
@@ -129,13 +131,15 @@ func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
return nil, nil
}
base := make(set.Int64s)
for _, n := range layers[0].Nodes() {
base.Add(n.ID())
nodes := layers[0].Nodes()
for nodes.Next() {
base.Add(nodes.Node().ID())
}
for i, l := range layers[1:] {
next := make(set.Int64s)
for _, n := range l.Nodes() {
next.Add(n.ID())
nodes := l.Nodes()
for nodes.Next() {
next.Add(nodes.Node().ID())
}
if !set.Int64sEqual(next, base) {
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.
func (g UndirectedLayers) Nodes() []graph.Node {
func (g UndirectedLayers) Nodes() graph.Nodes {
if len(g) == 0 {
return nil
}
@@ -216,12 +220,12 @@ var (
)
// 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))
for i := range g.nodes {
nodes[i] = node(i)
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Depth returns the number of layers in the multiplex graph.
@@ -285,7 +289,7 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
return r
}
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
// TODO(kortschak) This sort is necessary really only
// for testing. In practice we would not be using the
// community provided by the user for a Q calculation.
@@ -333,8 +337,9 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
var out []int
uid := u.ID()
ucid := communityOf[uid]
for _, v := range layer.From(uid) {
vid := v.ID()
to := layer.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
if vcid != ucid {
out = append(out, vcid)
@@ -415,8 +420,9 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
for _, v := range comm[i+1:] {
r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID())
}
for _, v := range layer.From(uid) {
vid := v.ID()
to := layer.From(uid)
for to.Next() {
vid := to.Node().ID()
vcid := communityOf[vid]
found := false
for _, e := range out {
@@ -458,22 +464,22 @@ func (g undirectedLayerHandle) Has(id int64) bool {
}
// 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))
for i := range g.multiplex.nodes {
nodes[i] = node(i)
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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]
nodes := make([]graph.Node, len(out))
for i, vid := range out {
nodes[i] = g.multiplex.nodes[vid]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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.
// If g has a zero edge weight sum, nil is returned.
func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *undirectedMultiplexLocalMover {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
l := undirectedMultiplexLocalMover{
g: g,
nodes: nodes,
@@ -637,8 +643,9 @@ func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities
for _, u := range l.nodes {
uid := u.ID()
w := weight(uid, uid)
for _, v := range layer.From(uid) {
w += weight(uid, v.ID())
to := layer.From(uid)
for to.Next() {
w += weight(uid, to.Node().ID())
}
l.edgeWeightOf[i][uid] = 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 {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
sort.Sort(ordered.ByID(nodes))
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 the node is not linked to any other node
// the graph needs to be written now.
if len(g.From(n.ID())) == 0 {
if g.From(n.ID()).Len() == 0 {
g := s.Subgraph()
_, subIsDirected := g.(graph.Directed)
if subIsDirected != isDirected {
@@ -188,7 +188,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
havePrintedEdgeHeader := false
for _, n := range nodes {
nid := n.ID()
to := g.From(nid)
to := graph.NodesOf(g.From(nid))
sort.Sort(ordered.ByID(to))
for _, t := range to {
tid := t.ID()

View File

@@ -6,6 +6,7 @@ package main
import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple"
)
@@ -83,7 +84,7 @@ func (g *GraphNode) has(id int64, visited map[int64]struct{}) bool {
return false
}
func (g *GraphNode) Nodes() []graph.Node {
func (g *GraphNode) Nodes() graph.Nodes {
toReturn := []graph.Node{g}
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 {
@@ -131,9 +132,9 @@ func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph
return list
}
func (g *GraphNode) From(id int64) []graph.Node {
func (g *GraphNode) From(id int64) graph.Nodes {
if id == g.ID() {
return g.neighbors
return iterator.NewOrderedNodes(g.neighbors)
}
visited := map[int64]struct{}{g.id: {}}
@@ -141,7 +142,7 @@ func (g *GraphNode) From(id int64) []graph.Node {
visited[root.ID()] = struct{}{}
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 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
// Nodes returns all the nodes in the graph.
Nodes() []Node
Nodes() Nodes
// From returns all nodes that can be reached directly
// from the node with the given ID.
From(id int64) []Node
From(id int64) Nodes
// HasEdgeBetween returns whether an edge exists between
// 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 the node with the given ID.
To(id int64) []Node
To(id int64) Nodes
}
// WeightedDirected is a weighted directed graph.
@@ -113,7 +113,7 @@ type WeightedDirected interface {
// To returns all nodes that can reach directly
// 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.
@@ -219,11 +219,15 @@ type DirectedWeightedBuilder interface {
// be present in the destination after the copy is complete.
func Copy(dst Builder, src Graph) {
nodes := src.Nodes()
for _, n := range nodes {
dst.AddNode(n)
for nodes.Next() {
dst.AddNode(nodes.Node())
}
for _, u := range nodes {
for _, v := range src.From(u.ID()) {
nodes.Reset()
for nodes.Next() {
u := nodes.Node()
to := src.From(u.ID())
for to.Next() {
v := to.Node()
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.
func CopyWeighted(dst WeightedBuilder, src Weighted) {
nodes := src.Nodes()
for _, n := range nodes {
dst.AddNode(n)
for nodes.Next() {
dst.AddNode(nodes.Node())
}
for _, u := range nodes {
for _, v := range src.From(u.ID()) {
nodes.Reset()
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()))
}
}

View File

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

View File

@@ -185,15 +185,16 @@ func TestPowerLawUndirected(t *testing.T) {
}
nodes := g.Nodes()
if len(nodes) != n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
if nodes.Len() != n {
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()
var lines int
for _, v := range g.From(uid) {
lines += len(g.Lines(uid, v.ID()))
for _, v := range graph.NodesOf(g.From(uid)) {
lines += g.Lines(uid, v.ID()).Len()
}
if lines < d {
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()
if len(nodes) != n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
if nodes.Len() != n {
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()
var lines int
for _, v := range g.From(uid) {
lines += len(g.Lines(uid, v.ID()))
for _, v := range graph.NodesOf(g.From(uid)) {
lines += g.Lines(uid, v.ID()).Len()
}
if lines < d {
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()
if len(nodes) != 2*n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
if nodes.Len() != 2*n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
}
if len(p1) != n {
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))
}
for _, u := range nodes {
for nodes.Next() {
u := nodes.Node()
uid := u.ID()
var lines int
for _, v := range g.From(uid) {
lines += len(g.Lines(uid, v.ID()))
for _, v := range graph.NodesOf(g.From(uid)) {
lines += g.Lines(uid, v.ID()).Len()
}
if lines < d {
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()
if len(nodes) != 2*n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
if nodes.Len() != 2*n {
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
}
if len(p1) != n {
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))
}
for _, u := range nodes {
for nodes.Next() {
u := nodes.Node()
uid := u.ID()
var lines int
for _, v := range g.From(uid) {
lines += len(g.Lines(uid, v.ID()))
for _, v := range graph.NodesOf(g.From(uid)) {
lines += g.Lines(uid, v.ID()).Len()
}
if lines < d {
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
}
nodes := dst.Nodes()
nodes := graph.NodesOf(dst.Nodes())
sort.Sort(ordered.ByID(nodes))
if len(nodes) == 0 {
n--
@@ -76,7 +76,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
// into the rest of the graph.
for {
// Add edges to parent's neighbours.
to := dst.From(u.ID())
to := graph.NodesOf(dst.From(u.ID()))
sort.Sort(ordered.ByID(to))
for _, v := range to {
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
}
}

View File

@@ -69,7 +69,7 @@ func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64
for i := 0; i < m; i++ {
// Triad formation.
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()
if wid == int64(v) || dst.HasEdgeBetween(wid, int64(v)) {
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/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
var (
@@ -154,7 +155,7 @@ func (g *DirectedGraph) Has(id int64) bool {
}
// 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 {
return nil
}
@@ -164,12 +165,12 @@ func (g *DirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.Edge.
func (g *DirectedGraph) Edges() []graph.Edge {
func (g *DirectedGraph) Edges() graph.Edges {
var edges []graph.Edge
for _, u := range g.nodes {
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.
func (g *DirectedGraph) From(id int64) []graph.Node {
func (g *DirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok {
return nil
}
@@ -197,11 +198,11 @@ func (g *DirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid]
i++
}
return from
return iterator.NewOrderedNodes(from)
}
// 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 {
return nil
}
@@ -212,7 +213,7 @@ func (g *DirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid]
i++
}
return to
return iterator.NewOrderedNodes(to)
}
// 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 returned graph.Edge is a multi.Edge if an edge exists.
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 {
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.
// 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]
if len(edge) == 0 {
return nil
@@ -247,7 +248,7 @@ func (g *DirectedGraph) Lines(uid, vid int64) []graph.Line {
for _, l := range edge {
lines = append(lines, l)
}
return lines
return iterator.NewOrderedLines(lines)
}
// 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]
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) {
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))
}
}

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
var (
@@ -143,7 +144,7 @@ func (g *UndirectedGraph) Has(id int64) bool {
}
// 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 {
return nil
}
@@ -153,12 +154,12 @@ func (g *UndirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.Edge.
func (g *UndirectedGraph) Edges() []graph.Edge {
func (g *UndirectedGraph) Edges() graph.Edges {
if len(g.lines) == 0 {
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.
func (g *UndirectedGraph) From(id int64) []graph.Node {
func (g *UndirectedGraph) From(id int64) graph.Nodes {
if !g.Has(id) {
return nil
}
@@ -195,7 +196,7 @@ func (g *UndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from]
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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 returned graph.Edge is a multi.Edge if an edge exists.
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 {
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.
// 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)
}
// 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
for _, l := range g.lines[xid][yid] {
lines = append(lines, l)
}
return 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
return iterator.NewOrderedLines(lines)
}

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
var (
@@ -160,7 +161,7 @@ func (g *WeightedDirectedGraph) Has(id int64) bool {
}
// 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 {
return nil
}
@@ -171,12 +172,12 @@ func (g *WeightedDirectedGraph) Nodes() []graph.Node {
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.WeightedEdge.
func (g *WeightedDirectedGraph) Edges() []graph.Edge {
func (g *WeightedDirectedGraph) Edges() graph.Edges {
var edges []graph.Edge
for _, u := range g.nodes {
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
// is a multi.WeightedEdge.
func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge {
func (g *WeightedDirectedGraph) WeightedEdges() graph.WeightedEdges {
var edges []graph.WeightedEdge
for _, u := range g.nodes {
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.
func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
func (g *WeightedDirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok {
return nil
}
@@ -224,11 +225,11 @@ func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid]
i++
}
return from
return iterator.NewOrderedNodes(from)
}
// 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 {
return nil
}
@@ -239,7 +240,7 @@ func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid]
i++
}
return to
return iterator.NewOrderedNodes(to)
}
// 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 returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
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 {
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.
// 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]
if len(edge) == 0 {
return nil
@@ -289,12 +290,12 @@ func (g *WeightedDirectedGraph) Lines(uid, vid int64) []graph.Line {
for _, l := range edge {
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
// 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]
if len(edge) == 0 {
return nil
@@ -303,27 +304,12 @@ func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLi
for _, l := range edge {
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
// 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) {
lines := g.WeightedLines(uid, vid)
lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
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) {
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))
}
}

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
var (
@@ -147,7 +148,7 @@ func (g *WeightedUndirectedGraph) Has(id int64) bool {
}
// 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 {
return nil
}
@@ -157,12 +158,12 @@ func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Edges returns all the edges in the graph. Each edge in the returned slice
// is a multi.Edge.
func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
func (g *WeightedUndirectedGraph) Edges() graph.Edges {
if len(g.lines) == 0 {
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.
func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
func (g *WeightedUndirectedGraph) From(id int64) graph.Nodes {
if !g.Has(id) {
return nil
}
@@ -199,7 +200,7 @@ func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from]
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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.
// 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)
}
// 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]
if len(edge) == 0 {
return nil
@@ -230,7 +231,7 @@ func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) []graph.Line {
seen[lid] = struct{}{}
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.
@@ -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 returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
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 {
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.
// 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)
}
// 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]
if len(edge) == 0 {
return nil
@@ -283,24 +284,12 @@ func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) []graph.W
seen[lid] = struct{}{}
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
// 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) {
lines := g.WeightedLines(xid, yid)
lines := graph.WeightedLinesOf(g.WeightedLines(xid, yid))
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
// Nodes returns all the nodes in the multigraph.
Nodes() []Node
Nodes() Nodes
// From returns all nodes that can be reached directly
// from the node with the given ID.
From(id int64) []Node
From(id int64) Nodes
// HasEdgeBetween returns whether an edge exists between
// 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
// node v must be directly reachable from u as defined by
// the From method.
Lines(uid, vid int64) []Line
Lines(uid, vid int64) Lines
}
// WeightedMultigraph is a weighted multigraph.
@@ -49,7 +49,7 @@ type WeightedMultigraph interface {
// with IDs uid and vid if any such lines exist and nil
// otherwise. The node v must be directly reachable
// from u as defined by the From method.
WeightedLines(uid, vid int64) []WeightedLine
WeightedLines(uid, vid int64) WeightedLines
}
// UndirectedMultigraph is an undirected multigraph.
@@ -58,7 +58,7 @@ type UndirectedMultigraph interface {
// LinesBetween returns the lines between nodes x and y
// with IDs xid and yid.
LinesBetween(xid, yid int64) []Line
LinesBetween(xid, yid int64) Lines
}
// WeightedUndirectedMultigraph is a weighted undirected multigraph.
@@ -67,7 +67,7 @@ type WeightedUndirectedMultigraph interface {
// WeightedLinesBetween returns the lines between nodes
// x and y with IDs xid and yid.
WeightedLinesBetween(xid, yid int64) []WeightedLine
WeightedLinesBetween(xid, yid int64) WeightedLines
}
// DirectedMultigraph is a directed multigraph.
@@ -81,7 +81,7 @@ type DirectedMultigraph interface {
// To returns all nodes that can reach directly
// to the node with the given ID.
To(id int64) []Node
To(id int64) Nodes
}
// WeightedDirectedMultigraph is a weighted directed multigraph.
@@ -95,7 +95,7 @@ type WeightedDirectedMultigraph interface {
// To returns all nodes that can reach directly
// 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.

View File

@@ -90,7 +90,7 @@ func EdgeBetweenness(g graph.Graph) map[[2]int64]float64 {
// 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)) {
var (
nodes = g.Nodes()
nodes = graph.NodesOf(g.Nodes())
stack linear.NodeStack
p = make(map[int64][]graph.Node, 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()
vid := v.ID()
stack.Push(v)
for _, w := range g.From(vid) {
for _, w := range graph.NodesOf(g.From(vid)) {
wid := w.ID()
// w found for the first time?
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 {
cb := make(map[int64]float64)
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
for i, s := range nodes {
sid := s.ID()
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)
_, isUndirected := g.(graph.Undirected)
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
for i, s := range nodes {
sid := s.ID()
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.
// If g contains self edges, NewLaplacian will panic.
func NewLaplacian(g graph.Undirected) Laplacian {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
id := n.ID()
@@ -123,7 +123,7 @@ func NewLaplacian(g graph.Undirected) Laplacian {
l := mat.NewSymDense(len(nodes), nil)
for j, u := range nodes {
uid := u.ID()
to := g.From(uid)
to := graph.NodesOf(g.From(uid))
l.SetSym(j, j, float64(len(to)))
for _, v := range to {
vid := v.ID()
@@ -146,7 +146,7 @@ func NewLaplacian(g graph.Undirected) Laplacian {
// matrix of the input graph.
// If g contains self edges, NewSymNormLaplacian will panic.
func NewSymNormLaplacian(g graph.Undirected) Laplacian {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
id := n.ID()
@@ -156,7 +156,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
l := mat.NewSymDense(len(nodes), nil)
for j, u := range nodes {
uid := u.ID()
to := g.From(uid)
to := graph.NodesOf(g.From(uid))
if len(to) == 0 {
continue
}
@@ -168,7 +168,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
panic("network: self edge in graph")
}
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.
// If g contains self edges, NewRandomWalkLaplacian will panic.
func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
id := n.ID()
@@ -193,7 +193,7 @@ func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
l := mat.NewDense(len(nodes), len(nodes), nil)
for j, u := range nodes {
uid := u.ID()
to := g.From(uid)
to := graph.NodesOf(g.From(uid))
if len(to) == 0 {
continue
}

View File

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

View File

@@ -19,7 +19,7 @@ import (
// For directed graphs the incoming paths are used. Infinite distances are
// not considered.
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))
for _, u := range nodes {
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
// not considered.
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))
for _, u := range nodes {
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
// not considered.
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))
for i, u := range nodes {
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
// not considered.
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))
for i, u := range nodes {
uid := u.ID()

View File

@@ -22,7 +22,7 @@ type HubAuthority struct {
// vector difference between iterations is below tol. The returned map is
// keyed on the graph node IDs.
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.
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))
for i, n := range nodes {
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()])
}
for _, v := range g.From(id) {
for _, v := range graph.NodesOf(g.From(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
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
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)
dangling := damp / float64(len(nodes))
for j, u := range nodes {
to := g.From(u.ID())
to := graph.NodesOf(g.From(u.ID()))
var z float64
for _, v := range to {
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
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
indexOf[n.ID()] = i
@@ -144,7 +144,7 @@ func edgeWeightedPageRankSparse(g graph.WeightedDirected, damp, tol float64) map
var dangling compressedRow
df := damp / float64(len(nodes))
for j, u := range nodes {
to := g.From(u.ID())
to := graph.NodesOf(g.From(u.ID()))
var z float64
for _, v := range to {
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
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
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)
dangling := damp / float64(len(nodes))
for j, u := range nodes {
to := g.From(u.ID())
to := graph.NodesOf(g.From(u.ID()))
f := damp / float64(len(to))
for _, v := range to {
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
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
indexOf := make(map[int64]int, len(nodes))
for i, n := range nodes {
indexOf[n.ID()] = i
@@ -298,7 +298,7 @@ func pageRankSparse(g graph.Directed, damp, tol float64) map[int64]float64 {
var dangling compressedRow
df := damp / float64(len(nodes))
for j, u := range nodes {
to := g.From(u.ID())
to := graph.NodesOf(g.From(u.ID()))
f := damp / float64(len(to))
for _, v := range to {
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()
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)
for _, v := range g.From(u.node.ID()) {
for _, v := range graph.NodesOf(g.From(u.node.ID())) {
vid := v.ID()
if visited.Has(vid) {
continue

View File

@@ -192,8 +192,8 @@ func TestExhaustiveAStar(t *testing.T) {
}
ps := DijkstraAllPaths(g)
for _, start := range g.Nodes() {
for _, goal := range g.Nodes() {
for _, start := range graph.NodesOf(g.Nodes()) {
for _, goal := range graph.NodesOf(g.Nodes()) {
pt, _ := AStar(start, goal, g, heuristic)
gotPath, gotWeight := pt.To(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 isMonotonic(g UndirectedWeightLister, h Heuristic) (ok bool, at graph.Edge, goal graph.Node) {
for _, goal := range g.Nodes() {
for _, edge := range g.WeightedEdges() {
for _, goal := range graph.NodesOf(g.Nodes()) {
for _, edge := range graph.WeightedEdgesOf(g.WeightedEdges()) {
from := edge.From()
to := edge.To()
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)
}
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
path = newShortestFrom(u, nodes)
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
for j, u := range nodes {
uid := u.ID()
for _, v := range g.From(uid) {
for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID()
k := path.indexOf[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 {
uid := u.ID()
for _, v := range g.From(uid) {
for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID()
k := path.indexOf[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/dot"
"gonum.org/v1/gonum/graph/graphs/gen"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/graph/simple"
"gonum.org/v1/gonum/graph/topo"
)
@@ -240,7 +241,7 @@ func duplication(n int, delta, alpha, sigma float64) func() *simple.DirectedGrap
if err != nil {
panic(err)
}
for _, e := range g.Edges() {
for _, e := range graph.EdgesOf(g.Edges()) {
if rnd.Intn(2) == 0 {
g.RemoveEdge(e.From().ID(), e.To().ID())
}
@@ -253,8 +254,10 @@ type undirected struct {
*simple.DirectedGraph
}
func (g undirected) From(id int64) []graph.Node {
return append(g.DirectedGraph.From(id), g.DirectedGraph.To(id)...)
func (g undirected) From(id int64) graph.Nodes {
return iterator.NewOrderedNodes(append(
graph.NodesOf(g.DirectedGraph.From(id)),
graph.NodesOf(g.DirectedGraph.To(id))...))
}
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
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()
idx, ok := lt.indexOf[wid]

View File

@@ -162,7 +162,7 @@ func (lt *sLengauerTarjan) dfs(g graph.Directed, v graph.Node) {
ltv.label = 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()
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()) {
return Shortest{from: u}
}
path = newShortestFrom(u, h.Nodes())
path = newShortestFrom(u, graph.NodesOf(h.Nodes()))
} else {
if g.From(u.ID()) == nil {
return Shortest{from: u}
@@ -58,7 +58,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
continue
}
mnid := mid.node.ID()
for _, v := range g.From(mnid) {
for _, v := range graph.NodesOf(g.From(mnid)) {
vid := v.ID()
j, ok := path.indexOf[vid]
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|).
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
paths = newAllShortest(g.Nodes(), false)
paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
dijkstraAllPaths(g, paths)
return paths
}
@@ -123,7 +123,7 @@ func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
paths.dist.Set(i, k, mid.dist)
}
mnid := mid.node.ID()
for _, v := range g.From(mnid) {
for _, v := range graph.NodesOf(g.From(mnid)) {
vid := v.ID()
j := paths.indexOf[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})
for _, n := range g.Nodes() {
for _, n := range graph.NodesOf(g.Nodes()) {
switch n.ID() {
case d.s.ID():
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))
}
}
for _, u := range d.model.Nodes() {
for _, u := range graph.NodesOf(d.model.Nodes()) {
uid := u.ID()
for _, v := range g.From(uid) {
for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID()
w := edgeWeight(d.weight, uid, vid)
if w < 0 {
@@ -196,7 +196,7 @@ func (d *DStarLite) findShortestPath() {
case u.g > u.rhs:
u.g = u.rhs
d.queue.remove(u)
for _, _s := range d.model.To(uid) {
for _, _s := range graph.NodesOf(d.model.To(uid)) {
s := _s.(*dStarLiteNode)
sid := s.ID()
if sid != d.t.ID() {
@@ -207,13 +207,13 @@ func (d *DStarLite) findShortestPath() {
default:
gOld := u.g
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)
sid := s.ID()
if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld {
if s.ID() != d.t.ID() {
s.rhs = math.Inf(1)
for _, t := range d.model.From(sid) {
for _, t := range graph.NodesOf(d.model.From(sid)) {
tid := t.ID()
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
dsid := d.s.ID()
for _, _s := range d.model.From(dsid) {
for _, _s := range graph.NodesOf(d.model.From(dsid)) {
s := _s.(*dStarLiteNode)
w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g
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 {
if uid != d.t.ID() {
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)
}
}
@@ -365,7 +365,7 @@ func (d *DStarLite) Path() (p []graph.Node, weight float64) {
cost float64
)
uid := u.ID()
for _, _v := range d.model.From(uid) {
for _, _v := range graph.NodesOf(d.model.From(uid)) {
v := _v.(*dStarLiteNode)
vid := v.ID()
w := edgeWeight(d.model.Weight, uid, vid)

View File

@@ -387,7 +387,7 @@ var dynamicDStarLiteTests = []struct {
modify: func(l *testgraphs.LimitedVisionGrid) {
all := l.Grid.AllVisible
l.Grid.AllVisible = false
for _, n := range l.Nodes() {
for _, n := range graph.NodesOf(l.Nodes()) {
id := n.ID()
l.Known[id] = !l.Grid.Has(id)
}
@@ -400,9 +400,9 @@ var dynamicDStarLiteTests = []struct {
l.Known[l.NodeAt(wallRow, wallCol).ID()] = false
// Check we have a correctly modified representation.
for _, u := range l.Nodes() {
for _, u := range graph.NodesOf(l.Nodes()) {
uid := u.ID()
for _, v := range l.Nodes() {
for _, v := range graph.NodesOf(l.Nodes()) {
vid := v.ID()
if l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) {
ur, uc := l.RowCol(uid)
@@ -598,7 +598,7 @@ func TestDStarLiteDynamic(t *testing.T) {
}
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() {
changes, _ := l.MoveTo(d.Here())
got = append(got, l.Location)

View File

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

View File

@@ -10,6 +10,7 @@ import (
"math"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"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
// false, otherwise all nodes are returned.
func (g *Grid) Nodes() []graph.Node {
func (g *Grid) Nodes() graph.Nodes {
var nodes []graph.Node
for id, ok := range g.open {
if ok || g.AllVisible {
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
@@ -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
// 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) {
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.

View File

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

View File

@@ -9,6 +9,7 @@ import (
"math"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
"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.
func (l *LimitedVisionGrid) Nodes() []graph.Node {
func (l *LimitedVisionGrid) Nodes() graph.Nodes {
nodes := make([]graph.Node, 0, len(l.Grid.open))
for id := range l.Grid.open {
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.
@@ -166,7 +167,7 @@ func (l *LimitedVisionGrid) Has(id int64) bool {
}
// 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) {
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.

View File

@@ -1166,11 +1166,11 @@ func TestLimitedVisionGrid(t *testing.T) {
l.Grid.AllowDiagonal = test.diag
x, y := l.XY(test.path[0].ID())
for _, u := range l.Nodes() {
for _, u := range graph.NodesOf(l.Nodes()) {
uid := u.ID()
ux, uy := l.XY(uid)
uNear := math.Hypot(x-ux, y-uy) <= test.radius
for _, v := range l.Nodes() {
for _, v := range graph.NodesOf(l.Nodes()) {
vid := v.ID()
vx, vy := l.XY(vid)
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)
}
paths = newAllShortest(g.Nodes(), false)
paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
sign := int64(-1)
for {
@@ -69,7 +69,7 @@ type johnsonWeightAdjuster struct {
q int64
g graph.Graph
from func(id int64) []graph.Node
from func(id int64) graph.Nodes
edgeTo func(uid, vid int64) graph.Edge
weight Weighting
@@ -93,14 +93,14 @@ func (g johnsonWeightAdjuster) Has(id int64) bool {
panic("path: unintended use of johnsonWeightAdjuster")
}
func (g johnsonWeightAdjuster) Nodes() []graph.Node {
func (g johnsonWeightAdjuster) Nodes() graph.Nodes {
if g.bellmanFord {
return append(g.g.Nodes(), johnsonGraphNode(g.q))
return newJohnsonNodeIterator(g.q, 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 {
return g.g.Nodes()
}
@@ -140,3 +140,59 @@ func (johnsonWeightAdjuster) HasEdgeBetween(_, _ int64) bool {
type johnsonGraphNode int64
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.
for _, n := range g.Nodes() {
g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node('Q'), T: n})
nodes := g.Nodes()
for nodes.Next() {
g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node('Q'), T: nodes.Node()})
}
// 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.
func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
if len(nodes) == 0 {
return 0
}
@@ -49,7 +49,7 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
u := nodes[0]
uid := u.ID()
for _, v := range g.From(uid) {
for _, v := range graph.NodesOf(g.From(uid)) {
w, ok := g.Weight(uid, v.ID())
if !ok {
panic("prim: unexpected invalid weight")
@@ -67,7 +67,7 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
u = e.From()
uid := u.ID()
for _, n := range g.From(uid) {
for _, n := range graph.NodesOf(g.From(uid)) {
if key, ok := q.key(n); ok {
w, ok := g.Weight(uid, n.ID())
if !ok {
@@ -146,7 +146,7 @@ func (q *primQueue) update(u, v graph.Node, key float64) {
// the set of edges in the graph.
type UndirectedWeightLister interface {
graph.WeightedUndirected
WeightedEdges() []graph.WeightedEdge
WeightedEdges() graph.WeightedEdges
}
// 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.
func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 {
edges := g.WeightedEdges()
edges := graph.WeightedEdgesOf(g.WeightedEdges())
sort.Sort(byWeight(edges))
ds := newDisjointSet()
for _, node := range g.Nodes() {
for _, node := range graph.NodesOf(g.Nodes()) {
dst.AddNode(node)
ds.makeSet(node.ID())
}

View File

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

View File

@@ -8,6 +8,7 @@ import (
"sort"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/iterator"
)
// 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{}
}
func (g yenKSPAdjuster) From(id int64) []graph.Node {
nodes := g.Graph.From(id)
func (g yenKSPAdjuster) From(id int64) graph.Nodes {
nodes := graph.NodesOf(g.Graph.From(id))
for i := 0; i < len(nodes); {
if g.canWalk(id, nodes[i].ID()) {
i++
@@ -126,7 +127,7 @@ func (g yenKSPAdjuster) From(id int64) []graph.Node {
nodes[i] = nodes[len(nodes)-1]
nodes = nodes[:len(nodes)-1]
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
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/internal/ordered"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/mat"
)
@@ -85,22 +86,18 @@ func (g *DirectedMatrix) has(id int64) bool {
}
// Nodes returns all the nodes in the graph.
func (g *DirectedMatrix) Nodes() []graph.Node {
func (g *DirectedMatrix) Nodes() graph.Nodes {
if g.nodes != nil {
nodes := make([]graph.Node, len(g.nodes))
copy(nodes, g.nodes)
return nodes
return iterator.NewOrderedNodes(nodes)
}
r, _ := g.mat.Dims()
nodes := make([]graph.Node, r)
for i := 0; i < r; i++ {
nodes[i] = Node(i)
}
return nodes
return iterator.NewImplicitNodes(0, r, newSimpleNode)
}
// Edges returns all the edges in the graph.
func (g *DirectedMatrix) Edges() []graph.Edge {
func (g *DirectedMatrix) Edges() graph.Edges {
var edges []graph.Edge
r, _ := g.mat.Dims()
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.
func (g *DirectedMatrix) From(id int64) []graph.Node {
func (g *DirectedMatrix) From(id int64) graph.Nodes {
if !g.has(id) {
return nil
}
var neighbors []graph.Node
var nodes []graph.Node
_, c := g.mat.Dims()
for j := 0; j < c; j++ {
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.
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.
func (g *DirectedMatrix) To(id int64) []graph.Node {
func (g *DirectedMatrix) To(id int64) graph.Nodes {
if !g.has(id) {
return nil
}
var neighbors []graph.Node
var nodes []graph.Node
r, _ := g.mat.Dims()
for i := 0; i < r; i++ {
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.
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
@@ -251,34 +248,6 @@ func (g *DirectedMatrix) RemoveEdge(fid, tid int64) {
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
// of the matrix is such that the matrix entry at G_{ij} is the weight of the edge
// from node i to node j.

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered"
"gonum.org/v1/gonum/graph/iterator"
"gonum.org/v1/gonum/mat"
)
@@ -85,22 +86,18 @@ func (g *UndirectedMatrix) has(id int64) bool {
}
// Nodes returns all the nodes in the graph.
func (g *UndirectedMatrix) Nodes() []graph.Node {
func (g *UndirectedMatrix) Nodes() graph.Nodes {
if g.nodes != nil {
nodes := make([]graph.Node, len(g.nodes))
copy(nodes, g.nodes)
return nodes
return iterator.NewOrderedNodes(nodes)
}
r := g.mat.Symmetric()
nodes := make([]graph.Node, r)
for i := 0; i < r; i++ {
nodes[i] = Node(i)
}
return nodes
return iterator.NewImplicitNodes(0, r, newSimpleNode)
}
// Edges returns all the edges in the graph.
func (g *UndirectedMatrix) Edges() []graph.Edge {
func (g *UndirectedMatrix) Edges() graph.Edges {
var edges []graph.Edge
r, _ := g.mat.Dims()
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.
func (g *UndirectedMatrix) From(id int64) []graph.Node {
func (g *UndirectedMatrix) From(id int64) graph.Nodes {
if !g.has(id) {
return nil
}
var neighbors []graph.Node
var nodes []graph.Node
r := g.mat.Symmetric()
for i := 0; i < r; i++ {
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.
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.
@@ -226,25 +223,6 @@ func (g *UndirectedMatrix) RemoveEdge(fid, tid int64) {
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.
func (g *UndirectedMatrix) Matrix() mat.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)
}
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)
}
}
@@ -61,7 +61,7 @@ func TestBasicDensePassable(t *testing.T) {
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)
}
}
@@ -77,18 +77,18 @@ func TestDirectedDenseAddRemove(t *testing.T) {
dg := NewDirectedMatrix(10, math.Inf(1), 0, math.Inf(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 {
t.Errorf("Adding edge didn't create successor")
}
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")
}
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")
}
@@ -109,12 +109,12 @@ func TestUndirectedDenseAddRemove(t *testing.T) {
dg := NewUndirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
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 {
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 {
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) {
dg := NewDirectedMatrix(15, 1, 0, math.Inf(1))
nodes := dg.Nodes()
nodes := graph.NodesOf(dg.Nodes())
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))
for i, node := range dg.Nodes() {
for i, node := range graph.NodesOf(dg.Nodes()) {
if int64(i) != node.ID() {
t.Errorf("Node list doesn't return properly id'd nodes")
}
}
edges := dg.Edges()
edges := graph.EdgesOf(dg.Edges())
if len(edges) != 15*14 {
t.Errorf("Improper number of edges for passable dense graph")
}
dg.RemoveEdge(int64(12), int64(11))
edges = dg.Edges()
edges = graph.EdgesOf(dg.Edges())
if len(edges) != (15*14)-1 {
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/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
// 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.
func (g *DirectedGraph) Nodes() []graph.Node {
func (g *DirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 {
return nil
}
@@ -141,22 +142,22 @@ func (g *DirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Edges returns all the edges in the graph.
func (g *DirectedGraph) Edges() []graph.Edge {
func (g *DirectedGraph) Edges() graph.Edges {
var edges []graph.Edge
for _, u := range g.nodes {
for _, e := range g.from[u.ID()] {
edges = append(edges, e)
}
}
return edges
return iterator.NewOrderedEdges(edges)
}
// 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 {
return nil
}
@@ -167,11 +168,11 @@ func (g *DirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid]
i++
}
return from
return iterator.NewOrderedNodes(from)
}
// 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 {
return nil
}
@@ -182,7 +183,7 @@ func (g *DirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid]
i++
}
return to
return iterator.NewOrderedNodes(to)
}
// 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
}
// 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) {
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))
}
}

View File

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

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
// 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.
func (g *UndirectedGraph) Nodes() []graph.Node {
func (g *UndirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 {
return nil
}
@@ -133,11 +134,11 @@ func (g *UndirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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 {
return nil
}
@@ -155,11 +156,11 @@ func (g *UndirectedGraph) Edges() []graph.Edge {
edges = append(edges, e)
}
}
return edges
return iterator.NewOrderedEdges(edges)
}
// 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) {
return nil
}
@@ -170,7 +171,7 @@ func (g *UndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from]
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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
}
// 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/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
// 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.
func (g *WeightedDirectedGraph) Nodes() []graph.Node {
func (g *WeightedDirectedGraph) Nodes() graph.Nodes {
if len(g.from) == 0 {
return nil
}
@@ -147,18 +148,18 @@ func (g *WeightedDirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// Edges returns all the edges in the graph.
func (g *WeightedDirectedGraph) Edges() []graph.Edge {
func (g *WeightedDirectedGraph) Edges() graph.Edges {
var edges []graph.Edge
for _, u := range g.nodes {
for _, e := range g.from[u.ID()] {
edges = append(edges, e)
}
}
return edges
return iterator.NewOrderedEdges(edges)
}
// 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.
func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
func (g *WeightedDirectedGraph) From(id int64) graph.Nodes {
if _, ok := g.from[id]; !ok {
return nil
}
@@ -184,11 +185,11 @@ func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
from[i] = g.nodes[vid]
i++
}
return from
return iterator.NewOrderedNodes(from)
}
// 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 {
return nil
}
@@ -199,7 +200,7 @@ func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
to[i] = g.nodes[uid]
i++
}
return to
return iterator.NewOrderedNodes(to)
}
// 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
}
// 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) {
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))
}
}

View File

@@ -9,6 +9,7 @@ import (
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/uid"
"gonum.org/v1/gonum/graph/iterator"
)
// 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.
func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
func (g *WeightedUndirectedGraph) Nodes() graph.Nodes {
if len(g.nodes) == 0 {
return nil
}
@@ -139,11 +140,11 @@ func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
nodes[i] = n
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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 {
return nil
}
@@ -161,11 +162,11 @@ func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
edges = append(edges, e)
}
}
return edges
return iterator.NewOrderedEdges(edges)
}
// 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
seen := make(map[[2]int64]struct{})
for _, u := range g.edges {
@@ -180,11 +181,11 @@ func (g *WeightedUndirectedGraph) WeightedEdges() []graph.WeightedEdge {
edges = append(edges, e)
}
}
return edges
return iterator.NewOrderedWeightedEdges(edges)
}
// 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) {
return nil
}
@@ -195,7 +196,7 @@ func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
nodes[i] = g.nodes[from]
i++
}
return nodes
return iterator.NewOrderedNodes(nodes)
}
// 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
}
// 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
// into s.
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
// 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 {
id := n.ID()
adj := g.From(id)
adj := graph.NodesOf(g.From(id))
neighbours[id] = adj
dv[id] = len(adj)
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.
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
// 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)
ordered.Reverse(order)
for _, v := range order {
neighbours := g.From(v.ID())
neighbours := graph.NodesOf(g.From(v.ID()))
nv := make(set.Nodes, len(neighbours))
for _, n := range neighbours {
nv.Add(n)
@@ -177,7 +177,7 @@ func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p
continue
}
vid := v.ID()
neighbours := g.From(vid)
neighbours := graph.NodesOf(g.From(vid))
nv := make(set.Nodes, len(neighbours))
for _, n := range neighbours {
nv.Add(n)
@@ -207,10 +207,10 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb
// compile time option.
if !tomitaTanakaTakahashi {
for _, n := range p {
return g.From(n.ID())
return graph.NodesOf(g.From(n.ID()))
}
for _, n := range x {
return g.From(n.ID())
return graph.NodesOf(g.From(n.ID()))
}
panic("bronKerbosch: empty set")
}
@@ -222,7 +222,7 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb
maxNeighbors := func(s set.Nodes) {
outer:
for _, u := range s {
nb := g.From(u.ID())
nb := graph.NodesOf(g.From(u.ID()))
c := len(nb)
if c <= max {
continue

View File

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

View File

@@ -19,7 +19,9 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
var cycles [][]graph.Node
done := make(set.Int64s)
var tree linear.NodeStack
for _, n := range g.Nodes() {
nodes := g.Nodes()
for nodes.Next() {
n := nodes.Node()
id := n.ID()
if done.Has(id) {
continue
@@ -35,7 +37,7 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
u := tree.Pop()
uid := u.ID()
adj := from[uid]
for _, v := range g.From(uid) {
for _, v := range graph.NodesOf(g.From(uid)) {
vid := v.ID()
switch {
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 {
nodes := g.Nodes()
nodes := graph.NodesOf(g.Nodes())
var succ func(id int64) []graph.Node
if order == nil {
succ = g.From
succ = func(id int64) []graph.Node {
return graph.NodesOf(g.From(id))
}
} else {
order(nodes)
ordered.Reverse(nodes)
succ = func(id int64) []graph.Node {
to := g.From(id)
to := graph.NodesOf(g.From(id))
order(to)
ordered.Reverse(to)
return to

View File

@@ -16,7 +16,7 @@ var _ Graph = graph.Graph(nil)
type Graph interface {
// From returns all nodes that can be reached directly
// 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,
// 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
}
tid := t.ID()
for _, n := range g.From(tid) {
to := g.From(tid)
for to.Next() {
n := to.Node()
nid := n.ID()
if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(tid, nid)) {
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.
func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
b.Reset()
for _, from := range g.Nodes() {
nodes := g.Nodes()
for nodes.Next() {
from := nodes.Node()
if b.Visited(from) {
continue
}
@@ -143,7 +147,9 @@ func (d *DepthFirst) Walk(g Graph, from graph.Node, until func(graph.Node) bool)
return t
}
tid := t.ID()
for _, n := range g.From(tid) {
to := g.From(tid)
for to.Next() {
n := to.Node()
nid := n.ID()
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(tid, nid)) {
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.
func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
d.Reset()
for _, from := range g.Nodes() {
nodes := g.Nodes()
for nodes.Next() {
from := nodes.Node()
if d.Visited(from) {
continue
}

View File

@@ -371,7 +371,7 @@ func gnpUndirected(n int, p float64) graph.Undirected {
}
func benchmarkWalkAllBreadthFirst(b *testing.B, g graph.Undirected) {
n := len(g.Nodes())
n := g.Nodes().Len()
b.ResetTimer()
var bft BreadthFirst
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) {
n := len(g.Nodes())
n := g.Nodes().Len()
b.ResetTimer()
var dft DepthFirst
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) }
// 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.
func (g Undirect) From(uid int64) []Node {
var nodes []Node
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
func (g Undirect) From(uid int64) Nodes {
return newNodeFilterIterator(g.G.From(uid), g.G.To(uid))
}
// 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) }
// 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.
func (g UndirectWeighted) From(uid int64) []Node {
var nodes []Node
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
func (g UndirectWeighted) From(uid int64) Nodes {
return newNodeFilterIterator(g.G.From(uid), g.G.To(uid))
}
// 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.
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}
dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0)
for _, u := range src.Nodes() {
for _, v := range src.From(u.ID()) {
nodes := graph.NodesOf(src.Nodes())
dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0)
for _, u := range nodes {
for _, v := range graph.NodesOf(src.From(u.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}
dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0)
for _, u := range src.Nodes() {
for _, v := range src.From(u.ID()) {
nodes := graph.NodesOf(src.Nodes())
dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0)
for _, u := range nodes {
for _, v := range graph.NodesOf(src.From(u.ID())) {
dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID()))
}
}