mirror of
https://github.com/gonum/gonum.git
synced 2025-10-13 10:54:03 +08:00
graph: add node, edge and line iterators
This commit is contained in:
@@ -26,7 +26,7 @@ func KCliqueCommunities(k int, g graph.Undirected) [][]graph.Node {
|
|||||||
}
|
}
|
||||||
switch k {
|
switch k {
|
||||||
case 1:
|
case 1:
|
||||||
return [][]graph.Node{g.Nodes()}
|
return [][]graph.Node{graph.NodesOf(g.Nodes())}
|
||||||
case 2:
|
case 2:
|
||||||
return topo.ConnectedComponents(g)
|
return topo.ConnectedComponents(g)
|
||||||
default:
|
default:
|
||||||
|
@@ -95,11 +95,11 @@ func Modularize(g graph.Graph, resolution float64, src rand.Source) ReducedGraph
|
|||||||
|
|
||||||
// Multiplex is a multiplex graph.
|
// Multiplex is a multiplex graph.
|
||||||
type Multiplex interface {
|
type Multiplex interface {
|
||||||
// Nodes returns the slice of nodes
|
// Nodes returns the nodes
|
||||||
// for the multiplex graph.
|
// for the multiplex graph.
|
||||||
// All layers must refer to the same
|
// All layers must refer to the same
|
||||||
// set of nodes.
|
// set of nodes.
|
||||||
Nodes() []graph.Node
|
Nodes() graph.Nodes
|
||||||
|
|
||||||
// Depth returns the number of layers
|
// Depth returns the number of layers
|
||||||
// in the multiplex graph.
|
// in the multiplex graph.
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
"gonum.org/v1/gonum/graph/internal/set"
|
"gonum.org/v1/gonum/graph/internal/set"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// qDirected returns the modularity Q score of the graph g subdivided into the
|
// qDirected returns the modularity Q score of the graph g subdivided into the
|
||||||
@@ -24,7 +25,7 @@ import (
|
|||||||
// Q = 1/m \sum_{ij} [ A_{ij} - (\gamma k_i^in k_j^out)/m ] \delta(c_i,c_j)
|
// Q = 1/m \sum_{ij} [ A_{ij} - (\gamma k_i^in k_j^out)/m ] \delta(c_i,c_j)
|
||||||
//
|
//
|
||||||
func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) float64 {
|
func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
weight := positiveWeightFuncFor(g)
|
weight := positiveWeightFuncFor(g)
|
||||||
|
|
||||||
// Calculate the total edge weight of the graph
|
// Calculate the total edge weight of the graph
|
||||||
@@ -35,14 +36,16 @@ func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64)
|
|||||||
var wOut float64
|
var wOut float64
|
||||||
u := n
|
u := n
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
wOut += weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
wOut += weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
var wIn float64
|
var wIn float64
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range g.To(vid) {
|
from := g.To(vid)
|
||||||
wIn += weight(u.ID(), vid)
|
for from.Next() {
|
||||||
|
wIn += weight(from.Node().ID(), vid)
|
||||||
}
|
}
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
w := weight(id, id)
|
w := weight(id, id)
|
||||||
@@ -177,7 +180,7 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
// TODO(kortschak) This sort is necessary really only
|
// TODO(kortschak) This sort is necessary really only
|
||||||
// for testing. In practice we would not be using the
|
// for testing. In practice we would not be using the
|
||||||
// community provided by the user for a Q calculation.
|
// community provided by the user for a Q calculation.
|
||||||
@@ -210,8 +213,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
|
|||||||
var out []int
|
var out []int
|
||||||
u := n
|
u := n
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
if vcid != id {
|
if vcid != id {
|
||||||
out = append(out, vcid)
|
out = append(out, vcid)
|
||||||
@@ -223,8 +227,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
|
|||||||
var in []int
|
var in []int
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range g.To(vid) {
|
from := g.To(vid)
|
||||||
uid := u.ID()
|
for from.Next() {
|
||||||
|
uid := from.Node().ID()
|
||||||
ucid := communityOf[uid]
|
ucid := communityOf[uid]
|
||||||
if ucid != id {
|
if ucid != id {
|
||||||
in = append(in, ucid)
|
in = append(in, ucid)
|
||||||
@@ -285,8 +290,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
|
|||||||
r.nodes[id].weight += weight(uid, v.ID())
|
r.nodes[id].weight += weight(uid, v.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
found := false
|
found := false
|
||||||
for _, e := range out {
|
for _, e := range out {
|
||||||
@@ -305,8 +311,9 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
|
|||||||
|
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range g.To(vid) {
|
from := g.To(vid)
|
||||||
uid := u.ID()
|
for from.Next() {
|
||||||
|
uid := from.Node().ID()
|
||||||
ucid := communityOf[uid]
|
ucid := communityOf[uid]
|
||||||
found := false
|
found := false
|
||||||
for _, e := range in {
|
for _, e := range in {
|
||||||
@@ -335,32 +342,32 @@ func (g *ReducedDirected) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *ReducedDirected) Nodes() []graph.Node {
|
func (g *ReducedDirected) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, len(g.nodes))
|
nodes := make([]graph.Node, len(g.nodes))
|
||||||
for i := range g.nodes {
|
for i := range g.nodes {
|
||||||
nodes[i] = node(i)
|
nodes[i] = node(i)
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from u.
|
// From returns all nodes in g that can be reached directly from u.
|
||||||
func (g *ReducedDirected) From(uid int64) []graph.Node {
|
func (g *ReducedDirected) From(uid int64) graph.Nodes {
|
||||||
out := g.edgesFrom[uid]
|
out := g.edgesFrom[uid]
|
||||||
nodes := make([]graph.Node, len(out))
|
nodes := make([]graph.Node, len(out))
|
||||||
for i, vid := range out {
|
for i, vid := range out {
|
||||||
nodes[i] = g.nodes[vid]
|
nodes[i] = g.nodes[vid]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to v.
|
// To returns all nodes in g that can reach directly to v.
|
||||||
func (g *ReducedDirected) To(vid int64) []graph.Node {
|
func (g *ReducedDirected) To(vid int64) graph.Nodes {
|
||||||
in := g.edgesTo[vid]
|
in := g.edgesTo[vid]
|
||||||
nodes := make([]graph.Node, len(in))
|
nodes := make([]graph.Node, len(in))
|
||||||
for i, uid := range in {
|
for i, uid := range in {
|
||||||
nodes[i] = g.nodes[uid]
|
nodes[i] = g.nodes[uid]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -473,7 +480,7 @@ type directedWeights struct {
|
|||||||
// nodes.
|
// nodes.
|
||||||
// If g has a zero edge weight sum, nil is returned.
|
// If g has a zero edge weight sum, nil is returned.
|
||||||
func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resolution float64) *directedLocalMover {
|
func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resolution float64) *directedLocalMover {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
l := directedLocalMover{
|
l := directedLocalMover{
|
||||||
g: g,
|
g: g,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
@@ -490,15 +497,17 @@ func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resol
|
|||||||
u := n
|
u := n
|
||||||
var wOut float64
|
var wOut float64
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
wOut += l.weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
wOut += l.weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
v := n
|
v := n
|
||||||
var wIn float64
|
var wIn float64
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range g.To(vid) {
|
from := g.To(vid)
|
||||||
wIn += l.weight(u.ID(), vid)
|
for from.Next() {
|
||||||
|
wIn += l.weight(from.Node().ID(), vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
"gonum.org/v1/gonum/graph/internal/set"
|
"gonum.org/v1/gonum/graph/internal/set"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DirectedMultiplex is a directed multiplex graph.
|
// DirectedMultiplex is a directed multiplex graph.
|
||||||
@@ -43,7 +44,7 @@ type DirectedMultiplex interface {
|
|||||||
// Note that Q values for multiplex graphs are not scaled by the total layer edge weight.
|
// Note that Q values for multiplex graphs are not scaled by the total layer edge weight.
|
||||||
func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
|
func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
|
||||||
q := make([]float64, g.Depth())
|
q := make([]float64, g.Depth())
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
layerWeight := 1.0
|
layerWeight := 1.0
|
||||||
layerResolution := 1.0
|
layerResolution := 1.0
|
||||||
if len(resolutions) == 1 {
|
if len(resolutions) == 1 {
|
||||||
@@ -78,14 +79,16 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights
|
|||||||
var wOut float64
|
var wOut float64
|
||||||
u := n
|
u := n
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
wOut += weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
wOut += weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
var wIn float64
|
var wIn float64
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range layer.To(vid) {
|
from := layer.To(vid)
|
||||||
wIn += weight(u.ID(), vid)
|
for from.Next() {
|
||||||
|
wIn += weight(from.Node().ID(), vid)
|
||||||
}
|
}
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
w := weight(id, id)
|
w := weight(id, id)
|
||||||
@@ -132,13 +135,15 @@ func NewDirectedLayers(layers ...graph.Directed) (DirectedLayers, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
base := make(set.Int64s)
|
base := make(set.Int64s)
|
||||||
for _, n := range layers[0].Nodes() {
|
nodes := layers[0].Nodes()
|
||||||
base.Add(n.ID())
|
for nodes.Next() {
|
||||||
|
base.Add(nodes.Node().ID())
|
||||||
}
|
}
|
||||||
for i, l := range layers[1:] {
|
for i, l := range layers[1:] {
|
||||||
next := make(set.Int64s)
|
next := make(set.Int64s)
|
||||||
for _, n := range l.Nodes() {
|
nodes := l.Nodes()
|
||||||
next.Add(n.ID())
|
for nodes.Next() {
|
||||||
|
next.Add(nodes.Node().ID())
|
||||||
}
|
}
|
||||||
if !set.Int64sEqual(base, next) {
|
if !set.Int64sEqual(base, next) {
|
||||||
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
|
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
|
||||||
@@ -148,7 +153,7 @@ func NewDirectedLayers(layers ...graph.Directed) (DirectedLayers, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns the nodes of the receiver.
|
// Nodes returns the nodes of the receiver.
|
||||||
func (g DirectedLayers) Nodes() []graph.Node {
|
func (g DirectedLayers) Nodes() graph.Nodes {
|
||||||
if len(g) == 0 {
|
if len(g) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -219,12 +224,12 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *ReducedDirectedMultiplex) Nodes() []graph.Node {
|
func (g *ReducedDirectedMultiplex) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, len(g.nodes))
|
nodes := make([]graph.Node, len(g.nodes))
|
||||||
for i := range g.nodes {
|
for i := range g.nodes {
|
||||||
nodes[i] = node(i)
|
nodes[i] = node(i)
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Depth returns the number of layers in the multiplex graph.
|
// Depth returns the number of layers in the multiplex graph.
|
||||||
@@ -288,7 +293,7 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
// TODO(kortschak) This sort is necessary really only
|
// TODO(kortschak) This sort is necessary really only
|
||||||
// for testing. In practice we would not be using the
|
// for testing. In practice we would not be using the
|
||||||
// community provided by the user for a Q calculation.
|
// community provided by the user for a Q calculation.
|
||||||
@@ -339,8 +344,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
|
|||||||
var out []int
|
var out []int
|
||||||
u := n
|
u := n
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
if vcid != id {
|
if vcid != id {
|
||||||
out = append(out, vcid)
|
out = append(out, vcid)
|
||||||
@@ -352,8 +358,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
|
|||||||
var in []int
|
var in []int
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range layer.To(vid) {
|
from := layer.To(vid)
|
||||||
uid := u.ID()
|
for from.Next() {
|
||||||
|
uid := from.Node().ID()
|
||||||
ucid := communityOf[uid]
|
ucid := communityOf[uid]
|
||||||
if ucid != id {
|
if ucid != id {
|
||||||
in = append(in, ucid)
|
in = append(in, ucid)
|
||||||
@@ -433,8 +440,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
|
|||||||
r.nodes[id].weights[l] += sign * weight(uid, v.ID())
|
r.nodes[id].weights[l] += sign * weight(uid, v.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
found := false
|
found := false
|
||||||
for _, e := range out {
|
for _, e := range out {
|
||||||
@@ -453,8 +461,9 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
|
|||||||
|
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
for _, u := range layer.To(vid) {
|
from := layer.To(vid)
|
||||||
uid := u.ID()
|
for from.Next() {
|
||||||
|
uid := from.Node().ID()
|
||||||
ucid := communityOf[uid]
|
ucid := communityOf[uid]
|
||||||
found := false
|
found := false
|
||||||
for _, e := range in {
|
for _, e := range in {
|
||||||
@@ -497,32 +506,32 @@ func (g directedLayerHandle) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g directedLayerHandle) Nodes() []graph.Node {
|
func (g directedLayerHandle) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, len(g.multiplex.nodes))
|
nodes := make([]graph.Node, len(g.multiplex.nodes))
|
||||||
for i := range g.multiplex.nodes {
|
for i := range g.multiplex.nodes {
|
||||||
nodes[i] = node(i)
|
nodes[i] = node(i)
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from u.
|
// From returns all nodes in g that can be reached directly from u.
|
||||||
func (g directedLayerHandle) From(uid int64) []graph.Node {
|
func (g directedLayerHandle) From(uid int64) graph.Nodes {
|
||||||
out := g.multiplex.layers[g.layer].edgesFrom[uid]
|
out := g.multiplex.layers[g.layer].edgesFrom[uid]
|
||||||
nodes := make([]graph.Node, len(out))
|
nodes := make([]graph.Node, len(out))
|
||||||
for i, vid := range out {
|
for i, vid := range out {
|
||||||
nodes[i] = g.multiplex.nodes[vid]
|
nodes[i] = g.multiplex.nodes[vid]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to v.
|
// To returns all nodes in g that can reach directly to v.
|
||||||
func (g directedLayerHandle) To(vid int64) []graph.Node {
|
func (g directedLayerHandle) To(vid int64) graph.Nodes {
|
||||||
in := g.multiplex.layers[g.layer].edgesTo[vid]
|
in := g.multiplex.layers[g.layer].edgesTo[vid]
|
||||||
nodes := make([]graph.Node, len(in))
|
nodes := make([]graph.Node, len(in))
|
||||||
for i, uid := range in {
|
for i, uid := range in {
|
||||||
nodes[i] = g.multiplex.nodes[uid]
|
nodes[i] = g.multiplex.nodes[uid]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -643,7 +652,7 @@ type directedMultiplexLocalMover struct {
|
|||||||
// node IDs of g must be contiguous in [0,n) where n is the number of nodes.
|
// node IDs of g must be contiguous in [0,n) where n is the number of nodes.
|
||||||
// If g has a zero edge weight sum, nil is returned.
|
// If g has a zero edge weight sum, nil is returned.
|
||||||
func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *directedMultiplexLocalMover {
|
func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *directedMultiplexLocalMover {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
l := directedMultiplexLocalMover{
|
l := directedMultiplexLocalMover{
|
||||||
g: g,
|
g: g,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
@@ -684,15 +693,17 @@ func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][
|
|||||||
u := n
|
u := n
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
var wOut float64
|
var wOut float64
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
wOut += weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
wOut += weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
v := n
|
v := n
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
var wIn float64
|
var wIn float64
|
||||||
for _, u := range layer.To(vid) {
|
from := layer.To(vid)
|
||||||
wIn += weight(u.ID(), vid)
|
for from.Next() {
|
||||||
|
wIn += weight(from.Node().ID(), vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
|
@@ -244,7 +244,7 @@ func init() {
|
|||||||
// such that every edge dupGraph is replaced
|
// such that every edge dupGraph is replaced
|
||||||
// with an edge that flows from the low node
|
// with an edge that flows from the low node
|
||||||
// ID to the high node ID.
|
// ID to the high node ID.
|
||||||
for _, e := range dupGraph.Edges() {
|
for _, e := range graph.EdgesOf(dupGraph.Edges()) {
|
||||||
if e.To().ID() < e.From().ID() {
|
if e.To().ID() < e.From().ID() {
|
||||||
se := e.(simple.Edge)
|
se := e.(simple.Edge)
|
||||||
se.F, se.T = se.T, se.F
|
se.F, se.T = se.T, se.F
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
"gonum.org/v1/gonum/graph/internal/set"
|
"gonum.org/v1/gonum/graph/internal/set"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// qUndirected returns the modularity Q score of the graph g subdivided into the
|
// qUndirected returns the modularity Q score of the graph g subdivided into the
|
||||||
@@ -26,7 +27,7 @@ import (
|
|||||||
// graph.Undirect may be used as a shim to allow calculation of Q for
|
// graph.Undirect may be used as a shim to allow calculation of Q for
|
||||||
// directed graphs.
|
// directed graphs.
|
||||||
func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution float64) float64 {
|
func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution float64) float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
weight := positiveWeightFuncFor(g)
|
weight := positiveWeightFuncFor(g)
|
||||||
|
|
||||||
// Calculate the total edge weight of the graph
|
// Calculate the total edge weight of the graph
|
||||||
@@ -36,8 +37,9 @@ func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution floa
|
|||||||
for _, u := range nodes {
|
for _, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
w := weight(uid, uid)
|
w := weight(uid, uid)
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
w += weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
w += weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
m2 += w
|
m2 += w
|
||||||
k[uid] = w
|
k[uid] = w
|
||||||
@@ -175,7 +177,7 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
// TODO(kortschak) This sort is necessary really only
|
// TODO(kortschak) This sort is necessary really only
|
||||||
// for testing. In practice we would not be using the
|
// for testing. In practice we would not be using the
|
||||||
// community provided by the user for a Q calculation.
|
// community provided by the user for a Q calculation.
|
||||||
@@ -205,8 +207,9 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
|
|||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
ucid := communityOf[uid]
|
ucid := communityOf[uid]
|
||||||
var out []int
|
var out []int
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
if vcid != ucid {
|
if vcid != ucid {
|
||||||
out = append(out, vcid)
|
out = append(out, vcid)
|
||||||
@@ -268,8 +271,9 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
|
|||||||
for _, v := range comm[i+1:] {
|
for _, v := range comm[i+1:] {
|
||||||
r.nodes[ucid].weight += 2 * weight(uid, v.ID())
|
r.nodes[ucid].weight += 2 * weight(uid, v.ID())
|
||||||
}
|
}
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
found := false
|
found := false
|
||||||
for _, e := range out {
|
for _, e := range out {
|
||||||
@@ -298,22 +302,22 @@ func (g *ReducedUndirected) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *ReducedUndirected) Nodes() []graph.Node {
|
func (g *ReducedUndirected) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, len(g.nodes))
|
nodes := make([]graph.Node, len(g.nodes))
|
||||||
for i := range g.nodes {
|
for i := range g.nodes {
|
||||||
nodes[i] = node(i)
|
nodes[i] = node(i)
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from u.
|
// From returns all nodes in g that can be reached directly from u.
|
||||||
func (g *ReducedUndirected) From(uid int64) []graph.Node {
|
func (g *ReducedUndirected) From(uid int64) graph.Nodes {
|
||||||
out := g.edges[uid]
|
out := g.edges[uid]
|
||||||
nodes := make([]graph.Node, len(out))
|
nodes := make([]graph.Node, len(out))
|
||||||
for i, vid := range out {
|
for i, vid := range out {
|
||||||
nodes[i] = g.nodes[vid]
|
nodes[i] = g.nodes[vid]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -427,7 +431,7 @@ type undirectedLocalMover struct {
|
|||||||
// node IDs of g must be contiguous in [0,n) where n is the number of nodes.
|
// node IDs of g must be contiguous in [0,n) where n is the number of nodes.
|
||||||
// If g has a zero edge weight sum, nil is returned.
|
// If g has a zero edge weight sum, nil is returned.
|
||||||
func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, resolution float64) *undirectedLocalMover {
|
func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, resolution float64) *undirectedLocalMover {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
l := undirectedLocalMover{
|
l := undirectedLocalMover{
|
||||||
g: g,
|
g: g,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
@@ -443,8 +447,9 @@ func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, r
|
|||||||
for _, u := range l.nodes {
|
for _, u := range l.nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
w := l.weight(uid, uid)
|
w := l.weight(uid, uid)
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
w += l.weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
w += l.weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
l.edgeWeightOf[uid] = w
|
l.edgeWeightOf[uid] = w
|
||||||
l.m2 += w
|
l.m2 += w
|
||||||
|
@@ -14,6 +14,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
"gonum.org/v1/gonum/graph/internal/set"
|
"gonum.org/v1/gonum/graph/internal/set"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UndirectedMultiplex is an undirected multiplex graph.
|
// UndirectedMultiplex is an undirected multiplex graph.
|
||||||
@@ -46,7 +47,7 @@ type UndirectedMultiplex interface {
|
|||||||
// directed graphs.
|
// directed graphs.
|
||||||
func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
|
func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64) []float64 {
|
||||||
q := make([]float64, g.Depth())
|
q := make([]float64, g.Depth())
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
layerWeight := 1.0
|
layerWeight := 1.0
|
||||||
layerResolution := 1.0
|
layerResolution := 1.0
|
||||||
if len(resolutions) == 1 {
|
if len(resolutions) == 1 {
|
||||||
@@ -80,8 +81,9 @@ func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, wei
|
|||||||
for _, u := range nodes {
|
for _, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
w := weight(uid, uid)
|
w := weight(uid, uid)
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
w += weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
w += weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
m2 += w
|
m2 += w
|
||||||
k[uid] = w
|
k[uid] = w
|
||||||
@@ -129,13 +131,15 @@ func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
base := make(set.Int64s)
|
base := make(set.Int64s)
|
||||||
for _, n := range layers[0].Nodes() {
|
nodes := layers[0].Nodes()
|
||||||
base.Add(n.ID())
|
for nodes.Next() {
|
||||||
|
base.Add(nodes.Node().ID())
|
||||||
}
|
}
|
||||||
for i, l := range layers[1:] {
|
for i, l := range layers[1:] {
|
||||||
next := make(set.Int64s)
|
next := make(set.Int64s)
|
||||||
for _, n := range l.Nodes() {
|
nodes := l.Nodes()
|
||||||
next.Add(n.ID())
|
for nodes.Next() {
|
||||||
|
next.Add(nodes.Node().ID())
|
||||||
}
|
}
|
||||||
if !set.Int64sEqual(next, base) {
|
if !set.Int64sEqual(next, base) {
|
||||||
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
|
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
|
||||||
@@ -145,7 +149,7 @@ func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns the nodes of the receiver.
|
// Nodes returns the nodes of the receiver.
|
||||||
func (g UndirectedLayers) Nodes() []graph.Node {
|
func (g UndirectedLayers) Nodes() graph.Nodes {
|
||||||
if len(g) == 0 {
|
if len(g) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -216,12 +220,12 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *ReducedUndirectedMultiplex) Nodes() []graph.Node {
|
func (g *ReducedUndirectedMultiplex) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, len(g.nodes))
|
nodes := make([]graph.Node, len(g.nodes))
|
||||||
for i := range g.nodes {
|
for i := range g.nodes {
|
||||||
nodes[i] = node(i)
|
nodes[i] = node(i)
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Depth returns the number of layers in the multiplex graph.
|
// Depth returns the number of layers in the multiplex graph.
|
||||||
@@ -285,7 +289,7 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
// TODO(kortschak) This sort is necessary really only
|
// TODO(kortschak) This sort is necessary really only
|
||||||
// for testing. In practice we would not be using the
|
// for testing. In practice we would not be using the
|
||||||
// community provided by the user for a Q calculation.
|
// community provided by the user for a Q calculation.
|
||||||
@@ -333,8 +337,9 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
|
|||||||
var out []int
|
var out []int
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
ucid := communityOf[uid]
|
ucid := communityOf[uid]
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
if vcid != ucid {
|
if vcid != ucid {
|
||||||
out = append(out, vcid)
|
out = append(out, vcid)
|
||||||
@@ -415,8 +420,9 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
|
|||||||
for _, v := range comm[i+1:] {
|
for _, v := range comm[i+1:] {
|
||||||
r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID())
|
r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID())
|
||||||
}
|
}
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
vcid := communityOf[vid]
|
vcid := communityOf[vid]
|
||||||
found := false
|
found := false
|
||||||
for _, e := range out {
|
for _, e := range out {
|
||||||
@@ -458,22 +464,22 @@ func (g undirectedLayerHandle) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g undirectedLayerHandle) Nodes() []graph.Node {
|
func (g undirectedLayerHandle) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, len(g.multiplex.nodes))
|
nodes := make([]graph.Node, len(g.multiplex.nodes))
|
||||||
for i := range g.multiplex.nodes {
|
for i := range g.multiplex.nodes {
|
||||||
nodes[i] = node(i)
|
nodes[i] = node(i)
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from u.
|
// From returns all nodes in g that can be reached directly from u.
|
||||||
func (g undirectedLayerHandle) From(uid int64) []graph.Node {
|
func (g undirectedLayerHandle) From(uid int64) graph.Nodes {
|
||||||
out := g.multiplex.layers[g.layer].edges[uid]
|
out := g.multiplex.layers[g.layer].edges[uid]
|
||||||
nodes := make([]graph.Node, len(out))
|
nodes := make([]graph.Node, len(out))
|
||||||
for i, vid := range out {
|
for i, vid := range out {
|
||||||
nodes[i] = g.multiplex.nodes[vid]
|
nodes[i] = g.multiplex.nodes[vid]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -597,7 +603,7 @@ type undirectedMultiplexLocalMover struct {
|
|||||||
// node IDs of g must be contiguous in [0,n) where n is the number of nodes.
|
// node IDs of g must be contiguous in [0,n) where n is the number of nodes.
|
||||||
// If g has a zero edge weight sum, nil is returned.
|
// If g has a zero edge weight sum, nil is returned.
|
||||||
func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *undirectedMultiplexLocalMover {
|
func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities [][]graph.Node, weights, resolutions []float64, all bool) *undirectedMultiplexLocalMover {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
l := undirectedMultiplexLocalMover{
|
l := undirectedMultiplexLocalMover{
|
||||||
g: g,
|
g: g,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
@@ -637,8 +643,9 @@ func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities
|
|||||||
for _, u := range l.nodes {
|
for _, u := range l.nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
w := weight(uid, uid)
|
w := weight(uid, uid)
|
||||||
for _, v := range layer.From(uid) {
|
to := layer.From(uid)
|
||||||
w += weight(uid, v.ID())
|
for to.Next() {
|
||||||
|
w += weight(uid, to.Node().ID())
|
||||||
}
|
}
|
||||||
l.edgeWeightOf[i][uid] = w
|
l.edgeWeightOf[i][uid] = w
|
||||||
l.m2[i] += w
|
l.m2[i] += w
|
||||||
|
@@ -109,7 +109,7 @@ type edge struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error {
|
func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool) error {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
sort.Sort(ordered.ByID(nodes))
|
sort.Sort(ordered.ByID(nodes))
|
||||||
|
|
||||||
p.buf.WriteString(p.prefix)
|
p.buf.WriteString(p.prefix)
|
||||||
@@ -156,7 +156,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
|
|||||||
if s, ok := n.(Subgrapher); ok {
|
if s, ok := n.(Subgrapher); ok {
|
||||||
// If the node is not linked to any other node
|
// If the node is not linked to any other node
|
||||||
// the graph needs to be written now.
|
// the graph needs to be written now.
|
||||||
if len(g.From(n.ID())) == 0 {
|
if g.From(n.ID()).Len() == 0 {
|
||||||
g := s.Subgraph()
|
g := s.Subgraph()
|
||||||
_, subIsDirected := g.(graph.Directed)
|
_, subIsDirected := g.(graph.Directed)
|
||||||
if subIsDirected != isDirected {
|
if subIsDirected != isDirected {
|
||||||
@@ -188,7 +188,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
|
|||||||
havePrintedEdgeHeader := false
|
havePrintedEdgeHeader := false
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
nid := n.ID()
|
nid := n.ID()
|
||||||
to := g.From(nid)
|
to := graph.NodesOf(g.From(nid))
|
||||||
sort.Sort(ordered.ByID(to))
|
sort.Sort(ordered.ByID(to))
|
||||||
for _, t := range to {
|
for _, t := range to {
|
||||||
tid := t.ID()
|
tid := t.ID()
|
||||||
|
@@ -6,6 +6,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ func (g *GraphNode) has(id int64, visited map[int64]struct{}) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GraphNode) Nodes() []graph.Node {
|
func (g *GraphNode) Nodes() graph.Nodes {
|
||||||
toReturn := []graph.Node{g}
|
toReturn := []graph.Node{g}
|
||||||
visited := map[int64]struct{}{g.id: {}}
|
visited := map[int64]struct{}{g.id: {}}
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ func (g *GraphNode) Nodes() []graph.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return toReturn
|
return iterator.NewOrderedNodes(toReturn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph.Node {
|
func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph.Node {
|
||||||
@@ -131,9 +132,9 @@ func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GraphNode) From(id int64) []graph.Node {
|
func (g *GraphNode) From(id int64) graph.Nodes {
|
||||||
if id == g.ID() {
|
if id == g.ID() {
|
||||||
return g.neighbors
|
return iterator.NewOrderedNodes(g.neighbors)
|
||||||
}
|
}
|
||||||
|
|
||||||
visited := map[int64]struct{}{g.id: {}}
|
visited := map[int64]struct{}{g.id: {}}
|
||||||
@@ -141,7 +142,7 @@ func (g *GraphNode) From(id int64) []graph.Node {
|
|||||||
visited[root.ID()] = struct{}{}
|
visited[root.ID()] = struct{}{}
|
||||||
|
|
||||||
if result := root.findNeighbors(id, visited); result != nil {
|
if result := root.findNeighbors(id, visited); result != nil {
|
||||||
return result
|
return iterator.NewOrderedNodes(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +151,7 @@ func (g *GraphNode) From(id int64) []graph.Node {
|
|||||||
|
|
||||||
if gn, ok := neigh.(*GraphNode); ok {
|
if gn, ok := neigh.(*GraphNode); ok {
|
||||||
if result := gn.findNeighbors(id, visited); result != nil {
|
if result := gn.findNeighbors(id, visited); result != nil {
|
||||||
return result
|
return iterator.NewOrderedNodes(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,11 +32,11 @@ type Graph interface {
|
|||||||
Has(id int64) bool
|
Has(id int64) bool
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
Nodes() []Node
|
Nodes() Nodes
|
||||||
|
|
||||||
// From returns all nodes that can be reached directly
|
// From returns all nodes that can be reached directly
|
||||||
// from the node with the given ID.
|
// from the node with the given ID.
|
||||||
From(id int64) []Node
|
From(id int64) Nodes
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between
|
// HasEdgeBetween returns whether an edge exists between
|
||||||
// nodes with IDs xid and yid without considering direction.
|
// nodes with IDs xid and yid without considering direction.
|
||||||
@@ -99,7 +99,7 @@ type Directed interface {
|
|||||||
|
|
||||||
// To returns all nodes that can reach directly
|
// To returns all nodes that can reach directly
|
||||||
// to the node with the given ID.
|
// to the node with the given ID.
|
||||||
To(id int64) []Node
|
To(id int64) Nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedDirected is a weighted directed graph.
|
// WeightedDirected is a weighted directed graph.
|
||||||
@@ -113,7 +113,7 @@ type WeightedDirected interface {
|
|||||||
|
|
||||||
// To returns all nodes that can reach directly
|
// To returns all nodes that can reach directly
|
||||||
// to the node with the given ID.
|
// to the node with the given ID.
|
||||||
To(id int64) []Node
|
To(id int64) Nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeAdder is an interface for adding arbitrary nodes to a graph.
|
// NodeAdder is an interface for adding arbitrary nodes to a graph.
|
||||||
@@ -219,11 +219,15 @@ type DirectedWeightedBuilder interface {
|
|||||||
// be present in the destination after the copy is complete.
|
// be present in the destination after the copy is complete.
|
||||||
func Copy(dst Builder, src Graph) {
|
func Copy(dst Builder, src Graph) {
|
||||||
nodes := src.Nodes()
|
nodes := src.Nodes()
|
||||||
for _, n := range nodes {
|
for nodes.Next() {
|
||||||
dst.AddNode(n)
|
dst.AddNode(nodes.Node())
|
||||||
}
|
}
|
||||||
for _, u := range nodes {
|
nodes.Reset()
|
||||||
for _, v := range src.From(u.ID()) {
|
for nodes.Next() {
|
||||||
|
u := nodes.Node()
|
||||||
|
to := src.From(u.ID())
|
||||||
|
for to.Next() {
|
||||||
|
v := to.Node()
|
||||||
dst.SetEdge(dst.NewEdge(u, v))
|
dst.SetEdge(dst.NewEdge(u, v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,11 +246,15 @@ func Copy(dst Builder, src Graph) {
|
|||||||
// to resolve such conflicts, an UndirectWeighted may be used to do this.
|
// to resolve such conflicts, an UndirectWeighted may be used to do this.
|
||||||
func CopyWeighted(dst WeightedBuilder, src Weighted) {
|
func CopyWeighted(dst WeightedBuilder, src Weighted) {
|
||||||
nodes := src.Nodes()
|
nodes := src.Nodes()
|
||||||
for _, n := range nodes {
|
for nodes.Next() {
|
||||||
dst.AddNode(n)
|
dst.AddNode(nodes.Node())
|
||||||
}
|
}
|
||||||
for _, u := range nodes {
|
nodes.Reset()
|
||||||
for _, v := range src.From(u.ID()) {
|
for nodes.Next() {
|
||||||
|
u := nodes.Node()
|
||||||
|
to := src.From(u.ID())
|
||||||
|
for to.Next() {
|
||||||
|
v := to.Node()
|
||||||
dst.SetWeightedEdge(dst.NewWeightedEdge(u, v, src.WeightedEdge(u.ID(), v.ID()).Weight()))
|
dst.SetWeightedEdge(dst.NewWeightedEdge(u, v, src.WeightedEdge(u.ID(), v.ID()).Weight()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -262,8 +262,8 @@ func TestCopyWeighted(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func same(a, b graph.Graph) bool {
|
func same(a, b graph.Graph) bool {
|
||||||
aNodes := a.Nodes()
|
aNodes := graph.NodesOf(a.Nodes())
|
||||||
bNodes := b.Nodes()
|
bNodes := graph.NodesOf(b.Nodes())
|
||||||
sort.Sort(ordered.ByID(aNodes))
|
sort.Sort(ordered.ByID(aNodes))
|
||||||
sort.Sort(ordered.ByID(bNodes))
|
sort.Sort(ordered.ByID(bNodes))
|
||||||
for i, na := range aNodes {
|
for i, na := range aNodes {
|
||||||
@@ -272,9 +272,9 @@ func same(a, b graph.Graph) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, u := range a.Nodes() {
|
for _, u := range graph.NodesOf(a.Nodes()) {
|
||||||
aFromU := a.From(u.ID())
|
aFromU := graph.NodesOf(a.From(u.ID()))
|
||||||
bFromU := b.From(u.ID())
|
bFromU := graph.NodesOf(b.From(u.ID()))
|
||||||
if len(aFromU) != len(bFromU) {
|
if len(aFromU) != len(bFromU) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@@ -185,15 +185,16 @@ func TestPowerLawUndirected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := g.Nodes()
|
||||||
if len(nodes) != n {
|
if nodes.Len() != n {
|
||||||
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
|
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range nodes {
|
for nodes.Next() {
|
||||||
|
u := nodes.Node()
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
var lines int
|
var lines int
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
lines += len(g.Lines(uid, v.ID()))
|
lines += g.Lines(uid, v.ID()).Len()
|
||||||
}
|
}
|
||||||
if lines < d {
|
if lines < d {
|
||||||
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
||||||
@@ -214,15 +215,16 @@ func TestPowerLawDirected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := g.Nodes()
|
||||||
if len(nodes) != n {
|
if nodes.Len() != n {
|
||||||
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
|
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range nodes {
|
for nodes.Next() {
|
||||||
|
u := nodes.Node()
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
var lines int
|
var lines int
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
lines += len(g.Lines(uid, v.ID()))
|
lines += g.Lines(uid, v.ID()).Len()
|
||||||
}
|
}
|
||||||
if lines < d {
|
if lines < d {
|
||||||
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
||||||
@@ -243,8 +245,8 @@ func TestBipartitePowerLawUndirected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := g.Nodes()
|
||||||
if len(nodes) != 2*n {
|
if nodes.Len() != 2*n {
|
||||||
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
|
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
|
||||||
}
|
}
|
||||||
if len(p1) != n {
|
if len(p1) != n {
|
||||||
t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1))
|
t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1))
|
||||||
@@ -266,11 +268,12 @@ func TestBipartitePowerLawUndirected(t *testing.T) {
|
|||||||
t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o))
|
t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range nodes {
|
for nodes.Next() {
|
||||||
|
u := nodes.Node()
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
var lines int
|
var lines int
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
lines += len(g.Lines(uid, v.ID()))
|
lines += g.Lines(uid, v.ID()).Len()
|
||||||
}
|
}
|
||||||
if lines < d {
|
if lines < d {
|
||||||
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
||||||
@@ -291,8 +294,8 @@ func TestBipartitePowerLawDirected(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := g.Nodes()
|
||||||
if len(nodes) != 2*n {
|
if nodes.Len() != 2*n {
|
||||||
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, len(nodes))
|
t.Errorf("unexpected number of nodes in graph: n=%d, d=%d: got:%d", n, d, nodes.Len())
|
||||||
}
|
}
|
||||||
if len(p1) != n {
|
if len(p1) != n {
|
||||||
t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1))
|
t.Errorf("unexpected number of nodes in p1: n=%d, d=%d: got:%d", n, d, len(p1))
|
||||||
@@ -314,11 +317,12 @@ func TestBipartitePowerLawDirected(t *testing.T) {
|
|||||||
t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o))
|
t.Errorf("unexpected overlap in partition membership: n=%d, d=%d: got:%d", n, d, len(o))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range nodes {
|
for nodes.Next() {
|
||||||
|
u := nodes.Node()
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
var lines int
|
var lines int
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
lines += len(g.Lines(uid, v.ID()))
|
lines += g.Lines(uid, v.ID()).Len()
|
||||||
}
|
}
|
||||||
if lines < d {
|
if lines < d {
|
||||||
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines)
|
||||||
|
@@ -56,7 +56,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
|
|||||||
rndN = r.Intn
|
rndN = r.Intn
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := dst.Nodes()
|
nodes := graph.NodesOf(dst.Nodes())
|
||||||
sort.Sort(ordered.ByID(nodes))
|
sort.Sort(ordered.ByID(nodes))
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
n--
|
n--
|
||||||
@@ -76,7 +76,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
|
|||||||
// into the rest of the graph.
|
// into the rest of the graph.
|
||||||
for {
|
for {
|
||||||
// Add edges to parent's neighbours.
|
// Add edges to parent's neighbours.
|
||||||
to := dst.From(u.ID())
|
to := graph.NodesOf(dst.From(u.ID()))
|
||||||
sort.Sort(ordered.ByID(to))
|
sort.Sort(ordered.ByID(to))
|
||||||
for _, v := range to {
|
for _, v := range to {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
@@ -119,7 +119,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dst.From(did)) != 0 {
|
if dst.From(did).Len() != 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -69,7 +69,7 @@ func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64
|
|||||||
for i := 0; i < m; i++ {
|
for i := 0; i < m; i++ {
|
||||||
// Triad formation.
|
// Triad formation.
|
||||||
if i != 0 && rnd() < p {
|
if i != 0 && rnd() < p {
|
||||||
for _, w := range permute(dst.From(int64(u)), rndN) {
|
for _, w := range permute(graph.NodesOf(dst.From(int64(u))), rndN) {
|
||||||
wid := w.ID()
|
wid := w.ID()
|
||||||
if wid == int64(v) || dst.HasEdgeBetween(wid, int64(v)) {
|
if wid == int64(v) || dst.HasEdgeBetween(wid, int64(v)) {
|
||||||
continue
|
continue
|
||||||
|
9
graph/iterator/doc.go
Normal file
9
graph/iterator/doc.go
Normal 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
131
graph/iterator/edges.go
Normal 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
|
||||||
|
}
|
114
graph/iterator/edges_test.go
Normal file
114
graph/iterator/edges_test.go
Normal 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
131
graph/iterator/lines.go
Normal 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
|
||||||
|
}
|
116
graph/iterator/lines_test.go
Normal file
116
graph/iterator/lines_test.go
Normal 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
115
graph/iterator/nodes.go
Normal 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
|
||||||
|
}
|
99
graph/iterator/nodes_test.go
Normal file
99
graph/iterator/nodes_test.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -154,7 +155,7 @@ func (g *DirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *DirectedGraph) Nodes() []graph.Node {
|
func (g *DirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -164,12 +165,12 @@ func (g *DirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph. Each edge in the returned slice
|
// Edges returns all the edges in the graph. Each edge in the returned slice
|
||||||
// is a multi.Edge.
|
// is a multi.Edge.
|
||||||
func (g *DirectedGraph) Edges() []graph.Edge {
|
func (g *DirectedGraph) Edges() graph.Edges {
|
||||||
var edges []graph.Edge
|
var edges []graph.Edge
|
||||||
for _, u := range g.nodes {
|
for _, u := range g.nodes {
|
||||||
for _, e := range g.from[u.ID()] {
|
for _, e := range g.from[u.ID()] {
|
||||||
@@ -182,11 +183,11 @@ func (g *DirectedGraph) Edges() []graph.Edge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *DirectedGraph) From(id int64) []graph.Node {
|
func (g *DirectedGraph) From(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -197,11 +198,11 @@ func (g *DirectedGraph) From(id int64) []graph.Node {
|
|||||||
from[i] = g.nodes[vid]
|
from[i] = g.nodes[vid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return from
|
return iterator.NewOrderedNodes(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to n.
|
// To returns all nodes in g that can reach directly to n.
|
||||||
func (g *DirectedGraph) To(id int64) []graph.Node {
|
func (g *DirectedGraph) To(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -212,7 +213,7 @@ func (g *DirectedGraph) To(id int64) []graph.Node {
|
|||||||
to[i] = g.nodes[uid]
|
to[i] = g.nodes[uid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return to
|
return iterator.NewOrderedNodes(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
||||||
@@ -229,7 +230,7 @@ func (g *DirectedGraph) HasEdgeBetween(xid, yid int64) bool {
|
|||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
// The returned graph.Edge is a multi.Edge if an edge exists.
|
// The returned graph.Edge is a multi.Edge if an edge exists.
|
||||||
func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge {
|
func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge {
|
||||||
lines := g.Lines(uid, vid)
|
lines := graph.LinesOf(g.Lines(uid, vid))
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -238,7 +239,7 @@ func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge {
|
|||||||
|
|
||||||
// Lines returns the lines from u to v if such any such lines exists and nil otherwise.
|
// Lines returns the lines from u to v if such any such lines exists and nil otherwise.
|
||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
func (g *DirectedGraph) Lines(uid, vid int64) []graph.Line {
|
func (g *DirectedGraph) Lines(uid, vid int64) graph.Lines {
|
||||||
edge := g.from[uid][vid]
|
edge := g.from[uid][vid]
|
||||||
if len(edge) == 0 {
|
if len(edge) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -247,7 +248,7 @@ func (g *DirectedGraph) Lines(uid, vid int64) []graph.Line {
|
|||||||
for _, l := range edge {
|
for _, l := range edge {
|
||||||
lines = append(lines, l)
|
lines = append(lines, l)
|
||||||
}
|
}
|
||||||
return lines
|
return iterator.NewOrderedLines(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeFromTo returns whether an edge exists in the graph from u to v.
|
// HasEdgeFromTo returns whether an edge exists in the graph from u to v.
|
||||||
@@ -255,18 +256,3 @@ func (g *DirectedGraph) HasEdgeFromTo(uid, vid int64) bool {
|
|||||||
_, ok := g.from[uid][vid]
|
_, ok := g.from[uid][vid]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the in+out degree of n in g.
|
|
||||||
func (g *DirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var deg int
|
|
||||||
for _, e := range g.from[id] {
|
|
||||||
deg += len(e)
|
|
||||||
}
|
|
||||||
for _, e := range g.to[id] {
|
|
||||||
deg += len(e)
|
|
||||||
}
|
|
||||||
return deg
|
|
||||||
}
|
|
||||||
|
@@ -21,7 +21,7 @@ var (
|
|||||||
func TestEdgeOvercounting(t *testing.T) {
|
func TestEdgeOvercounting(t *testing.T) {
|
||||||
g := generateDummyGraph()
|
g := generateDummyGraph()
|
||||||
|
|
||||||
if neigh := g.From(int64(2)); len(neigh) != 2 {
|
if neigh := graph.NodesOf(g.From(int64(2))); len(neigh) != 2 {
|
||||||
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -143,7 +144,7 @@ func (g *UndirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *UndirectedGraph) Nodes() []graph.Node {
|
func (g *UndirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -153,12 +154,12 @@ func (g *UndirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph. Each edge in the returned slice
|
// Edges returns all the edges in the graph. Each edge in the returned slice
|
||||||
// is a multi.Edge.
|
// is a multi.Edge.
|
||||||
func (g *UndirectedGraph) Edges() []graph.Edge {
|
func (g *UndirectedGraph) Edges() graph.Edges {
|
||||||
if len(g.lines) == 0 {
|
if len(g.lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -180,11 +181,11 @@ func (g *UndirectedGraph) Edges() []graph.Edge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *UndirectedGraph) From(id int64) []graph.Node {
|
func (g *UndirectedGraph) From(id int64) graph.Nodes {
|
||||||
if !g.Has(id) {
|
if !g.Has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -195,7 +196,7 @@ func (g *UndirectedGraph) From(id int64) []graph.Node {
|
|||||||
nodes[i] = g.nodes[from]
|
nodes[i] = g.nodes[from]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -213,7 +214,7 @@ func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
|
|||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
// The returned graph.Edge is a multi.Edge if an edge exists.
|
// The returned graph.Edge is a multi.Edge if an edge exists.
|
||||||
func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge {
|
func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge {
|
||||||
lines := g.LinesBetween(uid, vid)
|
lines := graph.LinesOf(g.LinesBetween(uid, vid))
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -222,27 +223,15 @@ func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge {
|
|||||||
|
|
||||||
// Lines returns the lines from u to v if such an edge exists and nil otherwise.
|
// Lines returns the lines from u to v if such an edge exists and nil otherwise.
|
||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
func (g *UndirectedGraph) Lines(uid, vid int64) []graph.Line {
|
func (g *UndirectedGraph) Lines(uid, vid int64) graph.Lines {
|
||||||
return g.LinesBetween(uid, vid)
|
return g.LinesBetween(uid, vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinesBetween returns the lines between nodes x and y.
|
// LinesBetween returns the lines between nodes x and y.
|
||||||
func (g *UndirectedGraph) LinesBetween(xid, yid int64) []graph.Line {
|
func (g *UndirectedGraph) LinesBetween(xid, yid int64) graph.Lines {
|
||||||
var lines []graph.Line
|
var lines []graph.Line
|
||||||
for _, l := range g.lines[xid][yid] {
|
for _, l := range g.lines[xid][yid] {
|
||||||
lines = append(lines, l)
|
lines = append(lines, l)
|
||||||
}
|
}
|
||||||
return lines
|
return iterator.NewOrderedLines(lines)
|
||||||
}
|
|
||||||
|
|
||||||
// Degree returns the degree of n in g.
|
|
||||||
func (g *UndirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var deg int
|
|
||||||
for _, e := range g.lines[id] {
|
|
||||||
deg += len(e)
|
|
||||||
}
|
|
||||||
return deg
|
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -160,7 +161,7 @@ func (g *WeightedDirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *WeightedDirectedGraph) Nodes() []graph.Node {
|
func (g *WeightedDirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -171,12 +172,12 @@ func (g *WeightedDirectedGraph) Nodes() []graph.Node {
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph. Each edge in the returned slice
|
// Edges returns all the edges in the graph. Each edge in the returned slice
|
||||||
// is a multi.WeightedEdge.
|
// is a multi.WeightedEdge.
|
||||||
func (g *WeightedDirectedGraph) Edges() []graph.Edge {
|
func (g *WeightedDirectedGraph) Edges() graph.Edges {
|
||||||
var edges []graph.Edge
|
var edges []graph.Edge
|
||||||
for _, u := range g.nodes {
|
for _, u := range g.nodes {
|
||||||
for _, e := range g.from[u.ID()] {
|
for _, e := range g.from[u.ID()] {
|
||||||
@@ -190,12 +191,12 @@ func (g *WeightedDirectedGraph) Edges() []graph.Edge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedEdges returns all the edges in the graph. Each edge in the returned slice
|
// WeightedEdges returns all the edges in the graph. Each edge in the returned slice
|
||||||
// is a multi.WeightedEdge.
|
// is a multi.WeightedEdge.
|
||||||
func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge {
|
func (g *WeightedDirectedGraph) WeightedEdges() graph.WeightedEdges {
|
||||||
var edges []graph.WeightedEdge
|
var edges []graph.WeightedEdge
|
||||||
for _, u := range g.nodes {
|
for _, u := range g.nodes {
|
||||||
for _, e := range g.from[u.ID()] {
|
for _, e := range g.from[u.ID()] {
|
||||||
@@ -209,11 +210,11 @@ func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedWeightedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
|
func (g *WeightedDirectedGraph) From(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -224,11 +225,11 @@ func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
|
|||||||
from[i] = g.nodes[vid]
|
from[i] = g.nodes[vid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return from
|
return iterator.NewOrderedNodes(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to n.
|
// To returns all nodes in g that can reach directly to n.
|
||||||
func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
|
func (g *WeightedDirectedGraph) To(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -239,7 +240,7 @@ func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
|
|||||||
to[i] = g.nodes[uid]
|
to[i] = g.nodes[uid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return to
|
return iterator.NewOrderedNodes(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
||||||
@@ -271,7 +272,7 @@ func (g *WeightedDirectedGraph) Edge(uid, vid int64) graph.Edge {
|
|||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
// The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
|
// The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
|
||||||
func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
|
func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
|
||||||
lines := g.WeightedLines(uid, vid)
|
lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -280,7 +281,7 @@ func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge
|
|||||||
|
|
||||||
// Lines returns the lines from u to v if such any such lines exists and nil otherwise.
|
// Lines returns the lines from u to v if such any such lines exists and nil otherwise.
|
||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
func (g *WeightedDirectedGraph) Lines(uid, vid int64) []graph.Line {
|
func (g *WeightedDirectedGraph) Lines(uid, vid int64) graph.Lines {
|
||||||
edge := g.from[uid][vid]
|
edge := g.from[uid][vid]
|
||||||
if len(edge) == 0 {
|
if len(edge) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -289,12 +290,12 @@ func (g *WeightedDirectedGraph) Lines(uid, vid int64) []graph.Line {
|
|||||||
for _, l := range edge {
|
for _, l := range edge {
|
||||||
lines = append(lines, l)
|
lines = append(lines, l)
|
||||||
}
|
}
|
||||||
return lines
|
return iterator.NewOrderedLines(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedLines returns the weighted lines from u to v if such any such lines exists
|
// WeightedLines returns the weighted lines from u to v if such any such lines exists
|
||||||
// and nil otherwise. The node v must be directly reachable from u as defined by the From method.
|
// and nil otherwise. The node v must be directly reachable from u as defined by the From method.
|
||||||
func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLine {
|
func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) graph.WeightedLines {
|
||||||
edge := g.from[uid][vid]
|
edge := g.from[uid][vid]
|
||||||
if len(edge) == 0 {
|
if len(edge) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -303,27 +304,12 @@ func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLi
|
|||||||
for _, l := range edge {
|
for _, l := range edge {
|
||||||
lines = append(lines, l)
|
lines = append(lines, l)
|
||||||
}
|
}
|
||||||
return lines
|
return iterator.NewOrderedWeightedLines(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weight returns the weight for the lines between x and y summarised by the receiver's
|
// Weight returns the weight for the lines between x and y summarised by the receiver's
|
||||||
// EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
|
// EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
|
||||||
func (g *WeightedDirectedGraph) Weight(uid, vid int64) (w float64, ok bool) {
|
func (g *WeightedDirectedGraph) Weight(uid, vid int64) (w float64, ok bool) {
|
||||||
lines := g.WeightedLines(uid, vid)
|
lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
|
||||||
return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0
|
return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the in+out degree of n in g.
|
|
||||||
func (g *WeightedDirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var deg int
|
|
||||||
for _, e := range g.from[id] {
|
|
||||||
deg += len(e)
|
|
||||||
}
|
|
||||||
for _, e := range g.to[id] {
|
|
||||||
deg += len(e)
|
|
||||||
}
|
|
||||||
return deg
|
|
||||||
}
|
|
||||||
|
@@ -22,7 +22,7 @@ var (
|
|||||||
func TestWeightedEdgeOvercounting(t *testing.T) {
|
func TestWeightedEdgeOvercounting(t *testing.T) {
|
||||||
g := generateDummyWeightedGraph()
|
g := generateDummyWeightedGraph()
|
||||||
|
|
||||||
if neigh := g.From(int64(2)); len(neigh) != 2 {
|
if neigh := graph.NodesOf(g.From(int64(2))); len(neigh) != 2 {
|
||||||
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -147,7 +148,7 @@ func (g *WeightedUndirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
|
func (g *WeightedUndirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -157,12 +158,12 @@ func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph. Each edge in the returned slice
|
// Edges returns all the edges in the graph. Each edge in the returned slice
|
||||||
// is a multi.Edge.
|
// is a multi.Edge.
|
||||||
func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
|
func (g *WeightedUndirectedGraph) Edges() graph.Edges {
|
||||||
if len(g.lines) == 0 {
|
if len(g.lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -184,11 +185,11 @@ func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
|
func (g *WeightedUndirectedGraph) From(id int64) graph.Nodes {
|
||||||
if !g.Has(id) {
|
if !g.Has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -199,7 +200,7 @@ func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
|
|||||||
nodes[i] = g.nodes[from]
|
nodes[i] = g.nodes[from]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -210,12 +211,12 @@ func (g *WeightedUndirectedGraph) HasEdgeBetween(xid, yid int64) bool {
|
|||||||
|
|
||||||
// Lines returns the lines from u to v if such an edge exists and nil otherwise.
|
// Lines returns the lines from u to v if such an edge exists and nil otherwise.
|
||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
func (g *WeightedUndirectedGraph) Lines(uid, vid int64) []graph.Line {
|
func (g *WeightedUndirectedGraph) Lines(uid, vid int64) graph.Lines {
|
||||||
return g.LinesBetween(uid, vid)
|
return g.LinesBetween(uid, vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinesBetween returns the lines between nodes x and y.
|
// LinesBetween returns the lines between nodes x and y.
|
||||||
func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) []graph.Line {
|
func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) graph.Lines {
|
||||||
edge := g.lines[xid][yid]
|
edge := g.lines[xid][yid]
|
||||||
if len(edge) == 0 {
|
if len(edge) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -230,7 +231,7 @@ func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) []graph.Line {
|
|||||||
seen[lid] = struct{}{}
|
seen[lid] = struct{}{}
|
||||||
lines = append(lines, l)
|
lines = append(lines, l)
|
||||||
}
|
}
|
||||||
return lines
|
return iterator.NewOrderedLines(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edge returns the edge from u to v if such an edge exists and nil otherwise.
|
// Edge returns the edge from u to v if such an edge exists and nil otherwise.
|
||||||
@@ -249,7 +250,7 @@ func (g *WeightedUndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
|
|||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
// The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
|
// The returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists.
|
||||||
func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
|
func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge {
|
||||||
lines := g.WeightedLines(uid, vid)
|
lines := graph.WeightedLinesOf(g.WeightedLines(uid, vid))
|
||||||
if len(lines) == 0 {
|
if len(lines) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -263,12 +264,12 @@ func (g *WeightedUndirectedGraph) WeightedEdgeBetween(xid, yid int64) graph.Weig
|
|||||||
|
|
||||||
// WeightedLines returns the lines from u to v if such an edge exists and nil otherwise.
|
// WeightedLines returns the lines from u to v if such an edge exists and nil otherwise.
|
||||||
// The node v must be directly reachable from u as defined by the From method.
|
// The node v must be directly reachable from u as defined by the From method.
|
||||||
func (g *WeightedUndirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLine {
|
func (g *WeightedUndirectedGraph) WeightedLines(uid, vid int64) graph.WeightedLines {
|
||||||
return g.WeightedLinesBetween(uid, vid)
|
return g.WeightedLinesBetween(uid, vid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedLinesBetween returns the lines between nodes x and y.
|
// WeightedLinesBetween returns the lines between nodes x and y.
|
||||||
func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) []graph.WeightedLine {
|
func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) graph.WeightedLines {
|
||||||
edge := g.lines[xid][yid]
|
edge := g.lines[xid][yid]
|
||||||
if len(edge) == 0 {
|
if len(edge) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -283,24 +284,12 @@ func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) []graph.W
|
|||||||
seen[lid] = struct{}{}
|
seen[lid] = struct{}{}
|
||||||
lines = append(lines, l)
|
lines = append(lines, l)
|
||||||
}
|
}
|
||||||
return lines
|
return iterator.NewOrderedWeightedLines(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weight returns the weight for the lines between x and y summarised by the receiver's
|
// Weight returns the weight for the lines between x and y summarised by the receiver's
|
||||||
// EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
|
// EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise.
|
||||||
func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
|
func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
|
||||||
lines := g.WeightedLines(xid, yid)
|
lines := graph.WeightedLinesOf(g.WeightedLines(xid, yid))
|
||||||
return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0
|
return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the degree of n in g.
|
|
||||||
func (g *WeightedUndirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var deg int
|
|
||||||
for _, e := range g.lines[id] {
|
|
||||||
deg += len(e)
|
|
||||||
}
|
|
||||||
return deg
|
|
||||||
}
|
|
||||||
|
@@ -24,11 +24,11 @@ type Multigraph interface {
|
|||||||
Has(id int64) bool
|
Has(id int64) bool
|
||||||
|
|
||||||
// Nodes returns all the nodes in the multigraph.
|
// Nodes returns all the nodes in the multigraph.
|
||||||
Nodes() []Node
|
Nodes() Nodes
|
||||||
|
|
||||||
// From returns all nodes that can be reached directly
|
// From returns all nodes that can be reached directly
|
||||||
// from the node with the given ID.
|
// from the node with the given ID.
|
||||||
From(id int64) []Node
|
From(id int64) Nodes
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between
|
// HasEdgeBetween returns whether an edge exists between
|
||||||
// nodes with IDs xid and yid without considering direction.
|
// nodes with IDs xid and yid without considering direction.
|
||||||
@@ -38,7 +38,7 @@ type Multigraph interface {
|
|||||||
// vid, if any such lines exist and nil otherwise. The
|
// vid, if any such lines exist and nil otherwise. The
|
||||||
// node v must be directly reachable from u as defined by
|
// node v must be directly reachable from u as defined by
|
||||||
// the From method.
|
// the From method.
|
||||||
Lines(uid, vid int64) []Line
|
Lines(uid, vid int64) Lines
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedMultigraph is a weighted multigraph.
|
// WeightedMultigraph is a weighted multigraph.
|
||||||
@@ -49,7 +49,7 @@ type WeightedMultigraph interface {
|
|||||||
// with IDs uid and vid if any such lines exist and nil
|
// with IDs uid and vid if any such lines exist and nil
|
||||||
// otherwise. The node v must be directly reachable
|
// otherwise. The node v must be directly reachable
|
||||||
// from u as defined by the From method.
|
// from u as defined by the From method.
|
||||||
WeightedLines(uid, vid int64) []WeightedLine
|
WeightedLines(uid, vid int64) WeightedLines
|
||||||
}
|
}
|
||||||
|
|
||||||
// UndirectedMultigraph is an undirected multigraph.
|
// UndirectedMultigraph is an undirected multigraph.
|
||||||
@@ -58,7 +58,7 @@ type UndirectedMultigraph interface {
|
|||||||
|
|
||||||
// LinesBetween returns the lines between nodes x and y
|
// LinesBetween returns the lines between nodes x and y
|
||||||
// with IDs xid and yid.
|
// with IDs xid and yid.
|
||||||
LinesBetween(xid, yid int64) []Line
|
LinesBetween(xid, yid int64) Lines
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedUndirectedMultigraph is a weighted undirected multigraph.
|
// WeightedUndirectedMultigraph is a weighted undirected multigraph.
|
||||||
@@ -67,7 +67,7 @@ type WeightedUndirectedMultigraph interface {
|
|||||||
|
|
||||||
// WeightedLinesBetween returns the lines between nodes
|
// WeightedLinesBetween returns the lines between nodes
|
||||||
// x and y with IDs xid and yid.
|
// x and y with IDs xid and yid.
|
||||||
WeightedLinesBetween(xid, yid int64) []WeightedLine
|
WeightedLinesBetween(xid, yid int64) WeightedLines
|
||||||
}
|
}
|
||||||
|
|
||||||
// DirectedMultigraph is a directed multigraph.
|
// DirectedMultigraph is a directed multigraph.
|
||||||
@@ -81,7 +81,7 @@ type DirectedMultigraph interface {
|
|||||||
|
|
||||||
// To returns all nodes that can reach directly
|
// To returns all nodes that can reach directly
|
||||||
// to the node with the given ID.
|
// to the node with the given ID.
|
||||||
To(id int64) []Node
|
To(id int64) Nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedDirectedMultigraph is a weighted directed multigraph.
|
// WeightedDirectedMultigraph is a weighted directed multigraph.
|
||||||
@@ -95,7 +95,7 @@ type WeightedDirectedMultigraph interface {
|
|||||||
|
|
||||||
// To returns all nodes that can reach directly
|
// To returns all nodes that can reach directly
|
||||||
// to the node with the given ID.
|
// to the node with the given ID.
|
||||||
To(id int64) []Node
|
To(id int64) Nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// LineAdder is an interface for adding lines to a multigraph.
|
// LineAdder is an interface for adding lines to a multigraph.
|
||||||
|
@@ -90,7 +90,7 @@ func EdgeBetweenness(g graph.Graph) map[[2]int64]float64 {
|
|||||||
// the accumulation loop provided by the accumulate closure.
|
// the accumulation loop provided by the accumulate closure.
|
||||||
func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack, p map[int64][]graph.Node, delta, sigma map[int64]float64)) {
|
func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack, p map[int64][]graph.Node, delta, sigma map[int64]float64)) {
|
||||||
var (
|
var (
|
||||||
nodes = g.Nodes()
|
nodes = graph.NodesOf(g.Nodes())
|
||||||
stack linear.NodeStack
|
stack linear.NodeStack
|
||||||
p = make(map[int64][]graph.Node, len(nodes))
|
p = make(map[int64][]graph.Node, len(nodes))
|
||||||
sigma = make(map[int64]float64, len(nodes))
|
sigma = make(map[int64]float64, len(nodes))
|
||||||
@@ -117,7 +117,7 @@ func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack
|
|||||||
v := queue.Dequeue()
|
v := queue.Dequeue()
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
stack.Push(v)
|
stack.Push(v)
|
||||||
for _, w := range g.From(vid) {
|
for _, w := range graph.NodesOf(g.From(vid)) {
|
||||||
wid := w.ID()
|
wid := w.ID()
|
||||||
// w found for the first time?
|
// w found for the first time?
|
||||||
if d[wid] < 0 {
|
if d[wid] < 0 {
|
||||||
@@ -151,7 +151,7 @@ func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack
|
|||||||
func BetweennessWeighted(g graph.Weighted, p path.AllShortest) map[int64]float64 {
|
func BetweennessWeighted(g graph.Weighted, p path.AllShortest) map[int64]float64 {
|
||||||
cb := make(map[int64]float64)
|
cb := make(map[int64]float64)
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
for i, s := range nodes {
|
for i, s := range nodes {
|
||||||
sid := s.ID()
|
sid := s.ID()
|
||||||
for j, t := range nodes {
|
for j, t := range nodes {
|
||||||
@@ -205,7 +205,7 @@ func EdgeBetweennessWeighted(g graph.Weighted, p path.AllShortest) map[[2]int64]
|
|||||||
cb := make(map[[2]int64]float64)
|
cb := make(map[[2]int64]float64)
|
||||||
|
|
||||||
_, isUndirected := g.(graph.Undirected)
|
_, isUndirected := g.(graph.Undirected)
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
for i, s := range nodes {
|
for i, s := range nodes {
|
||||||
sid := s.ID()
|
sid := s.ID()
|
||||||
for j, t := range nodes {
|
for j, t := range nodes {
|
||||||
|
@@ -113,7 +113,7 @@ type Laplacian struct {
|
|||||||
// degree of each node and A is the graph adjacency matrix of the input graph.
|
// degree of each node and A is the graph adjacency matrix of the input graph.
|
||||||
// If g contains self edges, NewLaplacian will panic.
|
// If g contains self edges, NewLaplacian will panic.
|
||||||
func NewLaplacian(g graph.Undirected) Laplacian {
|
func NewLaplacian(g graph.Undirected) Laplacian {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
@@ -123,7 +123,7 @@ func NewLaplacian(g graph.Undirected) Laplacian {
|
|||||||
l := mat.NewSymDense(len(nodes), nil)
|
l := mat.NewSymDense(len(nodes), nil)
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
to := g.From(uid)
|
to := graph.NodesOf(g.From(uid))
|
||||||
l.SetSym(j, j, float64(len(to)))
|
l.SetSym(j, j, float64(len(to)))
|
||||||
for _, v := range to {
|
for _, v := range to {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
@@ -146,7 +146,7 @@ func NewLaplacian(g graph.Undirected) Laplacian {
|
|||||||
// matrix of the input graph.
|
// matrix of the input graph.
|
||||||
// If g contains self edges, NewSymNormLaplacian will panic.
|
// If g contains self edges, NewSymNormLaplacian will panic.
|
||||||
func NewSymNormLaplacian(g graph.Undirected) Laplacian {
|
func NewSymNormLaplacian(g graph.Undirected) Laplacian {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
@@ -156,7 +156,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
|
|||||||
l := mat.NewSymDense(len(nodes), nil)
|
l := mat.NewSymDense(len(nodes), nil)
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
to := g.From(uid)
|
to := graph.NodesOf(g.From(uid))
|
||||||
if len(to) == 0 {
|
if len(to) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
|
|||||||
panic("network: self edge in graph")
|
panic("network: self edge in graph")
|
||||||
}
|
}
|
||||||
if uid < vid {
|
if uid < vid {
|
||||||
l.SetSym(indexOf[vid], j, -1/(squdeg*math.Sqrt(float64(len(g.From(vid))))))
|
l.SetSym(indexOf[vid], j, -1/(squdeg*math.Sqrt(float64(g.From(vid).Len()))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian {
|
|||||||
// graph.
|
// graph.
|
||||||
// If g contains self edges, NewRandomWalkLaplacian will panic.
|
// If g contains self edges, NewRandomWalkLaplacian will panic.
|
||||||
func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
|
func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
@@ -193,7 +193,7 @@ func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian {
|
|||||||
l := mat.NewDense(len(nodes), len(nodes), nil)
|
l := mat.NewDense(len(nodes), len(nodes), nil)
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
to := g.From(uid)
|
to := graph.NodesOf(g.From(uid))
|
||||||
if len(to) == 0 {
|
if len(to) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/floats"
|
"gonum.org/v1/gonum/floats"
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
"gonum.org/v1/gonum/mat"
|
"gonum.org/v1/gonum/mat"
|
||||||
)
|
)
|
||||||
@@ -304,10 +305,10 @@ type sortedNodeGraph struct {
|
|||||||
graph.Graph
|
graph.Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g sortedNodeGraph) Nodes() []graph.Node {
|
func (g sortedNodeGraph) Nodes() graph.Nodes {
|
||||||
n := g.Graph.Nodes()
|
n := graph.NodesOf(g.Graph.Nodes())
|
||||||
sort.Sort(ordered.ByID(n))
|
sort.Sort(ordered.ByID(n))
|
||||||
return n
|
return iterator.NewOrderedNodes(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
var diffuseToEquilibriumTests = []struct {
|
var diffuseToEquilibriumTests = []struct {
|
||||||
|
@@ -19,7 +19,7 @@ import (
|
|||||||
// For directed graphs the incoming paths are used. Infinite distances are
|
// For directed graphs the incoming paths are used. Infinite distances are
|
||||||
// not considered.
|
// not considered.
|
||||||
func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
c := make(map[int64]float64, len(nodes))
|
c := make(map[int64]float64, len(nodes))
|
||||||
for _, u := range nodes {
|
for _, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
@@ -48,7 +48,7 @@ func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
|||||||
// For directed graphs the incoming paths are used. Infinite distances are
|
// For directed graphs the incoming paths are used. Infinite distances are
|
||||||
// not considered.
|
// not considered.
|
||||||
func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
f := make(map[int64]float64, len(nodes))
|
f := make(map[int64]float64, len(nodes))
|
||||||
for _, u := range nodes {
|
for _, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
@@ -77,7 +77,7 @@ func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
|||||||
// For directed graphs the incoming paths are used. Infinite distances are
|
// For directed graphs the incoming paths are used. Infinite distances are
|
||||||
// not considered.
|
// not considered.
|
||||||
func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
h := make(map[int64]float64, len(nodes))
|
h := make(map[int64]float64, len(nodes))
|
||||||
for i, u := range nodes {
|
for i, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
@@ -108,7 +108,7 @@ func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
|||||||
// For directed graphs the incoming paths are used. Infinite distances are
|
// For directed graphs the incoming paths are used. Infinite distances are
|
||||||
// not considered.
|
// not considered.
|
||||||
func Residual(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
func Residual(g graph.Graph, p path.AllShortest) map[int64]float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
r := make(map[int64]float64, len(nodes))
|
r := make(map[int64]float64, len(nodes))
|
||||||
for i, u := range nodes {
|
for i, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
|
@@ -22,7 +22,7 @@ type HubAuthority struct {
|
|||||||
// vector difference between iterations is below tol. The returned map is
|
// vector difference between iterations is below tol. The returned map is
|
||||||
// keyed on the graph node IDs.
|
// keyed on the graph node IDs.
|
||||||
func HITS(g graph.Directed, tol float64) map[int64]HubAuthority {
|
func HITS(g graph.Directed, tol float64) map[int64]HubAuthority {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
|
|
||||||
// Make a topological copy of g with dense node IDs.
|
// Make a topological copy of g with dense node IDs.
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
@@ -33,10 +33,10 @@ func HITS(g graph.Directed, tol float64) map[int64]HubAuthority {
|
|||||||
nodesLinkedFrom := make([][]int, len(nodes))
|
nodesLinkedFrom := make([][]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
for _, u := range g.To(id) {
|
for _, u := range graph.NodesOf(g.To(id)) {
|
||||||
nodesLinkingTo[i] = append(nodesLinkingTo[i], indexOf[u.ID()])
|
nodesLinkingTo[i] = append(nodesLinkingTo[i], indexOf[u.ID()])
|
||||||
}
|
}
|
||||||
for _, v := range g.From(id) {
|
for _, v := range graph.NodesOf(g.From(id)) {
|
||||||
nodesLinkedFrom[i] = append(nodesLinkedFrom[i], indexOf[v.ID()])
|
nodesLinkedFrom[i] = append(nodesLinkedFrom[i], indexOf[v.ID()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -51,7 +51,7 @@ func edgeWeightedPageRank(g graph.WeightedDirected, damp, tol float64) map[int64
|
|||||||
//
|
//
|
||||||
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
indexOf[n.ID()] = i
|
indexOf[n.ID()] = i
|
||||||
@@ -60,7 +60,7 @@ func edgeWeightedPageRank(g graph.WeightedDirected, damp, tol float64) map[int64
|
|||||||
m := mat.NewDense(len(nodes), len(nodes), nil)
|
m := mat.NewDense(len(nodes), len(nodes), nil)
|
||||||
dangling := damp / float64(len(nodes))
|
dangling := damp / float64(len(nodes))
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
to := g.From(u.ID())
|
to := graph.NodesOf(g.From(u.ID()))
|
||||||
var z float64
|
var z float64
|
||||||
for _, v := range to {
|
for _, v := range to {
|
||||||
if w, ok := g.Weight(u.ID(), v.ID()); ok {
|
if w, ok := g.Weight(u.ID(), v.ID()); ok {
|
||||||
@@ -134,7 +134,7 @@ func edgeWeightedPageRankSparse(g graph.WeightedDirected, damp, tol float64) map
|
|||||||
//
|
//
|
||||||
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
indexOf[n.ID()] = i
|
indexOf[n.ID()] = i
|
||||||
@@ -144,7 +144,7 @@ func edgeWeightedPageRankSparse(g graph.WeightedDirected, damp, tol float64) map
|
|||||||
var dangling compressedRow
|
var dangling compressedRow
|
||||||
df := damp / float64(len(nodes))
|
df := damp / float64(len(nodes))
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
to := g.From(u.ID())
|
to := graph.NodesOf(g.From(u.ID()))
|
||||||
var z float64
|
var z float64
|
||||||
for _, v := range to {
|
for _, v := range to {
|
||||||
if w, ok := g.Weight(u.ID(), v.ID()); ok {
|
if w, ok := g.Weight(u.ID(), v.ID()); ok {
|
||||||
@@ -215,7 +215,7 @@ func pageRank(g graph.Directed, damp, tol float64) map[int64]float64 {
|
|||||||
//
|
//
|
||||||
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
indexOf[n.ID()] = i
|
indexOf[n.ID()] = i
|
||||||
@@ -224,7 +224,7 @@ func pageRank(g graph.Directed, damp, tol float64) map[int64]float64 {
|
|||||||
m := mat.NewDense(len(nodes), len(nodes), nil)
|
m := mat.NewDense(len(nodes), len(nodes), nil)
|
||||||
dangling := damp / float64(len(nodes))
|
dangling := damp / float64(len(nodes))
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
to := g.From(u.ID())
|
to := graph.NodesOf(g.From(u.ID()))
|
||||||
f := damp / float64(len(to))
|
f := damp / float64(len(to))
|
||||||
for _, v := range to {
|
for _, v := range to {
|
||||||
m.Set(indexOf[v.ID()], j, f)
|
m.Set(indexOf[v.ID()], j, f)
|
||||||
@@ -288,7 +288,7 @@ func pageRankSparse(g graph.Directed, damp, tol float64) map[int64]float64 {
|
|||||||
//
|
//
|
||||||
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
// http://www.ams.org/samplings/feature-column/fcarc-pagerank
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
indexOf := make(map[int64]int, len(nodes))
|
indexOf := make(map[int64]int, len(nodes))
|
||||||
for i, n := range nodes {
|
for i, n := range nodes {
|
||||||
indexOf[n.ID()] = i
|
indexOf[n.ID()] = i
|
||||||
@@ -298,7 +298,7 @@ func pageRankSparse(g graph.Directed, damp, tol float64) map[int64]float64 {
|
|||||||
var dangling compressedRow
|
var dangling compressedRow
|
||||||
df := damp / float64(len(nodes))
|
df := damp / float64(len(nodes))
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
to := g.From(u.ID())
|
to := graph.NodesOf(g.From(u.ID()))
|
||||||
f := damp / float64(len(to))
|
f := damp / float64(len(to))
|
||||||
for _, v := range to {
|
for _, v := range to {
|
||||||
m.addTo(indexOf[v.ID()], j, f)
|
m.addTo(indexOf[v.ID()], j, f)
|
||||||
|
209
graph/nodes_edges.go
Normal file
209
graph/nodes_edges.go
Normal 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
|
||||||
|
}
|
@@ -41,7 +41,7 @@ func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
path = newShortestFrom(s, g.Nodes())
|
path = newShortestFrom(s, graph.NodesOf(g.Nodes()))
|
||||||
tid := t.ID()
|
tid := t.ID()
|
||||||
|
|
||||||
visited := make(set.Int64s)
|
visited := make(set.Int64s)
|
||||||
@@ -59,7 +59,7 @@ func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded
|
|||||||
}
|
}
|
||||||
|
|
||||||
visited.Add(uid)
|
visited.Add(uid)
|
||||||
for _, v := range g.From(u.node.ID()) {
|
for _, v := range graph.NodesOf(g.From(u.node.ID())) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
if visited.Has(vid) {
|
if visited.Has(vid) {
|
||||||
continue
|
continue
|
||||||
|
@@ -192,8 +192,8 @@ func TestExhaustiveAStar(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ps := DijkstraAllPaths(g)
|
ps := DijkstraAllPaths(g)
|
||||||
for _, start := range g.Nodes() {
|
for _, start := range graph.NodesOf(g.Nodes()) {
|
||||||
for _, goal := range g.Nodes() {
|
for _, goal := range graph.NodesOf(g.Nodes()) {
|
||||||
pt, _ := AStar(start, goal, g, heuristic)
|
pt, _ := AStar(start, goal, g, heuristic)
|
||||||
gotPath, gotWeight := pt.To(goal.ID())
|
gotPath, gotWeight := pt.To(goal.ID())
|
||||||
wantPath, wantWeight, _ := ps.Between(start.ID(), goal.ID())
|
wantPath, wantWeight, _ := ps.Between(start.ID(), goal.ID())
|
||||||
@@ -226,8 +226,8 @@ func (e weightedEdge) To() graph.Node { return e.to }
|
|||||||
func (e weightedEdge) Weight() float64 { return e.cost }
|
func (e weightedEdge) Weight() float64 { return e.cost }
|
||||||
|
|
||||||
func isMonotonic(g UndirectedWeightLister, h Heuristic) (ok bool, at graph.Edge, goal graph.Node) {
|
func isMonotonic(g UndirectedWeightLister, h Heuristic) (ok bool, at graph.Edge, goal graph.Node) {
|
||||||
for _, goal := range g.Nodes() {
|
for _, goal := range graph.NodesOf(g.Nodes()) {
|
||||||
for _, edge := range g.WeightedEdges() {
|
for _, edge := range graph.WeightedEdgesOf(g.WeightedEdges()) {
|
||||||
from := edge.From()
|
from := edge.From()
|
||||||
to := edge.To()
|
to := edge.To()
|
||||||
w, ok := g.Weight(from.ID(), to.ID())
|
w, ok := g.Weight(from.ID(), to.ID())
|
||||||
|
@@ -22,7 +22,7 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
|
|||||||
weight = UniformCost(g)
|
weight = UniformCost(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
|
|
||||||
path = newShortestFrom(u, nodes)
|
path = newShortestFrom(u, nodes)
|
||||||
path.dist[path.indexOf[u.ID()]] = 0
|
path.dist[path.indexOf[u.ID()]] = 0
|
||||||
@@ -33,7 +33,7 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
|
|||||||
changed := false
|
changed := false
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
k := path.indexOf[vid]
|
k := path.indexOf[vid]
|
||||||
w, ok := weight(uid, vid)
|
w, ok := weight(uid, vid)
|
||||||
@@ -54,7 +54,7 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) {
|
|||||||
|
|
||||||
for j, u := range nodes {
|
for j, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
k := path.indexOf[vid]
|
k := path.indexOf[vid]
|
||||||
w, ok := weight(uid, vid)
|
w, ok := weight(uid, vid)
|
||||||
|
@@ -19,6 +19,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/graph/encoding"
|
"gonum.org/v1/gonum/graph/encoding"
|
||||||
"gonum.org/v1/gonum/graph/encoding/dot"
|
"gonum.org/v1/gonum/graph/encoding/dot"
|
||||||
"gonum.org/v1/gonum/graph/graphs/gen"
|
"gonum.org/v1/gonum/graph/graphs/gen"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
"gonum.org/v1/gonum/graph/topo"
|
"gonum.org/v1/gonum/graph/topo"
|
||||||
)
|
)
|
||||||
@@ -240,7 +241,7 @@ func duplication(n int, delta, alpha, sigma float64) func() *simple.DirectedGrap
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
for _, e := range g.Edges() {
|
for _, e := range graph.EdgesOf(g.Edges()) {
|
||||||
if rnd.Intn(2) == 0 {
|
if rnd.Intn(2) == 0 {
|
||||||
g.RemoveEdge(e.From().ID(), e.To().ID())
|
g.RemoveEdge(e.From().ID(), e.To().ID())
|
||||||
}
|
}
|
||||||
@@ -253,8 +254,10 @@ type undirected struct {
|
|||||||
*simple.DirectedGraph
|
*simple.DirectedGraph
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g undirected) From(id int64) []graph.Node {
|
func (g undirected) From(id int64) graph.Nodes {
|
||||||
return append(g.DirectedGraph.From(id), g.DirectedGraph.To(id)...)
|
return iterator.NewOrderedNodes(append(
|
||||||
|
graph.NodesOf(g.DirectedGraph.From(id)),
|
||||||
|
graph.NodesOf(g.DirectedGraph.To(id))...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g undirected) HasEdgeBetween(xid, yid int64) bool {
|
func (g undirected) HasEdgeBetween(xid, yid int64) bool {
|
||||||
|
@@ -140,7 +140,7 @@ func (lt *lengauerTarjan) dfs(g graph.Directed, v graph.Node) {
|
|||||||
ltv.label = ltv
|
ltv.label = ltv
|
||||||
lt.nodes = append(lt.nodes, ltv)
|
lt.nodes = append(lt.nodes, ltv)
|
||||||
|
|
||||||
for _, w := range g.From(v.ID()) {
|
for _, w := range graph.NodesOf(g.From(v.ID())) {
|
||||||
wid := w.ID()
|
wid := w.ID()
|
||||||
|
|
||||||
idx, ok := lt.indexOf[wid]
|
idx, ok := lt.indexOf[wid]
|
||||||
|
@@ -162,7 +162,7 @@ func (lt *sLengauerTarjan) dfs(g graph.Directed, v graph.Node) {
|
|||||||
ltv.label = ltv
|
ltv.label = ltv
|
||||||
lt.nodes = append(lt.nodes, ltv)
|
lt.nodes = append(lt.nodes, ltv)
|
||||||
|
|
||||||
for _, w := range g.From(v.ID()) {
|
for _, w := range graph.NodesOf(g.From(v.ID())) {
|
||||||
wid := w.ID()
|
wid := w.ID()
|
||||||
|
|
||||||
idx, ok := lt.indexOf[wid]
|
idx, ok := lt.indexOf[wid]
|
||||||
|
@@ -25,7 +25,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
|
|||||||
if !h.Has(u.ID()) {
|
if !h.Has(u.ID()) {
|
||||||
return Shortest{from: u}
|
return Shortest{from: u}
|
||||||
}
|
}
|
||||||
path = newShortestFrom(u, h.Nodes())
|
path = newShortestFrom(u, graph.NodesOf(h.Nodes()))
|
||||||
} else {
|
} else {
|
||||||
if g.From(u.ID()) == nil {
|
if g.From(u.ID()) == nil {
|
||||||
return Shortest{from: u}
|
return Shortest{from: u}
|
||||||
@@ -58,7 +58,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
mnid := mid.node.ID()
|
mnid := mid.node.ID()
|
||||||
for _, v := range g.From(mnid) {
|
for _, v := range graph.NodesOf(g.From(mnid)) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
j, ok := path.indexOf[vid]
|
j, ok := path.indexOf[vid]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -88,7 +88,7 @@ func DijkstraFrom(u graph.Node, g traverse.Graph) Shortest {
|
|||||||
//
|
//
|
||||||
// The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|).
|
// The time complexity of DijkstrAllPaths is O(|V|.|E|+|V|^2.log|V|).
|
||||||
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
|
func DijkstraAllPaths(g graph.Graph) (paths AllShortest) {
|
||||||
paths = newAllShortest(g.Nodes(), false)
|
paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
|
||||||
dijkstraAllPaths(g, paths)
|
dijkstraAllPaths(g, paths)
|
||||||
return paths
|
return paths
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ func dijkstraAllPaths(g graph.Graph, paths AllShortest) {
|
|||||||
paths.dist.Set(i, k, mid.dist)
|
paths.dist.Set(i, k, mid.dist)
|
||||||
}
|
}
|
||||||
mnid := mid.node.ID()
|
mnid := mid.node.ID()
|
||||||
for _, v := range g.From(mnid) {
|
for _, v := range graph.NodesOf(g.From(mnid)) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
j := paths.indexOf[vid]
|
j := paths.indexOf[vid]
|
||||||
w, ok := weight(mnid, vid)
|
w, ok := weight(mnid, vid)
|
||||||
|
@@ -88,7 +88,7 @@ func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel
|
|||||||
|
|
||||||
d.queue.insert(d.t, key{d.heuristic(s, t), 0})
|
d.queue.insert(d.t, key{d.heuristic(s, t), 0})
|
||||||
|
|
||||||
for _, n := range g.Nodes() {
|
for _, n := range graph.NodesOf(g.Nodes()) {
|
||||||
switch n.ID() {
|
switch n.ID() {
|
||||||
case d.s.ID():
|
case d.s.ID():
|
||||||
d.model.AddNode(d.s)
|
d.model.AddNode(d.s)
|
||||||
@@ -98,9 +98,9 @@ func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel
|
|||||||
d.model.AddNode(newDStarLiteNode(n))
|
d.model.AddNode(newDStarLiteNode(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, u := range d.model.Nodes() {
|
for _, u := range graph.NodesOf(d.model.Nodes()) {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
w := edgeWeight(d.weight, uid, vid)
|
w := edgeWeight(d.weight, uid, vid)
|
||||||
if w < 0 {
|
if w < 0 {
|
||||||
@@ -196,7 +196,7 @@ func (d *DStarLite) findShortestPath() {
|
|||||||
case u.g > u.rhs:
|
case u.g > u.rhs:
|
||||||
u.g = u.rhs
|
u.g = u.rhs
|
||||||
d.queue.remove(u)
|
d.queue.remove(u)
|
||||||
for _, _s := range d.model.To(uid) {
|
for _, _s := range graph.NodesOf(d.model.To(uid)) {
|
||||||
s := _s.(*dStarLiteNode)
|
s := _s.(*dStarLiteNode)
|
||||||
sid := s.ID()
|
sid := s.ID()
|
||||||
if sid != d.t.ID() {
|
if sid != d.t.ID() {
|
||||||
@@ -207,13 +207,13 @@ func (d *DStarLite) findShortestPath() {
|
|||||||
default:
|
default:
|
||||||
gOld := u.g
|
gOld := u.g
|
||||||
u.g = math.Inf(1)
|
u.g = math.Inf(1)
|
||||||
for _, _s := range append(d.model.To(uid), u) {
|
for _, _s := range append(graph.NodesOf(d.model.To(uid)), u) {
|
||||||
s := _s.(*dStarLiteNode)
|
s := _s.(*dStarLiteNode)
|
||||||
sid := s.ID()
|
sid := s.ID()
|
||||||
if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld {
|
if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld {
|
||||||
if s.ID() != d.t.ID() {
|
if s.ID() != d.t.ID() {
|
||||||
s.rhs = math.Inf(1)
|
s.rhs = math.Inf(1)
|
||||||
for _, t := range d.model.From(sid) {
|
for _, t := range graph.NodesOf(d.model.From(sid)) {
|
||||||
tid := t.ID()
|
tid := t.ID()
|
||||||
s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, tid)+t.(*dStarLiteNode).g)
|
s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, tid)+t.(*dStarLiteNode).g)
|
||||||
}
|
}
|
||||||
@@ -250,7 +250,7 @@ func (d *DStarLite) Step() bool {
|
|||||||
|
|
||||||
var next *dStarLiteNode
|
var next *dStarLiteNode
|
||||||
dsid := d.s.ID()
|
dsid := d.s.ID()
|
||||||
for _, _s := range d.model.From(dsid) {
|
for _, _s := range graph.NodesOf(d.model.From(dsid)) {
|
||||||
s := _s.(*dStarLiteNode)
|
s := _s.(*dStarLiteNode)
|
||||||
w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g
|
w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g
|
||||||
if w < min || (w == min && s.rhs < rhs) {
|
if w < min || (w == min && s.rhs < rhs) {
|
||||||
@@ -320,7 +320,7 @@ func (d *DStarLite) UpdateWorld(changes []graph.Edge) {
|
|||||||
} else if u.rhs == cOld+v.g {
|
} else if u.rhs == cOld+v.g {
|
||||||
if uid != d.t.ID() {
|
if uid != d.t.ID() {
|
||||||
u.rhs = math.Inf(1)
|
u.rhs = math.Inf(1)
|
||||||
for _, t := range d.model.From(uid) {
|
for _, t := range graph.NodesOf(d.model.From(uid)) {
|
||||||
u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, uid, t.ID())+t.(*dStarLiteNode).g)
|
u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, uid, t.ID())+t.(*dStarLiteNode).g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,7 +365,7 @@ func (d *DStarLite) Path() (p []graph.Node, weight float64) {
|
|||||||
cost float64
|
cost float64
|
||||||
)
|
)
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, _v := range d.model.From(uid) {
|
for _, _v := range graph.NodesOf(d.model.From(uid)) {
|
||||||
v := _v.(*dStarLiteNode)
|
v := _v.(*dStarLiteNode)
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
w := edgeWeight(d.model.Weight, uid, vid)
|
w := edgeWeight(d.model.Weight, uid, vid)
|
||||||
|
@@ -387,7 +387,7 @@ var dynamicDStarLiteTests = []struct {
|
|||||||
modify: func(l *testgraphs.LimitedVisionGrid) {
|
modify: func(l *testgraphs.LimitedVisionGrid) {
|
||||||
all := l.Grid.AllVisible
|
all := l.Grid.AllVisible
|
||||||
l.Grid.AllVisible = false
|
l.Grid.AllVisible = false
|
||||||
for _, n := range l.Nodes() {
|
for _, n := range graph.NodesOf(l.Nodes()) {
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
l.Known[id] = !l.Grid.Has(id)
|
l.Known[id] = !l.Grid.Has(id)
|
||||||
}
|
}
|
||||||
@@ -400,9 +400,9 @@ var dynamicDStarLiteTests = []struct {
|
|||||||
l.Known[l.NodeAt(wallRow, wallCol).ID()] = false
|
l.Known[l.NodeAt(wallRow, wallCol).ID()] = false
|
||||||
|
|
||||||
// Check we have a correctly modified representation.
|
// Check we have a correctly modified representation.
|
||||||
for _, u := range l.Nodes() {
|
for _, u := range graph.NodesOf(l.Nodes()) {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range l.Nodes() {
|
for _, v := range graph.NodesOf(l.Nodes()) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
if l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) {
|
if l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) {
|
||||||
ur, uc := l.RowCol(uid)
|
ur, uc := l.RowCol(uid)
|
||||||
@@ -598,7 +598,7 @@ func TestDStarLiteDynamic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dp.dump(true)
|
dp.dump(true)
|
||||||
dp.printEdges("Initial world knowledge: %s\n\n", simpleWeightedEdgesOf(l, world.Edges()))
|
dp.printEdges("Initial world knowledge: %s\n\n", simpleWeightedEdgesOf(l, graph.EdgesOf(world.Edges())))
|
||||||
for d.Step() {
|
for d.Step() {
|
||||||
changes, _ := l.MoveTo(d.Here())
|
changes, _ := l.MoveTo(d.Here())
|
||||||
got = append(got, l.Location)
|
got = append(got, l.Location)
|
||||||
|
@@ -19,13 +19,14 @@ func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) {
|
|||||||
weight = UniformCost(g)
|
weight = UniformCost(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
paths = newAllShortest(nodes, true)
|
paths = newAllShortest(nodes, true)
|
||||||
for i, u := range nodes {
|
for i, u := range nodes {
|
||||||
paths.dist.Set(i, i, 0)
|
paths.dist.Set(i, i, 0)
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
to := g.From(uid)
|
||||||
vid := v.ID()
|
for to.Next() {
|
||||||
|
vid := to.Node().ID()
|
||||||
j := paths.indexOf[vid]
|
j := paths.indexOf[vid]
|
||||||
w, ok := weight(uid, vid)
|
w, ok := weight(uid, vid)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -91,14 +92,14 @@ func NewGridFrom(rows ...string) *Grid {
|
|||||||
|
|
||||||
// Nodes returns all the open nodes in the grid if AllVisible is
|
// Nodes returns all the open nodes in the grid if AllVisible is
|
||||||
// false, otherwise all nodes are returned.
|
// false, otherwise all nodes are returned.
|
||||||
func (g *Grid) Nodes() []graph.Node {
|
func (g *Grid) Nodes() graph.Nodes {
|
||||||
var nodes []graph.Node
|
var nodes []graph.Node
|
||||||
for id, ok := range g.open {
|
for id, ok := range g.open {
|
||||||
if ok || g.AllVisible {
|
if ok || g.AllVisible {
|
||||||
nodes = append(nodes, simple.Node(id))
|
nodes = append(nodes, simple.Node(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has returns whether n is a node in the grid. The state of
|
// Has returns whether n is a node in the grid. The state of
|
||||||
@@ -158,7 +159,7 @@ func (g *Grid) NodeAt(r, c int) graph.Node {
|
|||||||
|
|
||||||
// From returns all the nodes reachable from u. Reachabilty requires that both
|
// From returns all the nodes reachable from u. Reachabilty requires that both
|
||||||
// ends of an edge must be open.
|
// ends of an edge must be open.
|
||||||
func (g *Grid) From(uid int64) []graph.Node {
|
func (g *Grid) From(uid int64) graph.Nodes {
|
||||||
if !g.HasOpen(uid) {
|
if !g.HasOpen(uid) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -171,7 +172,7 @@ func (g *Grid) From(uid int64) []graph.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return to
|
return iterator.NewOrderedNodes(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether there is an edge between u and v.
|
// HasEdgeBetween returns whether there is an edge between u and v.
|
||||||
|
@@ -249,7 +249,7 @@ func TestGrid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, test := range reach {
|
for _, test := range reach {
|
||||||
g.AllowDiagonal = test.diagonal
|
g.AllowDiagonal = test.diagonal
|
||||||
got := g.From(test.from.ID())
|
got := graph.NodesOf(g.From(test.from.ID()))
|
||||||
if !reflect.DeepEqual(got, test.to) {
|
if !reflect.DeepEqual(got, test.to) {
|
||||||
t.Fatalf("unexpected nodes from %d with allow diagonal=%t:\ngot: %v\nwant:%v",
|
t.Fatalf("unexpected nodes from %d with allow diagonal=%t:\ngot: %v\nwant:%v",
|
||||||
test.from, test.diagonal, got, test.to)
|
test.from, test.diagonal, got, test.to)
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -147,12 +148,12 @@ func (l *LimitedVisionGrid) XY(id int64) (x, y float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the grid.
|
// Nodes returns all the nodes in the grid.
|
||||||
func (l *LimitedVisionGrid) Nodes() []graph.Node {
|
func (l *LimitedVisionGrid) Nodes() graph.Nodes {
|
||||||
nodes := make([]graph.Node, 0, len(l.Grid.open))
|
nodes := make([]graph.Node, 0, len(l.Grid.open))
|
||||||
for id := range l.Grid.open {
|
for id := range l.Grid.open {
|
||||||
nodes = append(nodes, simple.Node(id))
|
nodes = append(nodes, simple.Node(id))
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeAt returns the node at (r, c). The returned node may be open or closed.
|
// NodeAt returns the node at (r, c). The returned node may be open or closed.
|
||||||
@@ -166,7 +167,7 @@ func (l *LimitedVisionGrid) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// From returns nodes that are optimistically reachable from u.
|
// From returns nodes that are optimistically reachable from u.
|
||||||
func (l *LimitedVisionGrid) From(uid int64) []graph.Node {
|
func (l *LimitedVisionGrid) From(uid int64) graph.Nodes {
|
||||||
if !l.Has(uid) {
|
if !l.Has(uid) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -180,7 +181,7 @@ func (l *LimitedVisionGrid) From(uid int64) []graph.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return to
|
return iterator.NewOrderedNodes(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween optimistically returns whether an edge is exists between u and v.
|
// HasEdgeBetween optimistically returns whether an edge is exists between u and v.
|
||||||
|
@@ -1166,11 +1166,11 @@ func TestLimitedVisionGrid(t *testing.T) {
|
|||||||
l.Grid.AllowDiagonal = test.diag
|
l.Grid.AllowDiagonal = test.diag
|
||||||
|
|
||||||
x, y := l.XY(test.path[0].ID())
|
x, y := l.XY(test.path[0].ID())
|
||||||
for _, u := range l.Nodes() {
|
for _, u := range graph.NodesOf(l.Nodes()) {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
ux, uy := l.XY(uid)
|
ux, uy := l.XY(uid)
|
||||||
uNear := math.Hypot(x-ux, y-uy) <= test.radius
|
uNear := math.Hypot(x-ux, y-uy) <= test.radius
|
||||||
for _, v := range l.Nodes() {
|
for _, v := range graph.NodesOf(l.Nodes()) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
vx, vy := l.XY(vid)
|
vx, vy := l.XY(vid)
|
||||||
vNear := math.Hypot(x-vx, y-vy) <= test.radius
|
vNear := math.Hypot(x-vx, y-vy) <= test.radius
|
||||||
|
@@ -29,7 +29,7 @@ func JohnsonAllPaths(g graph.Graph) (paths AllShortest, ok bool) {
|
|||||||
jg.weight = UniformCost(g)
|
jg.weight = UniformCost(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
paths = newAllShortest(g.Nodes(), false)
|
paths = newAllShortest(graph.NodesOf(g.Nodes()), false)
|
||||||
|
|
||||||
sign := int64(-1)
|
sign := int64(-1)
|
||||||
for {
|
for {
|
||||||
@@ -69,7 +69,7 @@ type johnsonWeightAdjuster struct {
|
|||||||
q int64
|
q int64
|
||||||
g graph.Graph
|
g graph.Graph
|
||||||
|
|
||||||
from func(id int64) []graph.Node
|
from func(id int64) graph.Nodes
|
||||||
edgeTo func(uid, vid int64) graph.Edge
|
edgeTo func(uid, vid int64) graph.Edge
|
||||||
weight Weighting
|
weight Weighting
|
||||||
|
|
||||||
@@ -93,14 +93,14 @@ func (g johnsonWeightAdjuster) Has(id int64) bool {
|
|||||||
panic("path: unintended use of johnsonWeightAdjuster")
|
panic("path: unintended use of johnsonWeightAdjuster")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g johnsonWeightAdjuster) Nodes() []graph.Node {
|
func (g johnsonWeightAdjuster) Nodes() graph.Nodes {
|
||||||
if g.bellmanFord {
|
if g.bellmanFord {
|
||||||
return append(g.g.Nodes(), johnsonGraphNode(g.q))
|
return newJohnsonNodeIterator(g.q, g.g.Nodes())
|
||||||
}
|
}
|
||||||
return g.g.Nodes()
|
return g.g.Nodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g johnsonWeightAdjuster) From(id int64) []graph.Node {
|
func (g johnsonWeightAdjuster) From(id int64) graph.Nodes {
|
||||||
if g.bellmanFord && id == g.q {
|
if g.bellmanFord && id == g.q {
|
||||||
return g.g.Nodes()
|
return g.g.Nodes()
|
||||||
}
|
}
|
||||||
@@ -140,3 +140,59 @@ func (johnsonWeightAdjuster) HasEdgeBetween(_, _ int64) bool {
|
|||||||
type johnsonGraphNode int64
|
type johnsonGraphNode int64
|
||||||
|
|
||||||
func (n johnsonGraphNode) ID() int64 { return int64(n) }
|
func (n johnsonGraphNode) ID() int64 { return int64(n) }
|
||||||
|
|
||||||
|
func newJohnsonNodeIterator(q int64, nodes graph.Nodes) *johnsonNodeIterator {
|
||||||
|
return &johnsonNodeIterator{q: q, nodes: nodes}
|
||||||
|
}
|
||||||
|
|
||||||
|
type johnsonNodeIterator struct {
|
||||||
|
q int64
|
||||||
|
nodes graph.Nodes
|
||||||
|
qUsed, qOK bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *johnsonNodeIterator) Len() int {
|
||||||
|
var len int
|
||||||
|
if it.nodes != nil {
|
||||||
|
len = it.nodes.Len()
|
||||||
|
}
|
||||||
|
if !it.qUsed {
|
||||||
|
len++
|
||||||
|
}
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *johnsonNodeIterator) Next() bool {
|
||||||
|
if it.nodes != nil {
|
||||||
|
ok := it.nodes.Next()
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !it.qUsed {
|
||||||
|
it.qOK = true
|
||||||
|
it.qUsed = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
it.qOK = false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *johnsonNodeIterator) Node() graph.Node {
|
||||||
|
if it.qOK {
|
||||||
|
return johnsonGraphNode(it.q)
|
||||||
|
}
|
||||||
|
if it.nodes == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return it.nodes.Node()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *johnsonNodeIterator) Reset() {
|
||||||
|
it.qOK = false
|
||||||
|
it.qUsed = false
|
||||||
|
if it.nodes == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
it.nodes.Reset()
|
||||||
|
}
|
||||||
|
@@ -36,8 +36,9 @@ func ExampleBellmanFordFrom_negativecycles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a zero-cost path to all nodes from a new node Q.
|
// Add a zero-cost path to all nodes from a new node Q.
|
||||||
for _, n := range g.Nodes() {
|
nodes := g.Nodes()
|
||||||
g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node('Q'), T: n})
|
for nodes.Next() {
|
||||||
|
g.SetWeightedEdge(simple.WeightedEdge{F: simple.Node('Q'), T: nodes.Node()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the shortest path to each node from Q.
|
// Find the shortest path to each node from Q.
|
||||||
|
@@ -32,7 +32,7 @@ type WeightedBuilder interface {
|
|||||||
//
|
//
|
||||||
// If dst has nodes that exist in g, Prim will panic.
|
// If dst has nodes that exist in g, Prim will panic.
|
||||||
func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
|
func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
|
|||||||
|
|
||||||
u := nodes[0]
|
u := nodes[0]
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
w, ok := g.Weight(uid, v.ID())
|
w, ok := g.Weight(uid, v.ID())
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("prim: unexpected invalid weight")
|
panic("prim: unexpected invalid weight")
|
||||||
@@ -67,7 +67,7 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 {
|
|||||||
|
|
||||||
u = e.From()
|
u = e.From()
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
for _, n := range g.From(uid) {
|
for _, n := range graph.NodesOf(g.From(uid)) {
|
||||||
if key, ok := q.key(n); ok {
|
if key, ok := q.key(n); ok {
|
||||||
w, ok := g.Weight(uid, n.ID())
|
w, ok := g.Weight(uid, n.ID())
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -146,7 +146,7 @@ func (q *primQueue) update(u, v graph.Node, key float64) {
|
|||||||
// the set of edges in the graph.
|
// the set of edges in the graph.
|
||||||
type UndirectedWeightLister interface {
|
type UndirectedWeightLister interface {
|
||||||
graph.WeightedUndirected
|
graph.WeightedUndirected
|
||||||
WeightedEdges() []graph.WeightedEdge
|
WeightedEdges() graph.WeightedEdges
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kruskal generates a minimum spanning tree of g by greedy tree coalescence, placing
|
// Kruskal generates a minimum spanning tree of g by greedy tree coalescence, placing
|
||||||
@@ -162,11 +162,11 @@ type UndirectedWeightLister interface {
|
|||||||
//
|
//
|
||||||
// If dst has nodes that exist in g, Kruskal will panic.
|
// If dst has nodes that exist in g, Kruskal will panic.
|
||||||
func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 {
|
func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 {
|
||||||
edges := g.WeightedEdges()
|
edges := graph.WeightedEdgesOf(g.WeightedEdges())
|
||||||
sort.Sort(byWeight(edges))
|
sort.Sort(byWeight(edges))
|
||||||
|
|
||||||
ds := newDisjointSet()
|
ds := newDisjointSet()
|
||||||
for _, node := range g.Nodes() {
|
for _, node := range graph.NodesOf(g.Nodes()) {
|
||||||
dst.AddNode(node)
|
dst.AddNode(node)
|
||||||
ds.makeSet(node.ID())
|
ds.makeSet(node.ID())
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ func init() {
|
|||||||
type spanningGraph interface {
|
type spanningGraph interface {
|
||||||
graph.WeightedBuilder
|
graph.WeightedBuilder
|
||||||
graph.WeightedUndirected
|
graph.WeightedUndirected
|
||||||
WeightedEdges() []graph.WeightedEdge
|
WeightedEdges() graph.WeightedEdges
|
||||||
}
|
}
|
||||||
|
|
||||||
var spanningTreeTests = []struct {
|
var spanningTreeTests = []struct {
|
||||||
@@ -254,7 +254,7 @@ func testMinumumSpanning(mst func(dst WeightedBuilder, g spanningGraph) float64,
|
|||||||
test.name, w, test.want)
|
test.name, w, test.want)
|
||||||
}
|
}
|
||||||
var got float64
|
var got float64
|
||||||
for _, e := range dst.WeightedEdges() {
|
for _, e := range graph.WeightedEdgesOf(dst.WeightedEdges()) {
|
||||||
got += e.Weight()
|
got += e.Weight()
|
||||||
}
|
}
|
||||||
if got != test.want {
|
if got != test.want {
|
||||||
@@ -262,7 +262,7 @@ func testMinumumSpanning(mst func(dst WeightedBuilder, g spanningGraph) float64,
|
|||||||
test.name, got, test.want)
|
test.name, got, test.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotEdges := dst.Edges()
|
gotEdges := graph.EdgesOf(dst.Edges())
|
||||||
if len(gotEdges) != len(test.treeEdges) {
|
if len(gotEdges) != len(test.treeEdges) {
|
||||||
t.Errorf("unexpected number of spanning tree edges for %q: got: %d want: %d",
|
t.Errorf("unexpected number of spanning tree edges for %q: got: %d want: %d",
|
||||||
test.name, len(gotEdges), len(test.treeEdges))
|
test.name, len(gotEdges), len(test.treeEdges))
|
||||||
|
@@ -8,6 +8,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// YenKShortestPaths returns the k-shortest loopless paths from s to t in g.
|
// YenKShortestPaths returns the k-shortest loopless paths from s to t in g.
|
||||||
@@ -116,8 +117,8 @@ type yenKSPAdjuster struct {
|
|||||||
visitedEdges map[[2]int64]struct{}
|
visitedEdges map[[2]int64]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g yenKSPAdjuster) From(id int64) []graph.Node {
|
func (g yenKSPAdjuster) From(id int64) graph.Nodes {
|
||||||
nodes := g.Graph.From(id)
|
nodes := graph.NodesOf(g.Graph.From(id))
|
||||||
for i := 0; i < len(nodes); {
|
for i := 0; i < len(nodes); {
|
||||||
if g.canWalk(id, nodes[i].ID()) {
|
if g.canWalk(id, nodes[i].ID()) {
|
||||||
i++
|
i++
|
||||||
@@ -126,7 +127,7 @@ func (g yenKSPAdjuster) From(id int64) []graph.Node {
|
|||||||
nodes[i] = nodes[len(nodes)-1]
|
nodes[i] = nodes[len(nodes)-1]
|
||||||
nodes = nodes[:len(nodes)-1]
|
nodes = nodes[:len(nodes)-1]
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g yenKSPAdjuster) canWalk(u, v int64) bool {
|
func (g yenKSPAdjuster) canWalk(u, v int64) bool {
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/mat"
|
"gonum.org/v1/gonum/mat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,22 +86,18 @@ func (g *DirectedMatrix) has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *DirectedMatrix) Nodes() []graph.Node {
|
func (g *DirectedMatrix) Nodes() graph.Nodes {
|
||||||
if g.nodes != nil {
|
if g.nodes != nil {
|
||||||
nodes := make([]graph.Node, len(g.nodes))
|
nodes := make([]graph.Node, len(g.nodes))
|
||||||
copy(nodes, g.nodes)
|
copy(nodes, g.nodes)
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
r, _ := g.mat.Dims()
|
r, _ := g.mat.Dims()
|
||||||
nodes := make([]graph.Node, r)
|
return iterator.NewImplicitNodes(0, r, newSimpleNode)
|
||||||
for i := 0; i < r; i++ {
|
|
||||||
nodes[i] = Node(i)
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph.
|
// Edges returns all the edges in the graph.
|
||||||
func (g *DirectedMatrix) Edges() []graph.Edge {
|
func (g *DirectedMatrix) Edges() graph.Edges {
|
||||||
var edges []graph.Edge
|
var edges []graph.Edge
|
||||||
r, _ := g.mat.Dims()
|
r, _ := g.mat.Dims()
|
||||||
for i := 0; i < r; i++ {
|
for i := 0; i < r; i++ {
|
||||||
@@ -113,15 +110,15 @@ func (g *DirectedMatrix) Edges() []graph.Edge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *DirectedMatrix) From(id int64) []graph.Node {
|
func (g *DirectedMatrix) From(id int64) graph.Nodes {
|
||||||
if !g.has(id) {
|
if !g.has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var neighbors []graph.Node
|
var nodes []graph.Node
|
||||||
_, c := g.mat.Dims()
|
_, c := g.mat.Dims()
|
||||||
for j := 0; j < c; j++ {
|
for j := 0; j < c; j++ {
|
||||||
if int64(j) == id {
|
if int64(j) == id {
|
||||||
@@ -129,18 +126,18 @@ func (g *DirectedMatrix) From(id int64) []graph.Node {
|
|||||||
}
|
}
|
||||||
// id is not greater than maximum int by this point.
|
// id is not greater than maximum int by this point.
|
||||||
if !isSame(g.mat.At(int(id), j), g.absent) {
|
if !isSame(g.mat.At(int(id), j), g.absent) {
|
||||||
neighbors = append(neighbors, g.Node(int64(j)))
|
nodes = append(nodes, g.Node(int64(j)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return neighbors
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to n.
|
// To returns all nodes in g that can reach directly to n.
|
||||||
func (g *DirectedMatrix) To(id int64) []graph.Node {
|
func (g *DirectedMatrix) To(id int64) graph.Nodes {
|
||||||
if !g.has(id) {
|
if !g.has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var neighbors []graph.Node
|
var nodes []graph.Node
|
||||||
r, _ := g.mat.Dims()
|
r, _ := g.mat.Dims()
|
||||||
for i := 0; i < r; i++ {
|
for i := 0; i < r; i++ {
|
||||||
if int64(i) == id {
|
if int64(i) == id {
|
||||||
@@ -148,10 +145,10 @@ func (g *DirectedMatrix) To(id int64) []graph.Node {
|
|||||||
}
|
}
|
||||||
// id is not greater than maximum int by this point.
|
// id is not greater than maximum int by this point.
|
||||||
if !isSame(g.mat.At(i, int(id)), g.absent) {
|
if !isSame(g.mat.At(i, int(id)), g.absent) {
|
||||||
neighbors = append(neighbors, g.Node(int64(i)))
|
nodes = append(nodes, g.Node(int64(i)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return neighbors
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
||||||
@@ -251,34 +248,6 @@ func (g *DirectedMatrix) RemoveEdge(fid, tid int64) {
|
|||||||
g.mat.Set(int(fid), int(tid), g.absent)
|
g.mat.Set(int(fid), int(tid), g.absent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the in+out degree of n in g.
|
|
||||||
func (g *DirectedMatrix) Degree(id int64) int {
|
|
||||||
if !g.has(id) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var deg int
|
|
||||||
r, c := g.mat.Dims()
|
|
||||||
for i := 0; i < r; i++ {
|
|
||||||
if int64(i) == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// id is not greater than maximum int by this point.
|
|
||||||
if !isSame(g.mat.At(int(id), i), g.absent) {
|
|
||||||
deg++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := 0; i < c; i++ {
|
|
||||||
if int64(i) == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// id is not greater than maximum int by this point.
|
|
||||||
if !isSame(g.mat.At(i, int(id)), g.absent) {
|
|
||||||
deg++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matrix returns the mat.Matrix representation of the graph. The orientation
|
// Matrix returns the mat.Matrix representation of the graph. The orientation
|
||||||
// of the matrix is such that the matrix entry at G_{ij} is the weight of the edge
|
// of the matrix is such that the matrix entry at G_{ij} is the weight of the edge
|
||||||
// from node i to node j.
|
// from node i to node j.
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
"gonum.org/v1/gonum/mat"
|
"gonum.org/v1/gonum/mat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,22 +86,18 @@ func (g *UndirectedMatrix) has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *UndirectedMatrix) Nodes() []graph.Node {
|
func (g *UndirectedMatrix) Nodes() graph.Nodes {
|
||||||
if g.nodes != nil {
|
if g.nodes != nil {
|
||||||
nodes := make([]graph.Node, len(g.nodes))
|
nodes := make([]graph.Node, len(g.nodes))
|
||||||
copy(nodes, g.nodes)
|
copy(nodes, g.nodes)
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
r := g.mat.Symmetric()
|
r := g.mat.Symmetric()
|
||||||
nodes := make([]graph.Node, r)
|
return iterator.NewImplicitNodes(0, r, newSimpleNode)
|
||||||
for i := 0; i < r; i++ {
|
|
||||||
nodes[i] = Node(i)
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph.
|
// Edges returns all the edges in the graph.
|
||||||
func (g *UndirectedMatrix) Edges() []graph.Edge {
|
func (g *UndirectedMatrix) Edges() graph.Edges {
|
||||||
var edges []graph.Edge
|
var edges []graph.Edge
|
||||||
r, _ := g.mat.Dims()
|
r, _ := g.mat.Dims()
|
||||||
for i := 0; i < r; i++ {
|
for i := 0; i < r; i++ {
|
||||||
@@ -110,15 +107,15 @@ func (g *UndirectedMatrix) Edges() []graph.Edge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *UndirectedMatrix) From(id int64) []graph.Node {
|
func (g *UndirectedMatrix) From(id int64) graph.Nodes {
|
||||||
if !g.has(id) {
|
if !g.has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var neighbors []graph.Node
|
var nodes []graph.Node
|
||||||
r := g.mat.Symmetric()
|
r := g.mat.Symmetric()
|
||||||
for i := 0; i < r; i++ {
|
for i := 0; i < r; i++ {
|
||||||
if int64(i) == id {
|
if int64(i) == id {
|
||||||
@@ -126,10 +123,10 @@ func (g *UndirectedMatrix) From(id int64) []graph.Node {
|
|||||||
}
|
}
|
||||||
// id is not greater than maximum int by this point.
|
// id is not greater than maximum int by this point.
|
||||||
if !isSame(g.mat.At(int(id), i), g.absent) {
|
if !isSame(g.mat.At(int(id), i), g.absent) {
|
||||||
neighbors = append(neighbors, g.Node(int64(i)))
|
nodes = append(nodes, g.Node(int64(i)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return neighbors
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -226,25 +223,6 @@ func (g *UndirectedMatrix) RemoveEdge(fid, tid int64) {
|
|||||||
g.mat.SetSym(int(fid), int(tid), g.absent)
|
g.mat.SetSym(int(fid), int(tid), g.absent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the degree of n in g.
|
|
||||||
func (g *UndirectedMatrix) Degree(id int64) int {
|
|
||||||
if !g.has(id) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var deg int
|
|
||||||
r := g.mat.Symmetric()
|
|
||||||
for i := 0; i < r; i++ {
|
|
||||||
if int64(i) == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// id is not greater than maximum int by this point.
|
|
||||||
if !isSame(g.mat.At(int(id), i), g.absent) {
|
|
||||||
deg++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deg
|
|
||||||
}
|
|
||||||
|
|
||||||
// Matrix returns the mat.Matrix representation of the graph.
|
// Matrix returns the mat.Matrix representation of the graph.
|
||||||
func (g *UndirectedMatrix) Matrix() mat.Matrix {
|
func (g *UndirectedMatrix) Matrix() mat.Matrix {
|
||||||
// Prevent alteration of dimensions of the returned matrix.
|
// Prevent alteration of dimensions of the returned matrix.
|
||||||
|
@@ -38,7 +38,7 @@ func TestBasicDenseImpassable(t *testing.T) {
|
|||||||
t.Errorf("Node that should exist doesn't: %d", i)
|
t.Errorf("Node that should exist doesn't: %d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if degree := dg.Degree(int64(i)); degree != 0 {
|
if degree := dg.From(int64(i)).Len(); degree != 0 {
|
||||||
t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree)
|
t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ func TestBasicDensePassable(t *testing.T) {
|
|||||||
t.Errorf("Node that should exist doesn't: %d", i)
|
t.Errorf("Node that should exist doesn't: %d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if degree := dg.Degree(int64(i)); degree != 4 {
|
if degree := dg.From(int64(i)).Len(); degree != 4 {
|
||||||
t.Errorf("Node in passable graph missing neighbors. Node: %d Degree: %d", i, degree)
|
t.Errorf("Node in passable graph missing neighbors. Node: %d Degree: %d", i, degree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,18 +77,18 @@ func TestDirectedDenseAddRemove(t *testing.T) {
|
|||||||
dg := NewDirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
|
dg := NewDirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
|
||||||
dg.SetWeightedEdge(WeightedEdge{F: Node(0), T: Node(2), W: 1})
|
dg.SetWeightedEdge(WeightedEdge{F: Node(0), T: Node(2), W: 1})
|
||||||
|
|
||||||
if neighbors := dg.From(int64(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
if neighbors := graph.NodesOf(dg.From(int64(0))); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||||
dg.Edge(int64(0), int64(2)) == nil {
|
dg.Edge(int64(0), int64(2)) == nil {
|
||||||
t.Errorf("Adding edge didn't create successor")
|
t.Errorf("Adding edge didn't create successor")
|
||||||
}
|
}
|
||||||
|
|
||||||
dg.RemoveEdge(int64(0), int64(2))
|
dg.RemoveEdge(int64(0), int64(2))
|
||||||
|
|
||||||
if neighbors := dg.From(int64(0)); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil {
|
if neighbors := graph.NodesOf(dg.From(int64(0))); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil {
|
||||||
t.Errorf("Removing edge didn't properly remove successor")
|
t.Errorf("Removing edge didn't properly remove successor")
|
||||||
}
|
}
|
||||||
|
|
||||||
if neighbors := dg.To(int64(2)); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil {
|
if neighbors := graph.NodesOf(dg.To(int64(2))); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil {
|
||||||
t.Errorf("Removing directed edge wrongly kept predecessor")
|
t.Errorf("Removing directed edge wrongly kept predecessor")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +109,12 @@ func TestUndirectedDenseAddRemove(t *testing.T) {
|
|||||||
dg := NewUndirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
|
dg := NewUndirectedMatrix(10, math.Inf(1), 0, math.Inf(1))
|
||||||
dg.SetEdge(Edge{F: Node(0), T: Node(2)})
|
dg.SetEdge(Edge{F: Node(0), T: Node(2)})
|
||||||
|
|
||||||
if neighbors := dg.From(int64(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
if neighbors := graph.NodesOf(dg.From(int64(0))); len(neighbors) != 1 || neighbors[0].ID() != 2 ||
|
||||||
dg.EdgeBetween(int64(0), int64(2)) == nil {
|
dg.EdgeBetween(int64(0), int64(2)) == nil {
|
||||||
t.Errorf("Couldn't add neighbor")
|
t.Errorf("Couldn't add neighbor")
|
||||||
}
|
}
|
||||||
|
|
||||||
if neighbors := dg.From(int64(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
if neighbors := graph.NodesOf(dg.From(int64(2))); len(neighbors) != 1 || neighbors[0].ID() != 0 ||
|
||||||
dg.EdgeBetween(int64(2), int64(0)) == nil {
|
dg.EdgeBetween(int64(2), int64(0)) == nil {
|
||||||
t.Errorf("Adding an undirected neighbor didn't add it reciprocally")
|
t.Errorf("Adding an undirected neighbor didn't add it reciprocally")
|
||||||
}
|
}
|
||||||
@@ -122,27 +122,27 @@ func TestUndirectedDenseAddRemove(t *testing.T) {
|
|||||||
|
|
||||||
func TestDenseLists(t *testing.T) {
|
func TestDenseLists(t *testing.T) {
|
||||||
dg := NewDirectedMatrix(15, 1, 0, math.Inf(1))
|
dg := NewDirectedMatrix(15, 1, 0, math.Inf(1))
|
||||||
nodes := dg.Nodes()
|
nodes := graph.NodesOf(dg.Nodes())
|
||||||
|
|
||||||
if len(nodes) != 15 {
|
if len(nodes) != 15 {
|
||||||
t.Fatalf("Wrong number of nodes")
|
t.Fatalf("Wrong number of nodes: got:%v want:%v", len(nodes), 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(ordered.ByID(nodes))
|
sort.Sort(ordered.ByID(nodes))
|
||||||
|
|
||||||
for i, node := range dg.Nodes() {
|
for i, node := range graph.NodesOf(dg.Nodes()) {
|
||||||
if int64(i) != node.ID() {
|
if int64(i) != node.ID() {
|
||||||
t.Errorf("Node list doesn't return properly id'd nodes")
|
t.Errorf("Node list doesn't return properly id'd nodes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
edges := dg.Edges()
|
edges := graph.EdgesOf(dg.Edges())
|
||||||
if len(edges) != 15*14 {
|
if len(edges) != 15*14 {
|
||||||
t.Errorf("Improper number of edges for passable dense graph")
|
t.Errorf("Improper number of edges for passable dense graph")
|
||||||
}
|
}
|
||||||
|
|
||||||
dg.RemoveEdge(int64(12), int64(11))
|
dg.RemoveEdge(int64(12), int64(11))
|
||||||
edges = dg.Edges()
|
edges = graph.EdgesOf(dg.Edges())
|
||||||
if len(edges) != (15*14)-1 {
|
if len(edges) != (15*14)-1 {
|
||||||
t.Errorf("Removing edge didn't affect edge listing properly")
|
t.Errorf("Removing edge didn't affect edge listing properly")
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DirectedGraph implements a generalized directed graph.
|
// DirectedGraph implements a generalized directed graph.
|
||||||
@@ -131,7 +132,7 @@ func (g *DirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *DirectedGraph) Nodes() []graph.Node {
|
func (g *DirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -141,22 +142,22 @@ func (g *DirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph.
|
// Edges returns all the edges in the graph.
|
||||||
func (g *DirectedGraph) Edges() []graph.Edge {
|
func (g *DirectedGraph) Edges() graph.Edges {
|
||||||
var edges []graph.Edge
|
var edges []graph.Edge
|
||||||
for _, u := range g.nodes {
|
for _, u := range g.nodes {
|
||||||
for _, e := range g.from[u.ID()] {
|
for _, e := range g.from[u.ID()] {
|
||||||
edges = append(edges, e)
|
edges = append(edges, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *DirectedGraph) From(id int64) []graph.Node {
|
func (g *DirectedGraph) From(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -167,11 +168,11 @@ func (g *DirectedGraph) From(id int64) []graph.Node {
|
|||||||
from[i] = g.nodes[vid]
|
from[i] = g.nodes[vid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return from
|
return iterator.NewOrderedNodes(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to n.
|
// To returns all nodes in g that can reach directly to n.
|
||||||
func (g *DirectedGraph) To(id int64) []graph.Node {
|
func (g *DirectedGraph) To(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -182,7 +183,7 @@ func (g *DirectedGraph) To(id int64) []graph.Node {
|
|||||||
to[i] = g.nodes[uid]
|
to[i] = g.nodes[uid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return to
|
return iterator.NewOrderedNodes(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
||||||
@@ -212,11 +213,3 @@ func (g *DirectedGraph) HasEdgeFromTo(uid, vid int64) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the in+out degree of n in g.
|
|
||||||
func (g *DirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(g.from[id]) + len(g.to[id])
|
|
||||||
}
|
|
||||||
|
@@ -21,7 +21,7 @@ var (
|
|||||||
func TestEdgeOvercounting(t *testing.T) {
|
func TestEdgeOvercounting(t *testing.T) {
|
||||||
g := generateDummyGraph()
|
g := generateDummyGraph()
|
||||||
|
|
||||||
if neigh := g.From(int64(2)); len(neigh) != 2 {
|
if neigh := graph.NodesOf(g.From(int64(2))); len(neigh) != 2 {
|
||||||
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,10 @@ func (n Node) ID() int64 {
|
|||||||
return int64(n)
|
return int64(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newSimpleNode(id int) graph.Node {
|
||||||
|
return Node(id)
|
||||||
|
}
|
||||||
|
|
||||||
// Edge is a simple graph edge.
|
// Edge is a simple graph edge.
|
||||||
type Edge struct {
|
type Edge struct {
|
||||||
F, T graph.Node
|
F, T graph.Node
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UndirectedGraph implements a generalized undirected graph.
|
// UndirectedGraph implements a generalized undirected graph.
|
||||||
@@ -123,7 +124,7 @@ func (g *UndirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *UndirectedGraph) Nodes() []graph.Node {
|
func (g *UndirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -133,11 +134,11 @@ func (g *UndirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph.
|
// Edges returns all the edges in the graph.
|
||||||
func (g *UndirectedGraph) Edges() []graph.Edge {
|
func (g *UndirectedGraph) Edges() graph.Edges {
|
||||||
if len(g.edges) == 0 {
|
if len(g.edges) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -155,11 +156,11 @@ func (g *UndirectedGraph) Edges() []graph.Edge {
|
|||||||
edges = append(edges, e)
|
edges = append(edges, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *UndirectedGraph) From(id int64) []graph.Node {
|
func (g *UndirectedGraph) From(id int64) graph.Nodes {
|
||||||
if !g.Has(id) {
|
if !g.Has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -170,7 +171,7 @@ func (g *UndirectedGraph) From(id int64) []graph.Node {
|
|||||||
nodes[i] = g.nodes[from]
|
nodes[i] = g.nodes[from]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -193,11 +194,3 @@ func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge {
|
|||||||
}
|
}
|
||||||
return edge
|
return edge
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the degree of n in g.
|
|
||||||
func (g *UndirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(g.edges[id])
|
|
||||||
}
|
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WeightedDirectedGraph implements a generalized weighted directed graph.
|
// WeightedDirectedGraph implements a generalized weighted directed graph.
|
||||||
@@ -137,7 +138,7 @@ func (g *WeightedDirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *WeightedDirectedGraph) Nodes() []graph.Node {
|
func (g *WeightedDirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.from) == 0 {
|
if len(g.from) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -147,18 +148,18 @@ func (g *WeightedDirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph.
|
// Edges returns all the edges in the graph.
|
||||||
func (g *WeightedDirectedGraph) Edges() []graph.Edge {
|
func (g *WeightedDirectedGraph) Edges() graph.Edges {
|
||||||
var edges []graph.Edge
|
var edges []graph.Edge
|
||||||
for _, u := range g.nodes {
|
for _, u := range g.nodes {
|
||||||
for _, e := range g.from[u.ID()] {
|
for _, e := range g.from[u.ID()] {
|
||||||
edges = append(edges, e)
|
edges = append(edges, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedEdges returns all the weighted edges in the graph.
|
// WeightedEdges returns all the weighted edges in the graph.
|
||||||
@@ -173,7 +174,7 @@ func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
|
func (g *WeightedDirectedGraph) From(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -184,11 +185,11 @@ func (g *WeightedDirectedGraph) From(id int64) []graph.Node {
|
|||||||
from[i] = g.nodes[vid]
|
from[i] = g.nodes[vid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return from
|
return iterator.NewOrderedNodes(from)
|
||||||
}
|
}
|
||||||
|
|
||||||
// To returns all nodes in g that can reach directly to n.
|
// To returns all nodes in g that can reach directly to n.
|
||||||
func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
|
func (g *WeightedDirectedGraph) To(id int64) graph.Nodes {
|
||||||
if _, ok := g.from[id]; !ok {
|
if _, ok := g.from[id]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -199,7 +200,7 @@ func (g *WeightedDirectedGraph) To(id int64) []graph.Node {
|
|||||||
to[i] = g.nodes[uid]
|
to[i] = g.nodes[uid]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return to
|
return iterator.NewOrderedNodes(to)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
// HasEdgeBetween returns whether an edge exists between nodes x and y without
|
||||||
@@ -251,11 +252,3 @@ func (g *WeightedDirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
|
|||||||
}
|
}
|
||||||
return g.absent, false
|
return g.absent, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the in+out degree of n in g.
|
|
||||||
func (g *WeightedDirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(g.from[id]) + len(g.to[id])
|
|
||||||
}
|
|
||||||
|
@@ -23,7 +23,7 @@ var (
|
|||||||
func TestWeightedEdgeOvercounting(t *testing.T) {
|
func TestWeightedEdgeOvercounting(t *testing.T) {
|
||||||
g := generateDummyGraph()
|
g := generateDummyGraph()
|
||||||
|
|
||||||
if neigh := g.From(int64(2)); len(neigh) != 2 {
|
if neigh := graph.NodesOf(g.From(int64(2))); len(neigh) != 2 {
|
||||||
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
t.Errorf("Node 2 has incorrect number of neighbors got neighbors %v (count %d), expected 2 neighbors {0,1}", neigh, len(neigh))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/uid"
|
"gonum.org/v1/gonum/graph/internal/uid"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WeightedUndirectedGraph implements a generalized weighted undirected graph.
|
// WeightedUndirectedGraph implements a generalized weighted undirected graph.
|
||||||
@@ -129,7 +130,7 @@ func (g *WeightedUndirectedGraph) Has(id int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
|
func (g *WeightedUndirectedGraph) Nodes() graph.Nodes {
|
||||||
if len(g.nodes) == 0 {
|
if len(g.nodes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -139,11 +140,11 @@ func (g *WeightedUndirectedGraph) Nodes() []graph.Node {
|
|||||||
nodes[i] = n
|
nodes[i] = n
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all the edges in the graph.
|
// Edges returns all the edges in the graph.
|
||||||
func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
|
func (g *WeightedUndirectedGraph) Edges() graph.Edges {
|
||||||
if len(g.edges) == 0 {
|
if len(g.edges) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -161,11 +162,11 @@ func (g *WeightedUndirectedGraph) Edges() []graph.Edge {
|
|||||||
edges = append(edges, e)
|
edges = append(edges, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WeightedEdges returns all the weighted edges in the graph.
|
// WeightedEdges returns all the weighted edges in the graph.
|
||||||
func (g *WeightedUndirectedGraph) WeightedEdges() []graph.WeightedEdge {
|
func (g *WeightedUndirectedGraph) WeightedEdges() graph.WeightedEdges {
|
||||||
var edges []graph.WeightedEdge
|
var edges []graph.WeightedEdge
|
||||||
seen := make(map[[2]int64]struct{})
|
seen := make(map[[2]int64]struct{})
|
||||||
for _, u := range g.edges {
|
for _, u := range g.edges {
|
||||||
@@ -180,11 +181,11 @@ func (g *WeightedUndirectedGraph) WeightedEdges() []graph.WeightedEdge {
|
|||||||
edges = append(edges, e)
|
edges = append(edges, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return edges
|
return iterator.NewOrderedWeightedEdges(edges)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from n.
|
// From returns all nodes in g that can be reached directly from n.
|
||||||
func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
|
func (g *WeightedUndirectedGraph) From(id int64) graph.Nodes {
|
||||||
if !g.Has(id) {
|
if !g.Has(id) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -195,7 +196,7 @@ func (g *WeightedUndirectedGraph) From(id int64) []graph.Node {
|
|||||||
nodes[i] = g.nodes[from]
|
nodes[i] = g.nodes[from]
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nodes
|
return iterator.NewOrderedNodes(nodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -245,11 +246,3 @@ func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) {
|
|||||||
}
|
}
|
||||||
return g.absent, false
|
return g.absent, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Degree returns the degree of n in g.
|
|
||||||
func (g *WeightedUndirectedGraph) Degree(id int64) int {
|
|
||||||
if _, ok := g.nodes[id]; !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(g.edges[id])
|
|
||||||
}
|
|
||||||
|
@@ -44,7 +44,7 @@ func KCore(k int, g graph.Undirected) []graph.Node {
|
|||||||
// s, a set of relative offsets into l for each k-core, where k is an index
|
// s, a set of relative offsets into l for each k-core, where k is an index
|
||||||
// into s.
|
// into s.
|
||||||
func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
|
func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
|
|
||||||
// The algorithm used here is essentially as described at
|
// The algorithm used here is essentially as described at
|
||||||
// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
|
// http://en.wikipedia.org/w/index.php?title=Degeneracy_%28graph_theory%29&oldid=640308710
|
||||||
@@ -61,7 +61,7 @@ func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
|
|||||||
)
|
)
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
adj := g.From(id)
|
adj := graph.NodesOf(g.From(id))
|
||||||
neighbours[id] = adj
|
neighbours[id] = adj
|
||||||
dv[id] = len(adj)
|
dv[id] = len(adj)
|
||||||
if len(adj) > maxDegree {
|
if len(adj) > maxDegree {
|
||||||
@@ -133,7 +133,7 @@ func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) {
|
|||||||
|
|
||||||
// BronKerbosch returns the set of maximal cliques of the undirected graph g.
|
// BronKerbosch returns the set of maximal cliques of the undirected graph g.
|
||||||
func BronKerbosch(g graph.Undirected) [][]graph.Node {
|
func BronKerbosch(g graph.Undirected) [][]graph.Node {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
|
|
||||||
// The algorithm used here is essentially BronKerbosch3 as described at
|
// The algorithm used here is essentially BronKerbosch3 as described at
|
||||||
// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
|
// http://en.wikipedia.org/w/index.php?title=Bron%E2%80%93Kerbosch_algorithm&oldid=656805858
|
||||||
@@ -147,7 +147,7 @@ func BronKerbosch(g graph.Undirected) [][]graph.Node {
|
|||||||
order, _ := degeneracyOrdering(g)
|
order, _ := degeneracyOrdering(g)
|
||||||
ordered.Reverse(order)
|
ordered.Reverse(order)
|
||||||
for _, v := range order {
|
for _, v := range order {
|
||||||
neighbours := g.From(v.ID())
|
neighbours := graph.NodesOf(g.From(v.ID()))
|
||||||
nv := make(set.Nodes, len(neighbours))
|
nv := make(set.Nodes, len(neighbours))
|
||||||
for _, n := range neighbours {
|
for _, n := range neighbours {
|
||||||
nv.Add(n)
|
nv.Add(n)
|
||||||
@@ -177,7 +177,7 @@ func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
neighbours := g.From(vid)
|
neighbours := graph.NodesOf(g.From(vid))
|
||||||
nv := make(set.Nodes, len(neighbours))
|
nv := make(set.Nodes, len(neighbours))
|
||||||
for _, n := range neighbours {
|
for _, n := range neighbours {
|
||||||
nv.Add(n)
|
nv.Add(n)
|
||||||
@@ -207,10 +207,10 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb
|
|||||||
// compile time option.
|
// compile time option.
|
||||||
if !tomitaTanakaTakahashi {
|
if !tomitaTanakaTakahashi {
|
||||||
for _, n := range p {
|
for _, n := range p {
|
||||||
return g.From(n.ID())
|
return graph.NodesOf(g.From(n.ID()))
|
||||||
}
|
}
|
||||||
for _, n := range x {
|
for _, n := range x {
|
||||||
return g.From(n.ID())
|
return graph.NodesOf(g.From(n.ID()))
|
||||||
}
|
}
|
||||||
panic("bronKerbosch: empty set")
|
panic("bronKerbosch: empty set")
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb
|
|||||||
maxNeighbors := func(s set.Nodes) {
|
maxNeighbors := func(s set.Nodes) {
|
||||||
outer:
|
outer:
|
||||||
for _, u := range s {
|
for _, u := range s {
|
||||||
nb := g.From(u.ID())
|
nb := graph.NodesOf(g.From(u.ID()))
|
||||||
c := len(nb)
|
c := len(nb)
|
||||||
if c <= max {
|
if c <= max {
|
||||||
continue
|
continue
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
"gonum.org/v1/gonum/graph/internal/set"
|
"gonum.org/v1/gonum/graph/internal/set"
|
||||||
|
"gonum.org/v1/gonum/graph/iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// johnson implements Johnson's "Finding all the elementary
|
// johnson implements Johnson's "Finding all the elementary
|
||||||
@@ -132,7 +133,7 @@ type johnsonGraph struct {
|
|||||||
|
|
||||||
// johnsonGraphFrom returns a deep copy of the graph g.
|
// johnsonGraphFrom returns a deep copy of the graph g.
|
||||||
func johnsonGraphFrom(g graph.Directed) johnsonGraph {
|
func johnsonGraphFrom(g graph.Directed) johnsonGraph {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
sort.Sort(ordered.ByID(nodes))
|
sort.Sort(ordered.ByID(nodes))
|
||||||
c := johnsonGraph{
|
c := johnsonGraph{
|
||||||
orig: nodes,
|
orig: nodes,
|
||||||
@@ -144,7 +145,7 @@ func johnsonGraphFrom(g graph.Directed) johnsonGraph {
|
|||||||
for i, u := range nodes {
|
for i, u := range nodes {
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
c.index[uid] = i
|
c.index[uid] = i
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
if c.succ[uid] == nil {
|
if c.succ[uid] == nil {
|
||||||
c.succ[uid] = make(set.Int64s)
|
c.succ[uid] = make(set.Int64s)
|
||||||
c.nodes.Add(uid)
|
c.nodes.Add(uid)
|
||||||
@@ -239,16 +240,16 @@ func (g johnsonGraph) sccSubGraph(sccs [][]graph.Node, min int) johnsonGraph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nodes is required to satisfy Tarjan.
|
// Nodes is required to satisfy Tarjan.
|
||||||
func (g johnsonGraph) Nodes() []graph.Node {
|
func (g johnsonGraph) Nodes() graph.Nodes {
|
||||||
n := make([]graph.Node, 0, len(g.nodes))
|
n := make([]graph.Node, 0, len(g.nodes))
|
||||||
for id := range g.nodes {
|
for id := range g.nodes {
|
||||||
n = append(n, johnsonGraphNode(id))
|
n = append(n, johnsonGraphNode(id))
|
||||||
}
|
}
|
||||||
return n
|
return iterator.NewOrderedNodes(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Successors is required to satisfy Tarjan.
|
// Successors is required to satisfy Tarjan.
|
||||||
func (g johnsonGraph) From(id int64) []graph.Node {
|
func (g johnsonGraph) From(id int64) graph.Nodes {
|
||||||
adj := g.succ[id]
|
adj := g.succ[id]
|
||||||
if len(adj) == 0 {
|
if len(adj) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -257,7 +258,7 @@ func (g johnsonGraph) From(id int64) []graph.Node {
|
|||||||
for id := range adj {
|
for id := range adj {
|
||||||
succ = append(succ, johnsonGraphNode(id))
|
succ = append(succ, johnsonGraphNode(id))
|
||||||
}
|
}
|
||||||
return succ
|
return iterator.NewOrderedNodes(succ)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (johnsonGraph) Has(int64) bool {
|
func (johnsonGraph) Has(int64) bool {
|
||||||
@@ -272,7 +273,7 @@ func (johnsonGraph) Edge(_, _ int64) graph.Edge {
|
|||||||
func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool {
|
func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool {
|
||||||
panic("topo: unintended use of johnsonGraph")
|
panic("topo: unintended use of johnsonGraph")
|
||||||
}
|
}
|
||||||
func (johnsonGraph) To(int64) []graph.Node {
|
func (johnsonGraph) To(int64) graph.Nodes {
|
||||||
panic("topo: unintended use of johnsonGraph")
|
panic("topo: unintended use of johnsonGraph")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,9 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
|
|||||||
var cycles [][]graph.Node
|
var cycles [][]graph.Node
|
||||||
done := make(set.Int64s)
|
done := make(set.Int64s)
|
||||||
var tree linear.NodeStack
|
var tree linear.NodeStack
|
||||||
for _, n := range g.Nodes() {
|
nodes := g.Nodes()
|
||||||
|
for nodes.Next() {
|
||||||
|
n := nodes.Node()
|
||||||
id := n.ID()
|
id := n.ID()
|
||||||
if done.Has(id) {
|
if done.Has(id) {
|
||||||
continue
|
continue
|
||||||
@@ -35,7 +37,7 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node {
|
|||||||
u := tree.Pop()
|
u := tree.Pop()
|
||||||
uid := u.ID()
|
uid := u.ID()
|
||||||
adj := from[uid]
|
adj := from[uid]
|
||||||
for _, v := range g.From(uid) {
|
for _, v := range graph.NodesOf(g.From(uid)) {
|
||||||
vid := v.ID()
|
vid := v.ID()
|
||||||
switch {
|
switch {
|
||||||
case uid == vid:
|
case uid == vid:
|
||||||
|
@@ -94,16 +94,18 @@ func TarjanSCC(g graph.Directed) [][]graph.Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node {
|
func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node {
|
||||||
nodes := g.Nodes()
|
nodes := graph.NodesOf(g.Nodes())
|
||||||
var succ func(id int64) []graph.Node
|
var succ func(id int64) []graph.Node
|
||||||
if order == nil {
|
if order == nil {
|
||||||
succ = g.From
|
succ = func(id int64) []graph.Node {
|
||||||
|
return graph.NodesOf(g.From(id))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
order(nodes)
|
order(nodes)
|
||||||
ordered.Reverse(nodes)
|
ordered.Reverse(nodes)
|
||||||
|
|
||||||
succ = func(id int64) []graph.Node {
|
succ = func(id int64) []graph.Node {
|
||||||
to := g.From(id)
|
to := graph.NodesOf(g.From(id))
|
||||||
order(to)
|
order(to)
|
||||||
ordered.Reverse(to)
|
ordered.Reverse(to)
|
||||||
return to
|
return to
|
||||||
|
@@ -16,7 +16,7 @@ var _ Graph = graph.Graph(nil)
|
|||||||
type Graph interface {
|
type Graph interface {
|
||||||
// From returns all nodes that can be reached directly
|
// From returns all nodes that can be reached directly
|
||||||
// from the node with the given ID.
|
// from the node with the given ID.
|
||||||
From(id int64) []graph.Node
|
From(id int64) graph.Nodes
|
||||||
|
|
||||||
// Edge returns the edge from u to v, with IDs uid and vid,
|
// Edge returns the edge from u to v, with IDs uid and vid,
|
||||||
// if such an edge exists and nil otherwise. The node v
|
// if such an edge exists and nil otherwise. The node v
|
||||||
@@ -56,7 +56,9 @@ func (b *BreadthFirst) Walk(g Graph, from graph.Node, until func(n graph.Node, d
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
tid := t.ID()
|
tid := t.ID()
|
||||||
for _, n := range g.From(tid) {
|
to := g.From(tid)
|
||||||
|
for to.Next() {
|
||||||
|
n := to.Node()
|
||||||
nid := n.ID()
|
nid := n.ID()
|
||||||
if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(tid, nid)) {
|
if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(tid, nid)) {
|
||||||
continue
|
continue
|
||||||
@@ -87,7 +89,9 @@ func (b *BreadthFirst) Walk(g Graph, from graph.Node, until func(n graph.Node, d
|
|||||||
// during is called on each node as it is traversed.
|
// during is called on each node as it is traversed.
|
||||||
func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
|
func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
|
||||||
b.Reset()
|
b.Reset()
|
||||||
for _, from := range g.Nodes() {
|
nodes := g.Nodes()
|
||||||
|
for nodes.Next() {
|
||||||
|
from := nodes.Node()
|
||||||
if b.Visited(from) {
|
if b.Visited(from) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -143,7 +147,9 @@ func (d *DepthFirst) Walk(g Graph, from graph.Node, until func(graph.Node) bool)
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
tid := t.ID()
|
tid := t.ID()
|
||||||
for _, n := range g.From(tid) {
|
to := g.From(tid)
|
||||||
|
for to.Next() {
|
||||||
|
n := to.Node()
|
||||||
nid := n.ID()
|
nid := n.ID()
|
||||||
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(tid, nid)) {
|
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(tid, nid)) {
|
||||||
continue
|
continue
|
||||||
@@ -168,7 +174,9 @@ func (d *DepthFirst) Walk(g Graph, from graph.Node, until func(graph.Node) bool)
|
|||||||
// during is called on each node as it is traversed.
|
// during is called on each node as it is traversed.
|
||||||
func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
|
func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
|
||||||
d.Reset()
|
d.Reset()
|
||||||
for _, from := range g.Nodes() {
|
nodes := g.Nodes()
|
||||||
|
for nodes.Next() {
|
||||||
|
from := nodes.Node()
|
||||||
if d.Visited(from) {
|
if d.Visited(from) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -371,7 +371,7 @@ func gnpUndirected(n int, p float64) graph.Undirected {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkWalkAllBreadthFirst(b *testing.B, g graph.Undirected) {
|
func benchmarkWalkAllBreadthFirst(b *testing.B, g graph.Undirected) {
|
||||||
n := len(g.Nodes())
|
n := g.Nodes().Len()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
var bft BreadthFirst
|
var bft BreadthFirst
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
@@ -402,7 +402,7 @@ func BenchmarkWalkAllBreadthFirstGnp_1000_half(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkWalkAllDepthFirst(b *testing.B, g graph.Undirected) {
|
func benchmarkWalkAllDepthFirst(b *testing.B, g graph.Undirected) {
|
||||||
n := len(g.Nodes())
|
n := g.Nodes().Len()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
var dft DepthFirst
|
var dft DepthFirst
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
@@ -15,25 +15,11 @@ var _ Undirected = Undirect{}
|
|||||||
func (g Undirect) Has(id int64) bool { return g.G.Has(id) }
|
func (g Undirect) Has(id int64) bool { return g.G.Has(id) }
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g Undirect) Nodes() []Node { return g.G.Nodes() }
|
func (g Undirect) Nodes() Nodes { return g.G.Nodes() }
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from u.
|
// From returns all nodes in g that can be reached directly from u.
|
||||||
func (g Undirect) From(uid int64) []Node {
|
func (g Undirect) From(uid int64) Nodes {
|
||||||
var nodes []Node
|
return newNodeFilterIterator(g.G.From(uid), g.G.To(uid))
|
||||||
seen := make(map[int64]struct{})
|
|
||||||
for _, n := range g.G.From(uid) {
|
|
||||||
seen[n.ID()] = struct{}{}
|
|
||||||
nodes = append(nodes, n)
|
|
||||||
}
|
|
||||||
for _, n := range g.G.To(uid) {
|
|
||||||
id := n.ID()
|
|
||||||
if _, ok := seen[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[n.ID()] = struct{}{}
|
|
||||||
nodes = append(nodes, n)
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -94,25 +80,11 @@ var (
|
|||||||
func (g UndirectWeighted) Has(id int64) bool { return g.G.Has(id) }
|
func (g UndirectWeighted) Has(id int64) bool { return g.G.Has(id) }
|
||||||
|
|
||||||
// Nodes returns all the nodes in the graph.
|
// Nodes returns all the nodes in the graph.
|
||||||
func (g UndirectWeighted) Nodes() []Node { return g.G.Nodes() }
|
func (g UndirectWeighted) Nodes() Nodes { return g.G.Nodes() }
|
||||||
|
|
||||||
// From returns all nodes in g that can be reached directly from u.
|
// From returns all nodes in g that can be reached directly from u.
|
||||||
func (g UndirectWeighted) From(uid int64) []Node {
|
func (g UndirectWeighted) From(uid int64) Nodes {
|
||||||
var nodes []Node
|
return newNodeFilterIterator(g.G.From(uid), g.G.To(uid))
|
||||||
seen := make(map[int64]struct{})
|
|
||||||
for _, n := range g.G.From(uid) {
|
|
||||||
seen[n.ID()] = struct{}{}
|
|
||||||
nodes = append(nodes, n)
|
|
||||||
}
|
|
||||||
for _, n := range g.G.To(uid) {
|
|
||||||
id := n.ID()
|
|
||||||
if _, ok := seen[id]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
seen[n.ID()] = struct{}{}
|
|
||||||
nodes = append(nodes, n)
|
|
||||||
}
|
|
||||||
return nodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
// HasEdgeBetween returns whether an edge exists between nodes x and y.
|
||||||
@@ -224,3 +196,54 @@ type WeightedEdgePair struct {
|
|||||||
|
|
||||||
// Weight returns the merged edge weights of the two edges.
|
// Weight returns the merged edge weights of the two edges.
|
||||||
func (e WeightedEdgePair) Weight() float64 { return e.W }
|
func (e WeightedEdgePair) Weight() float64 { return e.W }
|
||||||
|
|
||||||
|
// nodeFilterIterator combines two Nodes to produce a single stream of
|
||||||
|
// unique nodes.
|
||||||
|
type nodeFilterIterator struct {
|
||||||
|
a, b Nodes
|
||||||
|
|
||||||
|
// unique indicates the node in b with the key ID is unique.
|
||||||
|
unique map[int64]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNodeFilterIterator(a, b Nodes) *nodeFilterIterator {
|
||||||
|
n := nodeFilterIterator{a: a, b: b, unique: make(map[int64]bool)}
|
||||||
|
for n.b.Next() {
|
||||||
|
n.unique[n.b.Node().ID()] = true
|
||||||
|
}
|
||||||
|
n.b.Reset()
|
||||||
|
for n.a.Next() {
|
||||||
|
n.unique[n.a.Node().ID()] = false
|
||||||
|
}
|
||||||
|
n.a.Reset()
|
||||||
|
return &n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nodeFilterIterator) Len() int {
|
||||||
|
return len(n.unique)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nodeFilterIterator) Next() bool {
|
||||||
|
n.Len()
|
||||||
|
if n.a.Next() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for n.b.Next() {
|
||||||
|
if n.unique[n.b.Node().ID()] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nodeFilterIterator) Node() Node {
|
||||||
|
if n.a.Len() != 0 {
|
||||||
|
return n.a.Node()
|
||||||
|
}
|
||||||
|
return n.b.Node()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nodeFilterIterator) Reset() {
|
||||||
|
n.a.Reset()
|
||||||
|
n.b.Reset()
|
||||||
|
}
|
||||||
|
@@ -121,9 +121,10 @@ func TestUndirect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
src := graph.Undirect{G: g}
|
src := graph.Undirect{G: g}
|
||||||
dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0)
|
nodes := graph.NodesOf(src.Nodes())
|
||||||
for _, u := range src.Nodes() {
|
dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0)
|
||||||
for _, v := range src.From(u.ID()) {
|
for _, u := range nodes {
|
||||||
|
for _, v := range graph.NodesOf(src.From(u.ID())) {
|
||||||
dst.SetEdge(src.Edge(u.ID(), v.ID()))
|
dst.SetEdge(src.Edge(u.ID(), v.ID()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,9 +147,10 @@ func TestUndirectWeighted(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
src := graph.UndirectWeighted{G: g, Absent: test.absent, Merge: test.merge}
|
src := graph.UndirectWeighted{G: g, Absent: test.absent, Merge: test.merge}
|
||||||
dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0)
|
nodes := graph.NodesOf(src.Nodes())
|
||||||
for _, u := range src.Nodes() {
|
dst := simple.NewUndirectedMatrixFrom(nodes, 0, 0, 0)
|
||||||
for _, v := range src.From(u.ID()) {
|
for _, u := range nodes {
|
||||||
|
for _, v := range graph.NodesOf(src.From(u.ID())) {
|
||||||
dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID()))
|
dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user