diff --git a/graph/community/bisect_test.go b/graph/community/bisect_test.go index 37e20370..12a75dbb 100644 --- a/graph/community/bisect_test.go +++ b/graph/community/bisect_test.go @@ -62,7 +62,7 @@ func init() { friends = simple.NewWeightedUndirectedGraph(0, 0) for u, e := range middleEast.friends { // Ensure unconnected nodes are included. - if !friends.Has(simple.Node(u)) { + if !friends.Has(int64(u)) { friends.AddNode(simple.Node(u)) } for v := range e { @@ -72,7 +72,7 @@ func init() { enemies = simple.NewWeightedUndirectedGraph(0, 0) for u, e := range middleEast.enemies { // Ensure unconnected nodes are included. - if !enemies.Has(simple.Node(u)) { + if !enemies.Has(int64(u)) { enemies.AddNode(simple.Node(u)) } for v := range e { @@ -127,7 +127,7 @@ func TestProfileUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -144,7 +144,7 @@ func TestProfileWeightedUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -186,7 +186,7 @@ func TestProfileDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -203,7 +203,7 @@ func TestProfileWeightedDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/community/k_communities_test.go b/graph/community/k_communities_test.go index 9861b2d7..46a31e64 100644 --- a/graph/community/k_communities_test.go +++ b/graph/community/k_communities_test.go @@ -111,7 +111,7 @@ func TestKCliqueCommunities(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/community/louvain_common.go b/graph/community/louvain_common.go index be68e094..f0ebce39 100644 --- a/graph/community/louvain_common.go +++ b/graph/community/louvain_common.go @@ -359,10 +359,10 @@ const ( // positiveWeightFuncFor returns a constructed weight function for the // positively weighted g. Unweighted graphs have unit weight for existing // edges. -func positiveWeightFuncFor(g graph.Graph) func(x, y graph.Node) float64 { +func positiveWeightFuncFor(g graph.Graph) func(xid, yid int64) float64 { if wg, ok := g.(graph.Weighted); ok { - return func(x, y graph.Node) float64 { - w, ok := wg.Weight(x, y) + return func(xid, yid int64) float64 { + w, ok := wg.Weight(xid, yid) if !ok { return 0 } @@ -372,8 +372,8 @@ func positiveWeightFuncFor(g graph.Graph) func(x, y graph.Node) float64 { return w } } - return func(x, y graph.Node) float64 { - e := g.Edge(x, y) + return func(xid, yid int64) float64 { + e := g.Edge(xid, yid) if e == nil { return 0 } @@ -384,10 +384,10 @@ func positiveWeightFuncFor(g graph.Graph) func(x, y graph.Node) float64 { // negativeWeightFuncFor returns a constructed weight function for the // negatively weighted g. Unweighted graphs have unit weight for existing // edges. -func negativeWeightFuncFor(g graph.Graph) func(x, y graph.Node) float64 { +func negativeWeightFuncFor(g graph.Graph) func(xid, yid int64) float64 { if wg, ok := g.(graph.Weighted); ok { - return func(x, y graph.Node) float64 { - w, ok := wg.Weight(x, y) + return func(xid, yid int64) float64 { + w, ok := wg.Weight(xid, yid) if !ok { return 0 } @@ -397,8 +397,8 @@ func negativeWeightFuncFor(g graph.Graph) func(x, y graph.Node) float64 { return -w } } - return func(x, y graph.Node) float64 { - e := g.Edge(x, y) + return func(xid, yid int64) float64 { + e := g.Edge(xid, yid) if e == nil { return 0 } diff --git a/graph/community/louvain_directed.go b/graph/community/louvain_directed.go index bb4ef6c9..ce26cb71 100644 --- a/graph/community/louvain_directed.go +++ b/graph/community/louvain_directed.go @@ -34,24 +34,28 @@ func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) for _, n := range nodes { var wOut float64 u := n - for _, v := range g.From(u) { - wOut += weight(u, v) + uid := u.ID() + for _, v := range g.From(uid) { + wOut += weight(uid, v.ID()) } var wIn float64 v := n - for _, u := range g.To(v) { - wIn += weight(u, v) + vid := v.ID() + for _, u := range g.To(vid) { + wIn += weight(u.ID(), vid) } - w := weight(n, n) + id := n.ID() + w := weight(id, id) m += w + wOut // We only need to count edges once. - k[n.ID()] = directedWeights{out: w + wOut, in: w + wIn} + k[id] = directedWeights{out: w + wOut, in: w + wIn} } if communities == nil { var q float64 for _, u := range nodes { - kU := k[u.ID()] - q += weight(u, u) - resolution*kU.out*kU.in/m + uid := u.ID() + kU := k[uid] + q += weight(uid, uid) - resolution*kU.out*kU.in/m } return q / m } @@ -59,10 +63,12 @@ func qDirected(g graph.Directed, communities [][]graph.Node, resolution float64) var q float64 for _, c := range communities { for _, u := range c { - kU := k[u.ID()] + uid := u.ID() + kU := k[uid] for _, v := range c { - kV := k[v.ID()] - q += weight(u, v) - resolution*kU.out*kV.in/m + vid := v.ID() + kV := k[vid] + q += weight(uid, vid) - resolution*kU.out*kV.in/m } } } @@ -203,23 +209,27 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect var out []int u := n - for _, v := range g.From(u) { - vid := communityOf[v.ID()] - if vid != id { - out = append(out, vid) + uid := u.ID() + for _, v := range g.From(uid) { + vid := v.ID() + vcid := communityOf[vid] + if vcid != id { + out = append(out, vcid) } - r.weights[[2]int{id, vid}] = weight(u, v) + r.weights[[2]int{id, vcid}] = weight(uid, vid) } r.edgesFrom[id] = out var in []int v := n - for _, u := range g.To(v) { - uid := communityOf[u.ID()] - if uid != id { - in = append(in, uid) + vid := v.ID() + for _, u := range g.To(vid) { + uid := u.ID() + ucid := communityOf[uid] + if ucid != id { + in = append(in, ucid) } - r.weights[[2]int{uid, id}] = weight(u, v) + r.weights[[2]int{ucid, id}] = weight(uid, vid) } r.edgesTo[id] = in } @@ -270,43 +280,47 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect var out, in []int for _, n := range comm { u := n + uid := u.ID() for _, v := range comm { - r.nodes[id].weight += weight(u, v) + r.nodes[id].weight += weight(uid, v.ID()) } - for _, v := range g.From(u) { - vid := communityOf[v.ID()] + for _, v := range g.From(uid) { + vid := v.ID() + vcid := communityOf[vid] found := false for _, e := range out { - if e == vid { + if e == vcid { found = true break } } - if !found && vid != id { - out = append(out, vid) + if !found && vcid != id { + out = append(out, vcid) } // Add half weights because the other // ends of edges are also counted. - r.weights[[2]int{id, vid}] += weight(u, v) / 2 + r.weights[[2]int{id, vcid}] += weight(uid, vid) / 2 } v := n - for _, u := range g.To(v) { - uid := communityOf[u.ID()] + vid := v.ID() + for _, u := range g.To(vid) { + uid := u.ID() + ucid := communityOf[uid] found := false for _, e := range in { - if e == uid { + if e == ucid { found = true break } } - if !found && uid != id { - in = append(in, uid) + if !found && ucid != id { + in = append(in, ucid) } // Add half weights because the other // ends of edges are also counted. - r.weights[[2]int{uid, id}] += weight(u, v) / 2 + r.weights[[2]int{ucid, id}] += weight(uid, vid) / 2 } } r.edgesFrom[id] = out @@ -316,8 +330,7 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect } // Has returns whether the node exists within the graph. -func (g *ReducedDirected) Has(n graph.Node) bool { - id := n.ID() +func (g *ReducedDirected) Has(id int64) bool { return 0 <= id && id < int64(len(g.nodes)) } @@ -331,8 +344,8 @@ func (g *ReducedDirected) Nodes() []graph.Node { } // From returns all nodes in g that can be reached directly from u. -func (g *ReducedDirected) From(u graph.Node) []graph.Node { - out := g.edgesFrom[u.ID()] +func (g *ReducedDirected) From(uid int64) []graph.Node { + out := g.edgesFrom[uid] nodes := make([]graph.Node, len(out)) for i, vid := range out { nodes[i] = g.nodes[vid] @@ -341,8 +354,8 @@ func (g *ReducedDirected) From(u graph.Node) []graph.Node { } // To returns all nodes in g that can reach directly to v. -func (g *ReducedDirected) To(v graph.Node) []graph.Node { - in := g.edgesTo[v.ID()] +func (g *ReducedDirected) To(vid int64) []graph.Node { + in := g.edgesTo[vid] nodes := make([]graph.Node, len(in)) for i, uid := range in { nodes[i] = g.nodes[uid] @@ -351,9 +364,7 @@ func (g *ReducedDirected) To(v graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *ReducedDirected) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g *ReducedDirected) HasEdgeBetween(xid, yid int64) bool { if xid == yid || !isValidID(xid) || !isValidID(yid) { return false } @@ -366,9 +377,7 @@ func (g *ReducedDirected) HasEdgeBetween(x, y graph.Node) bool { } // HasEdgeFromTo returns whether an edge exists from node u to v. -func (g *ReducedDirected) HasEdgeFromTo(u, v graph.Node) bool { - uid := u.ID() - vid := v.ID() +func (g *ReducedDirected) HasEdgeFromTo(uid, vid int64) bool { if uid == vid || !isValidID(uid) || !isValidID(vid) { return false } @@ -378,15 +387,13 @@ func (g *ReducedDirected) HasEdgeFromTo(u, v graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *ReducedDirected) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdge(u, v) +func (g *ReducedDirected) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdge(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *ReducedDirected) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - uid := u.ID() - vid := v.ID() +func (g *ReducedDirected) WeightedEdge(uid, vid int64) graph.WeightedEdge { if uid == vid || !isValidID(uid) || !isValidID(vid) { return nil } @@ -401,9 +408,7 @@ func (g *ReducedDirected) WeightedEdge(u, v graph.Node) graph.WeightedEdge { // If x and y are the same node the internal node weight is returned. If there is no joining // edge between the two nodes the weight value returned is zero. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g *ReducedDirected) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g *ReducedDirected) Weight(xid, yid int64) (w float64, ok bool) { if !isValidID(xid) || !isValidID(yid) { return 0, false } @@ -433,7 +438,7 @@ type directedLocalMover struct { // that returns the Weight value // of the non-nil edge between x // and y. - weight func(x, y graph.Node) float64 + weight func(xid, yid int64) float64 // communities is the current // division of g. @@ -484,18 +489,21 @@ func newDirectedLocalMover(g *ReducedDirected, communities [][]graph.Node, resol for _, n := range l.nodes { u := n var wOut float64 - for _, v := range g.From(u) { - wOut += l.weight(u, v) + uid := u.ID() + for _, v := range g.From(uid) { + wOut += l.weight(uid, v.ID()) } v := n var wIn float64 - for _, u := range g.To(v) { - wIn += l.weight(u, v) + vid := v.ID() + for _, u := range g.To(vid) { + wIn += l.weight(u.ID(), vid) } - w := l.weight(n, n) - l.edgeWeightsOf[n.ID()] = directedWeights{out: w + wOut, in: w + wIn} + id := n.ID() + w := l.weight(id, id) + l.edgeWeightsOf[id] = directedWeights{out: w + wOut, in: w + wIn} l.m += w + wOut } @@ -562,7 +570,7 @@ func (l *directedLocalMover) move(dst int, src commIdx) { func (l *directedLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst int, src commIdx) { id := n.ID() - a_aa := l.weight(n, n) + a_aa := l.weight(id, id) k_a := l.edgeWeightsOf[id] m := l.m gamma := l.resolution @@ -613,8 +621,8 @@ func (l *directedLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst int, src removal = true } - k_aC.in += l.weight(u, n) - k_aC.out += l.weight(n, u) + k_aC.in += l.weight(uid, id) + k_aC.out += l.weight(id, uid) // sigma_totC could be kept for each community // and updated for moves, changing the calculation // of sigma_totC here from O(n_c) to O(1), but diff --git a/graph/community/louvain_directed_multiplex.go b/graph/community/louvain_directed_multiplex.go index 2f1a6060..a695b80a 100644 --- a/graph/community/louvain_directed_multiplex.go +++ b/graph/community/louvain_directed_multiplex.go @@ -63,7 +63,7 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights layerResolution = resolutions[l] } - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if layerWeight < 0 { weight = negativeWeightFuncFor(layer) } else { @@ -77,15 +77,18 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights for _, n := range nodes { var wOut float64 u := n - for _, v := range layer.From(u) { - wOut += weight(u, v) + uid := u.ID() + for _, v := range layer.From(uid) { + wOut += weight(uid, v.ID()) } var wIn float64 v := n - for _, u := range layer.To(v) { - wIn += weight(u, v) + vid := v.ID() + for _, u := range layer.To(vid) { + wIn += weight(u.ID(), vid) } - w := weight(n, n) + id := n.ID() + w := weight(id, id) m += w + wOut // We only need to count edges once. k[n.ID()] = directedWeights{out: w + wOut, in: w + wIn} } @@ -93,8 +96,9 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights if communities == nil { var qLayer float64 for _, u := range nodes { - kU := k[u.ID()] - qLayer += weight(u, u) - layerResolution*kU.out*kU.in/m + uid := u.ID() + kU := k[uid] + qLayer += weight(uid, uid) - layerResolution*kU.out*kU.in/m } q[l] = layerWeight * qLayer continue @@ -103,10 +107,12 @@ func qDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, weights var qLayer float64 for _, c := range communities { for _, u := range c { - kU := k[u.ID()] + uid := u.ID() + kU := k[uid] for _, v := range c { - kV := k[v.ID()] - qLayer += weight(u, v) - layerResolution*kU.out*kV.in/m + vid := v.ID() + kV := k[vid] + qLayer += weight(uid, vid) - layerResolution*kU.out*kV.in/m } } } @@ -321,7 +327,7 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we continue } var sign float64 - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if w < 0 { sign, weight = -1, negativeWeightFuncFor(layer) } else { @@ -332,23 +338,27 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we var out []int u := n - for _, v := range layer.From(u) { - vid := communityOf[v.ID()] - if vid != id { - out = append(out, vid) + uid := u.ID() + for _, v := range layer.From(uid) { + vid := v.ID() + vcid := communityOf[vid] + if vcid != id { + out = append(out, vcid) } - r.layers[l].weights[[2]int{id, vid}] = sign * weight(u, v) + r.layers[l].weights[[2]int{id, vcid}] = sign * weight(uid, vid) } r.layers[l].edgesFrom[id] = out var in []int v := n - for _, u := range layer.To(v) { - uid := communityOf[u.ID()] - if uid != id { - in = append(in, uid) + vid := v.ID() + for _, u := range layer.To(vid) { + uid := u.ID() + ucid := communityOf[uid] + if ucid != id { + in = append(in, ucid) } - r.layers[l].weights[[2]int{uid, id}] = sign * weight(u, v) + r.layers[l].weights[[2]int{ucid, id}] = sign * weight(uid, vid) } r.layers[l].edgesTo[id] = in } @@ -408,7 +418,7 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we continue } var sign float64 - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if w < 0 { sign, weight = -1, negativeWeightFuncFor(layer) } else { @@ -418,43 +428,47 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we var out, in []int for _, n := range comm { u := n + uid := u.ID() for _, v := range comm { - r.nodes[id].weights[l] += sign * weight(u, v) + r.nodes[id].weights[l] += sign * weight(uid, v.ID()) } - for _, v := range layer.From(u) { - vid := communityOf[v.ID()] + for _, v := range layer.From(uid) { + vid := v.ID() + vcid := communityOf[vid] found := false for _, e := range out { - if e == vid { + if e == vcid { found = true break } } - if !found && vid != id { - out = append(out, vid) + if !found && vcid != id { + out = append(out, vcid) } // Add half weights because the other // ends of edges are also counted. - r.layers[l].weights[[2]int{id, vid}] += sign * weight(u, v) / 2 + r.layers[l].weights[[2]int{id, vcid}] += sign * weight(uid, vid) / 2 } v := n - for _, u := range layer.To(v) { - uid := communityOf[u.ID()] + vid := v.ID() + for _, u := range layer.To(vid) { + uid := u.ID() + ucid := communityOf[uid] found := false for _, e := range in { - if e == uid { + if e == ucid { found = true break } } - if !found && uid != id { - in = append(in, uid) + if !found && ucid != id { + in = append(in, ucid) } // Add half weights because the other // ends of edges are also counted. - r.layers[l].weights[[2]int{uid, id}] += sign * weight(u, v) / 2 + r.layers[l].weights[[2]int{ucid, id}] += sign * weight(uid, vid) / 2 } } @@ -478,8 +492,7 @@ type directedLayerHandle struct { } // Has returns whether the node exists within the graph. -func (g directedLayerHandle) Has(n graph.Node) bool { - id := n.ID() +func (g directedLayerHandle) Has(id int64) bool { return 0 <= id && id < int64(len(g.multiplex.nodes)) } @@ -493,8 +506,8 @@ func (g directedLayerHandle) Nodes() []graph.Node { } // From returns all nodes in g that can be reached directly from u. -func (g directedLayerHandle) From(u graph.Node) []graph.Node { - out := g.multiplex.layers[g.layer].edgesFrom[u.ID()] +func (g directedLayerHandle) From(uid int64) []graph.Node { + out := g.multiplex.layers[g.layer].edgesFrom[uid] nodes := make([]graph.Node, len(out)) for i, vid := range out { nodes[i] = g.multiplex.nodes[vid] @@ -503,8 +516,8 @@ func (g directedLayerHandle) From(u graph.Node) []graph.Node { } // To returns all nodes in g that can reach directly to v. -func (g directedLayerHandle) To(v graph.Node) []graph.Node { - in := g.multiplex.layers[g.layer].edgesTo[v.ID()] +func (g directedLayerHandle) To(vid int64) []graph.Node { + in := g.multiplex.layers[g.layer].edgesTo[vid] nodes := make([]graph.Node, len(in)) for i, uid := range in { nodes[i] = g.multiplex.nodes[uid] @@ -513,9 +526,7 @@ func (g directedLayerHandle) To(v graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g directedLayerHandle) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g directedLayerHandle) HasEdgeBetween(xid, yid int64) bool { if xid == yid { return false } @@ -531,9 +542,7 @@ func (g directedLayerHandle) HasEdgeBetween(x, y graph.Node) bool { } // HasEdgeFromTo returns whether an edge exists from node u to v. -func (g directedLayerHandle) HasEdgeFromTo(u, v graph.Node) bool { - uid := u.ID() - vid := v.ID() +func (g directedLayerHandle) HasEdgeFromTo(uid, vid int64) bool { if uid == vid || !isValidID(uid) || !isValidID(vid) { return false } @@ -543,15 +552,13 @@ func (g directedLayerHandle) HasEdgeFromTo(u, v graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g directedLayerHandle) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdge(u, v) +func (g directedLayerHandle) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdge(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g directedLayerHandle) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - uid := u.ID() - vid := v.ID() +func (g directedLayerHandle) WeightedEdge(uid, vid int64) graph.WeightedEdge { if uid == vid || !isValidID(uid) || !isValidID(vid) { return nil } @@ -559,16 +566,14 @@ func (g directedLayerHandle) WeightedEdge(u, v graph.Node) graph.WeightedEdge { if !ok { return nil } - return multiplexEdge{from: g.multiplex.nodes[u.ID()], to: g.multiplex.nodes[v.ID()], weight: w} + return multiplexEdge{from: g.multiplex.nodes[uid], to: g.multiplex.nodes[vid], weight: w} } // Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge. // If x and y are the same node the internal node weight is returned. If there is no joining // edge between the two nodes the weight value returned is zero. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g directedLayerHandle) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g directedLayerHandle) Weight(xid, yid int64) (w float64, ok bool) { if !isValidID(xid) || !isValidID(yid) { return 0, false } @@ -598,7 +603,7 @@ type directedMultiplexLocalMover struct { // that returns the Weight value // of the non-nil edge between x // and y. - weight []func(x, y graph.Node) float64 + weight []func(xid, yid int64) float64 // communities is the current // division of g. @@ -648,7 +653,7 @@ func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][ memberships: make([]int, len(nodes)), resolutions: resolutions, weights: weights, - weight: make([]func(x, y graph.Node) float64, g.Depth()), + weight: make([]func(xid, yid int64) float64, g.Depth()), } // Calculate the total edge weight of the graph @@ -656,7 +661,7 @@ func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][ var zero int for i := 0; i < g.Depth(); i++ { l.edgeWeightsOf[i] = make([]directedWeights, len(nodes)) - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if weights != nil { if weights[i] == 0 { @@ -677,19 +682,22 @@ func newDirectedMultiplexLocalMover(g *ReducedDirectedMultiplex, communities [][ layer := g.Layer(i) for _, n := range l.nodes { u := n + uid := u.ID() var wOut float64 - for _, v := range layer.From(u) { - wOut += weight(u, v) + for _, v := range layer.From(uid) { + wOut += weight(uid, v.ID()) } v := n + vid := v.ID() var wIn float64 - for _, u := range layer.To(v) { - wIn += weight(u, v) + for _, u := range layer.To(vid) { + wIn += weight(u.ID(), vid) } - w := weight(n, n) - l.edgeWeightsOf[i][u.ID()] = directedWeights{out: w + wOut, in: w + wIn} + id := n.ID() + w := weight(id, id) + l.edgeWeightsOf[i][uid] = directedWeights{out: w + wOut, in: w + wIn} l.m[i] += w + wOut } if l.m[i] == 0 { @@ -836,8 +844,8 @@ func (l *directedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst removal = true } - k_aC.in += l.weight[layer](n, u) - k_aC.out += l.weight[layer](u, n) + k_aC.in += l.weight[layer](id, uid) + k_aC.out += l.weight[layer](uid, id) // sigma_totC could be kept for each community // and updated for moves, changing the calculation // of sigma_totC here from O(n_c) to O(1), but @@ -849,7 +857,7 @@ func (l *directedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst sigma_totC.out += w.out } - a_aa := l.weight[layer](n, n) + a_aa := l.weight[layer](id, id) k_a := l.edgeWeightsOf[layer][id] gamma := 1.0 if l.resolutions != nil { diff --git a/graph/community/louvain_directed_multiplex_test.go b/graph/community/louvain_directed_multiplex_test.go index 5f6082ed..f1523f12 100644 --- a/graph/community/louvain_directed_multiplex_test.go +++ b/graph/community/louvain_directed_multiplex_test.go @@ -390,7 +390,7 @@ tests: } layer := g.Layer(l) for n := range c { - if layer.HasEdgeBetween(simple.Node(n), target) { + if layer.HasEdgeBetween(int64(n), target.ID()) { connected = true break search } @@ -701,7 +701,7 @@ func directedMultiplexFrom(raw []layer) (DirectedLayers, []float64, error) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range l.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/community/louvain_directed_test.go b/graph/community/louvain_directed_test.go index 3a649791..5f17d2ff 100644 --- a/graph/community/louvain_directed_test.go +++ b/graph/community/louvain_directed_test.go @@ -204,7 +204,7 @@ func TestCommunityQDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -221,7 +221,7 @@ func TestCommunityQWeightedDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -257,7 +257,7 @@ func TestCommunityDeltaQDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -274,7 +274,7 @@ func TestCommunityDeltaQWeightedDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -338,7 +338,7 @@ func testCommunityDeltaQDirected(t *testing.T, test communityDirectedQTest, g gr } connected := false for n := range c { - if g.HasEdgeBetween(simple.Node(n), target) { + if g.HasEdgeBetween(int64(n), target.ID()) { connected = true break } @@ -382,7 +382,7 @@ func TestReduceQConsistencyDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -399,7 +399,7 @@ func TestReduceQConsistencyWeightedDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -500,7 +500,7 @@ func TestMoveLocalDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -517,7 +517,7 @@ func TestMoveLocalWeightedDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -562,7 +562,7 @@ func TestModularizeDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -579,7 +579,7 @@ func TestModularizeWeightedDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/community/louvain_undirected.go b/graph/community/louvain_undirected.go index 838708cb..0f33d3f4 100644 --- a/graph/community/louvain_undirected.go +++ b/graph/community/louvain_undirected.go @@ -34,19 +34,21 @@ func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution floa var m2 float64 k := make(map[int64]float64, len(nodes)) for _, u := range nodes { - w := weight(u, u) - for _, v := range g.From(u) { - w += weight(u, v) + uid := u.ID() + w := weight(uid, uid) + for _, v := range g.From(uid) { + w += weight(uid, v.ID()) } m2 += w - k[u.ID()] = w + k[uid] = w } if communities == nil { var q float64 for _, u := range nodes { - kU := k[u.ID()] - q += weight(u, u) - resolution*kU*kU/m2 + uid := u.ID() + kU := k[uid] + q += weight(uid, uid) - resolution*kU*kU/m2 } return q / m2 } @@ -57,10 +59,12 @@ func qUndirected(g graph.Undirected, communities [][]graph.Node, resolution floa var q float64 for _, c := range communities { for i, u := range c { - kU := k[u.ID()] - q += weight(u, u) - resolution*kU*kU/m2 + uid := u.ID() + kU := k[uid] + q += weight(uid, uid) - resolution*kU*kU/m2 for _, v := range c[i+1:] { - q += 2 * (weight(u, v) - resolution*kU*k[v.ID()]/m2) + vid := v.ID() + q += 2 * (weight(uid, vid) - resolution*kU*k[vid]/m2) } } } @@ -198,19 +202,21 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn communityOf[n.ID()] = i } for _, u := range nodes { + uid := u.ID() + ucid := communityOf[uid] var out []int - uid := communityOf[u.ID()] - for _, v := range g.From(u) { - vid := communityOf[v.ID()] - if vid != uid { - out = append(out, vid) + for _, v := range g.From(uid) { + vid := v.ID() + vcid := communityOf[vid] + if vcid != ucid { + out = append(out, vcid) } - if uid < vid { + if ucid < vcid { // Only store the weight once. - r.weights[[2]int{uid, vid}] = weight(u, v) + r.weights[[2]int{ucid, vcid}] = weight(uid, vid) } } - r.edges[uid] = out + r.edges[ucid] = out } return &r } @@ -254,39 +260,40 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn communityOf[n.ID()] = i } } - for uid, comm := range communities { + for ucid, comm := range communities { var out []int for i, u := range comm { - r.nodes[uid].weight += weight(u, u) + uid := u.ID() + r.nodes[ucid].weight += weight(uid, uid) for _, v := range comm[i+1:] { - r.nodes[uid].weight += 2 * weight(u, v) + r.nodes[ucid].weight += 2 * weight(uid, v.ID()) } - for _, v := range g.From(u) { - vid := communityOf[v.ID()] + for _, v := range g.From(uid) { + vid := v.ID() + vcid := communityOf[vid] found := false for _, e := range out { - if e == vid { + if e == vcid { found = true break } } - if !found && vid != uid { - out = append(out, vid) + if !found && vcid != ucid { + out = append(out, vcid) } - if uid < vid { + if ucid < vcid { // Only store the weight once. - r.weights[[2]int{uid, vid}] += weight(u, v) + r.weights[[2]int{ucid, vcid}] += weight(uid, vid) } } } - r.edges[uid] = out + r.edges[ucid] = out } return &r } // Has returns whether the node exists within the graph. -func (g *ReducedUndirected) Has(n graph.Node) bool { - id := n.ID() +func (g *ReducedUndirected) Has(id int64) bool { return 0 <= id || id < int64(len(g.nodes)) } @@ -300,8 +307,8 @@ func (g *ReducedUndirected) Nodes() []graph.Node { } // From returns all nodes in g that can be reached directly from u. -func (g *ReducedUndirected) From(u graph.Node) []graph.Node { - out := g.edges[u.ID()] +func (g *ReducedUndirected) From(uid int64) []graph.Node { + out := g.edges[uid] nodes := make([]graph.Node, len(out)) for i, vid := range out { nodes[i] = g.nodes[vid] @@ -310,9 +317,7 @@ func (g *ReducedUndirected) From(u graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *ReducedUndirected) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g *ReducedUndirected) HasEdgeBetween(xid, yid int64) bool { if xid == yid || !isValidID(xid) || !isValidID(yid) { return false } @@ -325,25 +330,23 @@ func (g *ReducedUndirected) HasEdgeBetween(x, y graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *ReducedUndirected) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g *ReducedUndirected) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *ReducedUndirected) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - return g.WeightedEdgeBetween(u, v) +func (g *ReducedUndirected) WeightedEdge(uid, vid int64) graph.WeightedEdge { + return g.WeightedEdgeBetween(uid, vid) } // EdgeBetween returns the edge between nodes x and y. -func (g *ReducedUndirected) EdgeBetween(x, y graph.Node) graph.Edge { - return g.WeightedEdgeBetween(x, y) +func (g *ReducedUndirected) EdgeBetween(xid, yid int64) graph.Edge { + return g.WeightedEdgeBetween(xid, yid) } // WeightedEdgeBetween returns the weighted edge between nodes x and y. -func (g *ReducedUndirected) WeightedEdgeBetween(x, y graph.Node) graph.WeightedEdge { - xid := x.ID() - yid := y.ID() +func (g *ReducedUndirected) WeightedEdgeBetween(xid, yid int64) graph.WeightedEdge { if xid == yid || !isValidID(xid) || !isValidID(yid) { return nil } @@ -354,16 +357,14 @@ func (g *ReducedUndirected) WeightedEdgeBetween(x, y graph.Node) graph.WeightedE if !ok { return nil } - return edge{from: g.nodes[x.ID()], to: g.nodes[y.ID()], weight: w} + return edge{from: g.nodes[xid], to: g.nodes[yid], weight: w} } // Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge. // If x and y are the same node the internal node weight is returned. If there is no joining // edge between the two nodes the weight value returned is zero. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g *ReducedUndirected) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g *ReducedUndirected) Weight(xid, yid int64) (w float64, ok bool) { if !isValidID(xid) || !isValidID(yid) { return 0, false } @@ -396,7 +397,7 @@ type undirectedLocalMover struct { // that returns the Weight value // of the non-nil edge between x // and y. - weight func(x, y graph.Node) float64 + weight func(xid, yid int64) float64 // communities is the current // division of g. @@ -440,11 +441,12 @@ func newUndirectedLocalMover(g *ReducedUndirected, communities [][]graph.Node, r // Calculate the total edge weight of the graph // and degree weights for each node. for _, u := range l.nodes { - w := l.weight(u, u) - for _, v := range g.From(u) { - w += l.weight(u, v) + uid := u.ID() + w := l.weight(uid, uid) + for _, v := range g.From(uid) { + w += l.weight(uid, v.ID()) } - l.edgeWeightOf[u.ID()] = w + l.edgeWeightOf[uid] = w l.m2 += w } if l.m2 == 0 { @@ -514,7 +516,7 @@ func (l *undirectedLocalMover) move(dst int, src commIdx) { // is in communities. func (l *undirectedLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst int, src commIdx) { id := n.ID() - a_aa := l.weight(n, n) + a_aa := l.weight(id, id) k_a := l.edgeWeightOf[id] m2 := l.m2 gamma := l.resolution @@ -559,7 +561,7 @@ func (l *undirectedLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst int, sr removal = true } - k_aC += l.weight(n, u) + k_aC += l.weight(id, uid) // sigma_totC could be kept for each community // and updated for moves, changing the calculation // of sigma_totC here from O(n_c) to O(1), but diff --git a/graph/community/louvain_undirected_multiplex.go b/graph/community/louvain_undirected_multiplex.go index 39f51714..794d90dd 100644 --- a/graph/community/louvain_undirected_multiplex.go +++ b/graph/community/louvain_undirected_multiplex.go @@ -66,7 +66,7 @@ func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, wei layerResolution = resolutions[l] } - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if layerWeight < 0 { weight = negativeWeightFuncFor(layer) } else { @@ -78,19 +78,21 @@ func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, wei var m2 float64 k := make(map[int64]float64, len(nodes)) for _, u := range nodes { - w := weight(u, u) - for _, v := range layer.From(u) { - w += weight(u, v) + uid := u.ID() + w := weight(uid, uid) + for _, v := range layer.From(uid) { + w += weight(uid, v.ID()) } m2 += w - k[u.ID()] = w + k[uid] = w } if communities == nil { var qLayer float64 for _, u := range nodes { - kU := k[u.ID()] - qLayer += weight(u, u) - layerResolution*kU*kU/m2 + uid := u.ID() + kU := k[uid] + qLayer += weight(uid, uid) - layerResolution*kU*kU/m2 } q[l] = layerWeight * qLayer continue @@ -102,10 +104,12 @@ func qUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node, wei var qLayer float64 for _, c := range communities { for i, u := range c { - kU := k[u.ID()] - qLayer += weight(u, u) - layerResolution*kU*kU/m2 + uid := u.ID() + kU := k[uid] + qLayer += weight(uid, uid) - layerResolution*kU*kU/m2 for _, v := range c[i+1:] { - qLayer += 2 * (weight(u, v) - layerResolution*kU*k[v.ID()]/m2) + vid := v.ID() + qLayer += 2 * (weight(uid, vid) - layerResolution*kU*k[vid]/m2) } } } @@ -319,7 +323,7 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node continue } var sign float64 - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if w < 0 { sign, weight = -1, negativeWeightFuncFor(layer) } else { @@ -327,18 +331,20 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node } for _, u := range nodes { var out []int - uid := communityOf[u.ID()] - for _, v := range layer.From(u) { - vid := communityOf[v.ID()] - if vid != uid { - out = append(out, vid) + uid := u.ID() + ucid := communityOf[uid] + for _, v := range layer.From(uid) { + vid := v.ID() + vcid := communityOf[vid] + if vcid != ucid { + out = append(out, vcid) } - if uid < vid { + if ucid < vcid { // Only store the weight once. - r.layers[l].weights[[2]int{uid, vid}] = sign * weight(u, v) + r.layers[l].weights[[2]int{ucid, vcid}] = sign * weight(uid, vid) } } - r.layers[l].edges[uid] = out + r.layers[l].edges[ucid] = out } } return &r @@ -395,38 +401,40 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node continue } var sign float64 - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if w < 0 { sign, weight = -1, negativeWeightFuncFor(layer) } else { sign, weight = 1, positiveWeightFuncFor(layer) } - for uid, comm := range communities { + for ucid, comm := range communities { var out []int for i, u := range comm { - r.nodes[uid].weights[l] += sign * weight(u, u) + uid := u.ID() + r.nodes[ucid].weights[l] += sign * weight(uid, uid) for _, v := range comm[i+1:] { - r.nodes[uid].weights[l] += 2 * sign * weight(u, v) + r.nodes[ucid].weights[l] += 2 * sign * weight(uid, v.ID()) } - for _, v := range layer.From(u) { - vid := communityOf[v.ID()] + for _, v := range layer.From(uid) { + vid := v.ID() + vcid := communityOf[vid] found := false for _, e := range out { - if e == vid { + if e == vcid { found = true break } } - if !found && vid != uid { - out = append(out, vid) + if !found && vcid != ucid { + out = append(out, vcid) } - if uid < vid { + if ucid < vcid { // Only store the weight once. - r.layers[l].weights[[2]int{uid, vid}] += sign * weight(u, v) + r.layers[l].weights[[2]int{ucid, vcid}] += sign * weight(uid, vid) } } } - r.layers[l].edges[uid] = out + r.layers[l].edges[ucid] = out } } return &r @@ -445,8 +453,7 @@ type undirectedLayerHandle struct { } // Has returns whether the node exists within the graph. -func (g undirectedLayerHandle) Has(n graph.Node) bool { - id := n.ID() +func (g undirectedLayerHandle) Has(id int64) bool { return 0 <= id && id < int64(len(g.multiplex.nodes)) } @@ -460,8 +467,8 @@ func (g undirectedLayerHandle) Nodes() []graph.Node { } // From returns all nodes in g that can be reached directly from u. -func (g undirectedLayerHandle) From(u graph.Node) []graph.Node { - out := g.multiplex.layers[g.layer].edges[u.ID()] +func (g undirectedLayerHandle) From(uid int64) []graph.Node { + out := g.multiplex.layers[g.layer].edges[uid] nodes := make([]graph.Node, len(out)) for i, vid := range out { nodes[i] = g.multiplex.nodes[vid] @@ -470,9 +477,7 @@ func (g undirectedLayerHandle) From(u graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g undirectedLayerHandle) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g undirectedLayerHandle) HasEdgeBetween(xid, yid int64) bool { if xid == yid || !isValidID(xid) || !isValidID(yid) { return false } @@ -485,25 +490,23 @@ func (g undirectedLayerHandle) HasEdgeBetween(x, y graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g undirectedLayerHandle) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g undirectedLayerHandle) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g undirectedLayerHandle) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - return g.WeightedEdgeBetween(u, v) +func (g undirectedLayerHandle) WeightedEdge(uid, vid int64) graph.WeightedEdge { + return g.WeightedEdgeBetween(uid, vid) } // EdgeBetween returns the edge between nodes x and y. -func (g undirectedLayerHandle) EdgeBetween(x, y graph.Node) graph.Edge { - return g.WeightedEdgeBetween(x, y) +func (g undirectedLayerHandle) EdgeBetween(xid, yid int64) graph.Edge { + return g.WeightedEdgeBetween(xid, yid) } // WeightedEdgeBetween returns the weighted edge between nodes x and y. -func (g undirectedLayerHandle) WeightedEdgeBetween(x, y graph.Node) graph.WeightedEdge { - xid := x.ID() - yid := y.ID() +func (g undirectedLayerHandle) WeightedEdgeBetween(xid, yid int64) graph.WeightedEdge { if xid == yid || !isValidID(xid) || !isValidID(yid) { return nil } @@ -514,16 +517,14 @@ func (g undirectedLayerHandle) WeightedEdgeBetween(x, y graph.Node) graph.Weight if !ok { return nil } - return multiplexEdge{from: g.multiplex.nodes[x.ID()], to: g.multiplex.nodes[y.ID()], weight: w} + return multiplexEdge{from: g.multiplex.nodes[xid], to: g.multiplex.nodes[yid], weight: w} } // Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge. // If x and y are the same node the internal node weight is returned. If there is no joining // edge between the two nodes the weight value returned is zero. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g undirectedLayerHandle) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g undirectedLayerHandle) Weight(xid, yid int64) (w float64, ok bool) { if !isValidID(xid) || !isValidID(yid) { return 0, false } @@ -556,7 +557,7 @@ type undirectedMultiplexLocalMover struct { // that returns the Weight value // of the non-nil edge between x // and y. - weight []func(x, y graph.Node) float64 + weight []func(xid, yid int64) float64 // communities is the current // division of g. @@ -606,7 +607,7 @@ func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities memberships: make([]int, len(nodes)), resolutions: resolutions, weights: weights, - weight: make([]func(x, y graph.Node) float64, g.Depth()), + weight: make([]func(xid, yid int64) float64, g.Depth()), } // Calculate the total edge weight of the graph @@ -614,7 +615,7 @@ func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities var zero int for i := 0; i < g.Depth(); i++ { l.edgeWeightOf[i] = make([]float64, len(nodes)) - var weight func(x, y graph.Node) float64 + var weight func(xid, yid int64) float64 if weights != nil { if weights[i] == 0 { @@ -634,11 +635,12 @@ func newUndirectedMultiplexLocalMover(g *ReducedUndirectedMultiplex, communities l.weight[i] = weight layer := g.Layer(i) for _, u := range l.nodes { - w := weight(u, u) - for _, v := range layer.From(u) { - w += weight(u, v) + uid := u.ID() + w := weight(uid, uid) + for _, v := range layer.From(uid) { + w += weight(uid, v.ID()) } - l.edgeWeightOf[i][u.ID()] = w + l.edgeWeightOf[i][uid] = w l.m2[i] += w } if l.m2[i] == 0 { @@ -779,7 +781,7 @@ func (l *undirectedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, ds removal = true } - k_aC += l.weight[layer](n, u) + k_aC += l.weight[layer](id, uid) // sigma_totC could be kept for each community // and updated for moves, changing the calculation // of sigma_totC here from O(n_c) to O(1), but @@ -789,7 +791,7 @@ func (l *undirectedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, ds sigma_totC += l.edgeWeightOf[layer][uid] } - a_aa := l.weight[layer](n, n) + a_aa := l.weight[layer](id, id) k_a := l.edgeWeightOf[layer][id] gamma := 1.0 if l.resolutions != nil { diff --git a/graph/community/louvain_undirected_multiplex_test.go b/graph/community/louvain_undirected_multiplex_test.go index 48e3a355..f4b460eb 100644 --- a/graph/community/louvain_undirected_multiplex_test.go +++ b/graph/community/louvain_undirected_multiplex_test.go @@ -359,7 +359,7 @@ tests: } layer := g.Layer(l) for n := range c { - if layer.HasEdgeBetween(simple.Node(n), target) { + if layer.HasEdgeBetween(int64(n), target.ID()) { connected = true break search } @@ -670,7 +670,7 @@ func undirectedMultiplexFrom(raw []layer) (UndirectedLayers, []float64, error) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range l.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/community/louvain_undirected_test.go b/graph/community/louvain_undirected_test.go index bb21aa34..2d95b277 100644 --- a/graph/community/louvain_undirected_test.go +++ b/graph/community/louvain_undirected_test.go @@ -267,7 +267,7 @@ func TestCommunityQUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -284,7 +284,7 @@ func TestCommunityQWeightedUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -320,7 +320,7 @@ func TestCommunityDeltaQUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -337,7 +337,7 @@ func TestCommunityDeltaQWeightedUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -401,7 +401,7 @@ func testCommunityDeltaQUndirected(t *testing.T, test communityUndirectedQTest, } connected := false for n := range c { - if g.HasEdgeBetween(simple.Node(n), target) { + if g.HasEdgeBetween(int64(n), target.ID()) { connected = true break } @@ -445,7 +445,7 @@ func TestReduceQConsistencyUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -462,7 +462,7 @@ func TestReduceQConsistencyWeightedUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -563,7 +563,7 @@ func TestMoveLocalUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -580,7 +580,7 @@ func TestMoveLocalWeightedUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -625,7 +625,7 @@ func TestModularizeUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -642,7 +642,7 @@ func TestModularizeWeightedUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, 0) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/encoding/dot/encode.go b/graph/encoding/dot/encode.go index 04918402..55b96e31 100644 --- a/graph/encoding/dot/encode.go +++ b/graph/encoding/dot/encode.go @@ -151,7 +151,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool if s, ok := n.(Subgrapher); ok { // If the node is not linked to any other node // the graph needs to be written now. - if len(g.From(n)) == 0 { + if len(g.From(n.ID())) == 0 { g := s.Subgraph() _, subIsDirected := g.(graph.Directed) if subIsDirected != isDirected { @@ -182,20 +182,22 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool havePrintedEdgeHeader := false for _, n := range nodes { - to := g.From(n) + nid := n.ID() + to := g.From(nid) sort.Sort(ordered.ByID(to)) for _, t := range to { + tid := t.ID() if isDirected { - if p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] { + if p.visited[edge{inGraph: name, from: nid, to: tid}] { continue } - p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] = true + p.visited[edge{inGraph: name, from: nid, to: tid}] = true } else { - if p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] { + if p.visited[edge{inGraph: name, from: nid, to: tid}] { continue } - p.visited[edge{inGraph: name, from: n.ID(), to: t.ID()}] = true - p.visited[edge{inGraph: name, from: t.ID(), to: n.ID()}] = true + p.visited[edge{inGraph: name, from: nid, to: tid}] = true + p.visited[edge{inGraph: name, from: tid, to: n.ID()}] = true } if !havePrintedEdgeHeader { @@ -217,7 +219,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool } else { p.writeNode(n) } - e, edgeIsPorter := g.Edge(n, t).(Porter) + e, edgeIsPorter := g.Edge(nid, tid).(Porter) if edgeIsPorter { p.writePorts(e.FromPort()) } @@ -242,7 +244,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool p.writePorts(e.ToPort()) } - if a, ok := g.Edge(n, t).(encoding.Attributer); ok { + if a, ok := g.Edge(nid, tid).(encoding.Attributer); ok { p.writeAttributeList(a) } diff --git a/graph/ex/fdpclust/gn.go b/graph/ex/fdpclust/gn.go index bcb1aad1..b44457e8 100644 --- a/graph/ex/fdpclust/gn.go +++ b/graph/ex/fdpclust/gn.go @@ -11,29 +11,29 @@ type GraphNode struct { roots []*GraphNode } -func (g *GraphNode) Has(n graph.Node) bool { - if n.ID() == g.id { +func (g *GraphNode) Has(id int64) bool { + if id == g.id { return true } visited := map[int64]struct{}{g.id: {}} for _, root := range g.roots { - if root.ID() == n.ID() { + if root.ID() == id { return true } - if root.has(n, visited) { + if root.has(id, visited) { return true } } for _, neigh := range g.neighbors { - if neigh.ID() == n.ID() { + if neigh.ID() == id { return true } if gn, ok := neigh.(*GraphNode); ok { - if gn.has(n, visited) { + if gn.has(id, visited) { return true } } @@ -42,18 +42,18 @@ func (g *GraphNode) Has(n graph.Node) bool { return false } -func (g *GraphNode) has(n graph.Node, visited map[int64]struct{}) bool { +func (g *GraphNode) has(id int64, visited map[int64]struct{}) bool { for _, root := range g.roots { if _, ok := visited[root.ID()]; ok { continue } visited[root.ID()] = struct{}{} - if root.ID() == n.ID() { + if root.ID() == id { return true } - if root.has(n, visited) { + if root.has(id, visited) { return true } @@ -65,16 +65,15 @@ func (g *GraphNode) has(n graph.Node, visited map[int64]struct{}) bool { } visited[neigh.ID()] = struct{}{} - if neigh.ID() == n.ID() { + if neigh.ID() == id { return true } if gn, ok := neigh.(*GraphNode); ok { - if gn.has(n, visited) { + if gn.has(id, visited) { return true } } - } return false @@ -128,8 +127,8 @@ func (g *GraphNode) nodes(list []graph.Node, visited map[int64]struct{}) []graph return list } -func (g *GraphNode) From(n graph.Node) []graph.Node { - if n.ID() == g.ID() { +func (g *GraphNode) From(id int64) []graph.Node { + if id == g.ID() { return g.neighbors } @@ -137,7 +136,7 @@ func (g *GraphNode) From(n graph.Node) []graph.Node { for _, root := range g.roots { visited[root.ID()] = struct{}{} - if result := root.findNeighbors(n, visited); result != nil { + if result := root.findNeighbors(id, visited); result != nil { return result } } @@ -146,7 +145,7 @@ func (g *GraphNode) From(n graph.Node) []graph.Node { visited[neigh.ID()] = struct{}{} if gn, ok := neigh.(*GraphNode); ok { - if result := gn.findNeighbors(n, visited); result != nil { + if result := gn.findNeighbors(id, visited); result != nil { return result } } @@ -155,8 +154,8 @@ func (g *GraphNode) From(n graph.Node) []graph.Node { return nil } -func (g *GraphNode) findNeighbors(n graph.Node, visited map[int64]struct{}) []graph.Node { - if n.ID() == g.ID() { +func (g *GraphNode) findNeighbors(id int64, visited map[int64]struct{}) []graph.Node { + if id == g.ID() { return g.neighbors } @@ -166,7 +165,7 @@ func (g *GraphNode) findNeighbors(n graph.Node, visited map[int64]struct{}) []gr } visited[root.ID()] = struct{}{} - if result := root.findNeighbors(n, visited); result != nil { + if result := root.findNeighbors(id, visited); result != nil { return result } } @@ -178,7 +177,7 @@ func (g *GraphNode) findNeighbors(n graph.Node, visited map[int64]struct{}) []gr visited[neigh.ID()] = struct{}{} if gn, ok := neigh.(*GraphNode); ok { - if result := gn.findNeighbors(n, visited); result != nil { + if result := gn.findNeighbors(id, visited); result != nil { return result } } @@ -187,18 +186,18 @@ func (g *GraphNode) findNeighbors(n graph.Node, visited map[int64]struct{}) []gr return nil } -func (g *GraphNode) HasEdgeBetween(u, v graph.Node) bool { - return g.EdgeBetween(u, v) != nil +func (g *GraphNode) HasEdgeBetween(uid, vid int64) bool { + return g.EdgeBetween(uid, vid) != nil } -func (g *GraphNode) Edge(u, v graph.Node) graph.Edge { - return g.EdgeBetween(u, v) +func (g *GraphNode) Edge(uid, vid int64) graph.Edge { + return g.EdgeBetween(uid, vid) } -func (g *GraphNode) EdgeBetween(u, v graph.Node) graph.Edge { - if u.ID() == g.id || v.ID() == g.id { +func (g *GraphNode) EdgeBetween(uid, vid int64) graph.Edge { + if uid == g.id || vid == g.id { for _, neigh := range g.neighbors { - if neigh.ID() == u.ID() || neigh.ID() == v.ID() { + if neigh.ID() == uid || neigh.ID() == vid { return simple.Edge{F: g, T: neigh} } } @@ -208,7 +207,7 @@ func (g *GraphNode) EdgeBetween(u, v graph.Node) graph.Edge { visited := map[int64]struct{}{g.id: {}} for _, root := range g.roots { visited[root.ID()] = struct{}{} - if result := root.edgeBetween(u, v, visited); result != nil { + if result := root.edgeBetween(uid, vid, visited); result != nil { return result } } @@ -216,7 +215,7 @@ func (g *GraphNode) EdgeBetween(u, v graph.Node) graph.Edge { for _, neigh := range g.neighbors { visited[neigh.ID()] = struct{}{} if gn, ok := neigh.(*GraphNode); ok { - if result := gn.edgeBetween(u, v, visited); result != nil { + if result := gn.edgeBetween(uid, vid, visited); result != nil { return result } } @@ -225,10 +224,10 @@ func (g *GraphNode) EdgeBetween(u, v graph.Node) graph.Edge { return nil } -func (g *GraphNode) edgeBetween(u, v graph.Node, visited map[int64]struct{}) graph.Edge { - if u.ID() == g.id || v.ID() == g.id { +func (g *GraphNode) edgeBetween(uid, vid int64, visited map[int64]struct{}) graph.Edge { + if uid == g.id || vid == g.id { for _, neigh := range g.neighbors { - if neigh.ID() == u.ID() || neigh.ID() == v.ID() { + if neigh.ID() == uid || neigh.ID() == vid { return simple.Edge{F: g, T: neigh} } } @@ -240,7 +239,7 @@ func (g *GraphNode) edgeBetween(u, v graph.Node, visited map[int64]struct{}) gra continue } visited[root.ID()] = struct{}{} - if result := root.edgeBetween(u, v, visited); result != nil { + if result := root.edgeBetween(uid, vid, visited); result != nil { return result } } @@ -252,7 +251,7 @@ func (g *GraphNode) edgeBetween(u, v graph.Node, visited map[int64]struct{}) gra visited[neigh.ID()] = struct{}{} if gn, ok := neigh.(*GraphNode); ok { - if result := gn.edgeBetween(u, v, visited); result != nil { + if result := gn.edgeBetween(uid, vid, visited); result != nil { return result } } diff --git a/graph/graph.go b/graph/graph.go index 4a3a6379..7c6de2e0 100644 --- a/graph/graph.go +++ b/graph/graph.go @@ -27,53 +27,57 @@ type WeightedEdge interface { // Graph is a generalized graph. type Graph interface { - // Has returns whether the node exists within the graph. - Has(Node) bool + // Has returns whether a node with the given ID exists + // within the graph. + Has(id int64) bool // Nodes returns all the nodes in the graph. Nodes() []Node // From returns all nodes that can be reached directly - // from the given node. - From(Node) []Node + // from the node with the given ID. + From(id int64) []Node // HasEdgeBetween returns whether an edge exists between - // nodes x and y without considering direction. - HasEdgeBetween(x, y Node) bool + // nodes with IDs xid and yid without considering direction. + HasEdgeBetween(xid, yid int64) bool - // Edge returns the edge 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. - Edge(u, v Node) Edge + // Edge returns the edge from u to v, with IDs uid and vid, + // if such an edge exists and nil otherwise. The node v + // must be directly reachable from u as defined by the + // From method. + Edge(uid, vid int64) Edge } // Weighted is a weighted graph. type Weighted interface { Graph - // WeightedEdge returns the weighted edge 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. - WeightedEdge(u, v Node) WeightedEdge + // WeightedEdge returns the weighted edge from u to v + // with IDs uid and vid if such an edge exists and + // nil otherwise. The node v must be directly + // reachable from u as defined by the From method. + WeightedEdge(uid, vid int64) WeightedEdge // Weight returns the weight for the edge between - // x and y if Edge(x, y) returns a non-nil Edge. + // x and y with IDs xid and yid if Edge(xid, yid) + // returns a non-nil Edge. // If x and y are the same node or there is no // joining edge between the two nodes the weight // value returned is implementation dependent. // Weight returns true if an edge exists between // x and y or if x and y have the same ID, false // otherwise. - Weight(x, y Node) (w float64, ok bool) + Weight(xid, yid int64) (w float64, ok bool) } // Undirected is an undirected graph. type Undirected interface { Graph - // EdgeBetween returns the edge between nodes x and y. - EdgeBetween(x, y Node) Edge + // EdgeBetween returns the edge between nodes x and y + // with IDs xid and yid. + EdgeBetween(xid, yid int64) Edge } // WeightedUndirected is a weighted undirected graph. @@ -81,8 +85,8 @@ type WeightedUndirected interface { Weighted // WeightedEdgeBetween returns the edge between nodes - // x and y. - WeightedEdgeBetween(x, y Node) WeightedEdge + // x and y with IDs xid and yid. + WeightedEdgeBetween(xid, yid int64) WeightedEdge } // Directed is a directed graph. @@ -90,12 +94,12 @@ type Directed interface { Graph // HasEdgeFromTo returns whether an edge exists - // in the graph from u to v. - HasEdgeFromTo(u, v Node) bool + // in the graph from u to v with IDs uid and vid. + HasEdgeFromTo(uid, vid int64) bool // To returns all nodes that can reach directly - // to the given node. - To(Node) []Node + // to the node with the given ID. + To(id int64) []Node } // WeightedDirected is a weighted directed graph. @@ -103,12 +107,13 @@ type WeightedDirected interface { Weighted // HasEdgeFromTo returns whether an edge exists - // in the graph from u to v. - HasEdgeFromTo(u, v Node) bool + // in the graph from u to v with the IDs uid and + // vid. + HasEdgeFromTo(uid, vid int64) bool // To returns all nodes that can reach directly - // to the given node. - To(Node) []Node + // to the node with the given ID. + To(id int64) []Node } // NodeAdder is an interface for adding arbitrary nodes to a graph. @@ -217,7 +222,7 @@ func Copy(dst Builder, src Graph) { dst.AddNode(n) } for _, u := range nodes { - for _, v := range src.From(u) { + for _, v := range src.From(u.ID()) { dst.SetEdge(dst.NewEdge(u, v)) } } @@ -240,8 +245,8 @@ func CopyWeighted(dst WeightedBuilder, src Weighted) { dst.AddNode(n) } for _, u := range nodes { - for _, v := range src.From(u) { - dst.SetWeightedEdge(dst.NewWeightedEdge(u, v, src.WeightedEdge(u, v).Weight())) + for _, v := range src.From(u.ID()) { + dst.SetWeightedEdge(dst.NewWeightedEdge(u, v, src.WeightedEdge(u.ID(), v.ID()).Weight())) } } } diff --git a/graph/graph_test.go b/graph/graph_test.go index 5a7ce337..47b671f8 100644 --- a/graph/graph_test.go +++ b/graph/graph_test.go @@ -273,8 +273,8 @@ func same(a, b graph.Graph) bool { } } for _, u := range a.Nodes() { - aFromU := a.From(u) - bFromU := b.From(u) + aFromU := a.From(u.ID()) + bFromU := b.From(u.ID()) if len(aFromU) != len(bFromU) { return false } @@ -288,7 +288,7 @@ func same(a, b graph.Graph) bool { aW, aWok := a.(graph.Weighted) bW, bWok := b.(graph.Weighted) if aWok && bWok { - if aW.WeightedEdge(u, va).Weight() != bW.WeightedEdge(u, vb).Weight() { + if aW.WeightedEdge(u.ID(), va.ID()).Weight() != bW.WeightedEdge(u.ID(), vb.ID()).Weight() { return false } } diff --git a/graph/graphs/gen/batagelj_brandes.go b/graph/graphs/gen/batagelj_brandes.go index ce3ec5f9..8238233a 100644 --- a/graph/graphs/gen/batagelj_brandes.go +++ b/graph/graphs/gen/batagelj_brandes.go @@ -36,7 +36,7 @@ func Gnp(dst GraphBuilder, n int, p float64, src *rand.Rand) error { } for i := 0; i < n; i++ { - if !dst.Has(simple.Node(i)) { + if !dst.Has(int64(i)) { dst.AddNode(simple.Node(i)) } } @@ -112,7 +112,7 @@ func Gnm(dst GraphBuilder, n, m int, src *rand.Rand) error { } for i := 0; i < n; i++ { - if !dst.Has(simple.Node(i)) { + if !dst.Has(int64(i)) { dst.AddNode(simple.Node(i)) } } @@ -122,7 +122,7 @@ func Gnm(dst GraphBuilder, n, m int, src *rand.Rand) error { for { v, w := edgeNodesFor(rnd(nChoose2)) e := simple.Edge{F: w, T: v} - if !hasEdge(e.F, e.T) { + if !hasEdge(e.F.ID(), e.T.ID()) { dst.SetEdge(e) break } @@ -137,7 +137,7 @@ func Gnm(dst GraphBuilder, n, m int, src *rand.Rand) error { for { v, w := edgeNodesFor(rnd(nChoose2)) e := simple.Edge{F: v, T: w} - if !hasEdge(e.F, e.T) { + if !hasEdge(e.F.ID(), e.T.ID()) { dst.SetEdge(e) break } @@ -182,7 +182,7 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src *rand.Rand) error } for i := 0; i < n; i++ { - if !dst.Has(simple.Node(i)) { + if !dst.Has(int64(i)) { dst.AddNode(simple.Node(i)) } } @@ -201,14 +201,14 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src *rand.Rand) error j := v*(v-1)/2 + (v+i)%n var ej simple.Edge ej.T, ej.F = edgeNodesFor(j) - if !hasEdge(ej.From(), ej.To()) { + if !hasEdge(ej.From().ID(), ej.To().ID()) { dst.SetEdge(ej) } k-- m++ var em simple.Edge em.T, em.F = edgeNodesFor(m) - if !hasEdge(em.From(), em.To()) { + if !hasEdge(em.From().ID(), em.To().ID()) { replace[j] = m } else { replace[j] = replace[m] @@ -222,17 +222,17 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src *rand.Rand) error r := rndN(nChoose2-i) + i var er simple.Edge er.T, er.F = edgeNodesFor(r) - if !hasEdge(er.From(), er.To()) { + if !hasEdge(er.From().ID(), er.To().ID()) { dst.SetEdge(er) } else { er.T, er.F = edgeNodesFor(replace[r]) - if !hasEdge(er.From(), er.To()) { + if !hasEdge(er.From().ID(), er.To().ID()) { dst.SetEdge(er) } } var ei simple.Edge ei.T, ei.F = edgeNodesFor(i) - if !hasEdge(ei.From(), ei.To()) { + if !hasEdge(ei.From().ID(), ei.To().ID()) { replace[r] = i } else { replace[r] = replace[i] @@ -252,12 +252,12 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src *rand.Rand) error j := v*(v-1)/2 + (v+i)%n var ej simple.Edge ej.F, ej.T = edgeNodesFor(j) - if !hasEdge(ej.From(), ej.To()) { + if !hasEdge(ej.From().ID(), ej.To().ID()) { dst.SetEdge(ej) } k-- m++ - if !hasEdge(edgeNodesFor(m)) { + if u, v := edgeNodesFor(m); !hasEdge(u.ID(), v.ID()) { replace[j] = m } else { replace[j] = replace[m] @@ -271,15 +271,15 @@ func SmallWorldsBB(dst GraphBuilder, n, d int, p float64, src *rand.Rand) error r := rndN(nChoose2-i) + i var er simple.Edge er.F, er.T = edgeNodesFor(r) - if !hasEdge(er.From(), er.To()) { + if !hasEdge(er.From().ID(), er.To().ID()) { dst.SetEdge(er) } else { er.F, er.T = edgeNodesFor(replace[r]) - if !hasEdge(er.From(), er.To()) { + if !hasEdge(er.From().ID(), er.To().ID()) { dst.SetEdge(er) } } - if !hasEdge(edgeNodesFor(i)) { + if u, v := edgeNodesFor(i); !hasEdge(u.ID(), v.ID()) { replace[r] = i } else { replace[r] = replace[i] diff --git a/graph/graphs/gen/batagelj_brandes_test.go b/graph/graphs/gen/batagelj_brandes_test.go index 393f877c..390ab1dd 100644 --- a/graph/graphs/gen/batagelj_brandes_test.go +++ b/graph/graphs/gen/batagelj_brandes_test.go @@ -27,7 +27,7 @@ func (g *gnUndirected) SetEdge(e graph.Edge) { return case e.From().ID() > e.To().ID(): g.addBackwards = true - case g.UndirectedBuilder.HasEdgeBetween(e.From(), e.To()): + case g.UndirectedBuilder.HasEdgeBetween(e.From().ID(), e.To().ID()): g.addMultipleEdge = true } @@ -45,7 +45,7 @@ func (g *gnDirected) SetEdge(e graph.Edge) { case e.From().ID() == e.To().ID(): g.addSelfLoop = true return - case g.DirectedBuilder.HasEdgeFromTo(e.From(), e.To()): + case g.DirectedBuilder.HasEdgeFromTo(e.From().ID(), e.To().ID()): g.addMultipleEdge = true } @@ -190,9 +190,10 @@ func TestPowerLawUndirected(t *testing.T) { } for _, u := range nodes { + uid := u.ID() var lines int - for _, v := range g.From(u) { - lines += len(g.Lines(u, v)) + for _, v := range g.From(uid) { + lines += len(g.Lines(uid, v.ID())) } if lines < d { t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) @@ -218,9 +219,10 @@ func TestPowerLawDirected(t *testing.T) { } for _, u := range nodes { + uid := u.ID() var lines int - for _, v := range g.From(u) { - lines += len(g.Lines(u, v)) + for _, v := range g.From(uid) { + lines += len(g.Lines(uid, v.ID())) } if lines < d { t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) @@ -265,9 +267,10 @@ func TestBipartitePowerLawUndirected(t *testing.T) { } for _, u := range nodes { + uid := u.ID() var lines int - for _, v := range g.From(u) { - lines += len(g.Lines(u, v)) + for _, v := range g.From(uid) { + lines += len(g.Lines(uid, v.ID())) } if lines < d { t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) @@ -312,9 +315,10 @@ func TestBipartitePowerLawDirected(t *testing.T) { } for _, u := range nodes { + uid := u.ID() var lines int - for _, v := range g.From(u) { - lines += len(g.Lines(u, v)) + for _, v := range g.From(uid) { + lines += len(g.Lines(uid, v.ID())) } if lines < d { t.Errorf("unexpected degree below d: n=%d, d=%d: got:%d", n, d, lines) diff --git a/graph/graphs/gen/duplication.go b/graph/graphs/gen/duplication.go index a59889a0..86558cf3 100644 --- a/graph/graphs/gen/duplication.go +++ b/graph/graphs/gen/duplication.go @@ -66,6 +66,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src for i := 0; i < n; i++ { u := nodes[rndN(len(nodes))] d := dst.NewNode() + did := d.ID() // Add the duplicate node. dst.AddNode(d) @@ -74,13 +75,14 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src // into the rest of the graph. for { // Add edges to parent's neighbours. - to := dst.From(u) + to := dst.From(u.ID()) sort.Sort(ordered.ByID(to)) for _, v := range to { - if rnd() < delta || dst.HasEdgeBetween(v, d) { + vid := v.ID() + if rnd() < delta || dst.HasEdgeBetween(vid, did) { continue } - if v.ID() < d.ID() { + if vid < did { dst.SetEdge(dst.NewEdge(v, d)) } else { dst.SetEdge(dst.NewEdge(d, v)) @@ -90,11 +92,13 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src // Add edges to old nodes. scaledAlpha := alpha / float64(len(nodes)) for _, v := range nodes { - switch v.ID() { - case u.ID(): + uid := u.ID() + vid := v.ID() + switch vid { + case uid: if !math.IsNaN(sigma) { if i == 0 || rnd() < sigma { - if v.ID() < d.ID() { + if vid < did { dst.SetEdge(dst.NewEdge(v, d)) } else { dst.SetEdge(dst.NewEdge(d, v)) @@ -104,8 +108,8 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src } fallthrough default: - if rnd() < scaledAlpha && !dst.HasEdgeBetween(v, d) { - if v.ID() < d.ID() { + if rnd() < scaledAlpha && !dst.HasEdgeBetween(vid, did) { + if vid < did { dst.SetEdge(dst.NewEdge(v, d)) } else { dst.SetEdge(dst.NewEdge(d, v)) @@ -114,7 +118,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src } } - if len(dst.From(d)) != 0 { + if len(dst.From(did)) != 0 { break } } diff --git a/graph/graphs/gen/duplication_test.go b/graph/graphs/gen/duplication_test.go index cebf36a5..ae28b8d2 100644 --- a/graph/graphs/gen/duplication_test.go +++ b/graph/graphs/gen/duplication_test.go @@ -25,7 +25,7 @@ func (g *duplication) SetEdge(e graph.Edge) { return case e.From().ID() > e.To().ID(): g.addBackwards = true - case g.UndirectedMutator.HasEdgeBetween(e.From(), e.To()): + case g.UndirectedMutator.HasEdgeBetween(e.From().ID(), e.To().ID()): g.addMultipleEdge = true } diff --git a/graph/graphs/gen/gen.go b/graph/graphs/gen/gen.go index ec724bae..61c3c793 100644 --- a/graph/graphs/gen/gen.go +++ b/graph/graphs/gen/gen.go @@ -8,8 +8,8 @@ import "gonum.org/v1/gonum/graph" // GraphBuilder is a graph that can have nodes and edges added. type GraphBuilder interface { - Has(graph.Node) bool - HasEdgeBetween(x, y graph.Node) bool + Has(id int64) bool + HasEdgeBetween(xid, yid int64) bool graph.Builder } diff --git a/graph/graphs/gen/holme_kim.go b/graph/graphs/gen/holme_kim.go index 3c929970..351aa85a 100644 --- a/graph/graphs/gen/holme_kim.go +++ b/graph/graphs/gen/holme_kim.go @@ -47,7 +47,7 @@ func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64 // Initial condition. wt := make([]float64, n) for u := 0; u < m; u++ { - if !dst.Has(simple.Node(u)) { + if !dst.Has(int64(0)) { dst.AddNode(simple.Node(u)) } // We need to give equal probability for @@ -68,9 +68,9 @@ func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64 for i := 0; i < m; i++ { // Triad formation. if i != 0 && rnd() < p { - for _, w := range permute(dst.From(simple.Node(u)), rndN) { + for _, w := range permute(dst.From(int64(u)), rndN) { wid := w.ID() - if wid == int64(v) || dst.HasEdgeBetween(w, simple.Node(v)) { + if wid == int64(v) || dst.HasEdgeBetween(wid, int64(v)) { continue } dst.SetEdge(simple.Edge{F: w, T: simple.Node(v)}) @@ -87,7 +87,7 @@ func TunableClusteringScaleFree(dst graph.UndirectedBuilder, n, m int, p float64 if !ok { return errors.New("gen: depleted distribution") } - if u == v || dst.HasEdgeBetween(simple.Node(u), simple.Node(v)) { + if u == v || dst.HasEdgeBetween(int64(u), int64(v)) { continue } dst.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) @@ -128,7 +128,7 @@ func PreferentialAttachment(dst graph.UndirectedBuilder, n, m int, src *rand.Ran // Initial condition. wt := make([]float64, n) for u := 0; u < m; u++ { - if !dst.Has(simple.Node(u)) { + if !dst.Has(int64(u)) { dst.AddNode(simple.Node(u)) } // We need to give equal probability for diff --git a/graph/graphs/gen/small_world.go b/graph/graphs/gen/small_world.go index b19faf3b..1c6565da 100644 --- a/graph/graphs/gen/small_world.go +++ b/graph/graphs/gen/small_world.go @@ -40,7 +40,7 @@ func NavigableSmallWorld(dst GraphBuilder, dims []int, p, q int, r float64, src n *= d } for i := 0; i < n; i++ { - if !dst.Has(simple.Node(i)) { + if !dst.Has(int64(i)) { dst.AddNode(simple.Node(i)) } } @@ -67,14 +67,14 @@ func NavigableSmallWorld(dst GraphBuilder, dims []int, p, q int, r float64, src if uid > vid { e.F, e.T = e.T, e.F } - if !hasEdge(e.From(), e.To()) { + if !hasEdge(e.From().ID(), e.To().ID()) { dst.SetEdge(e) } if !isDirected { return } e.F, e.T = e.T, e.F - if !hasEdge(e.From(), e.To()) { + if !hasEdge(e.From().ID(), e.To().ID()) { dst.SetEdge(e) } }) @@ -110,7 +110,7 @@ func NavigableSmallWorld(dst GraphBuilder, dims []int, p, q int, r float64, src if !isDirected && uid > vid { e.F, e.T = e.T, e.F } - if !hasEdge(e.From(), e.To()) { + if !hasEdge(e.From().ID(), e.To().ID()) { dst.SetEdge(e) } } diff --git a/graph/multi/directed.go b/graph/multi/directed.go index 92143eb4..8da3cfc5 100644 --- a/graph/multi/directed.go +++ b/graph/multi/directed.go @@ -103,13 +103,13 @@ func (g *DirectedGraph) SetLine(l graph.Line) { lid = l.ID() ) - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } if g.from[fid][tid] == nil { g.from[fid][tid] = make(map[int64]graph.Line) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } if g.to[tid][fid] == nil { @@ -149,8 +149,8 @@ func (g *DirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *DirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *DirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -187,30 +187,30 @@ func (g *DirectedGraph) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *DirectedGraph) From(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *DirectedGraph) From(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - from := make([]graph.Node, len(g.from[n.ID()])) + from := make([]graph.Node, len(g.from[id])) i := 0 - for id := range g.from[n.ID()] { - from[i] = g.nodes[id] + for vid := range g.from[id] { + from[i] = g.nodes[vid] i++ } return from } // To returns all nodes in g that can reach directly to n. -func (g *DirectedGraph) To(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *DirectedGraph) To(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - to := make([]graph.Node, len(g.to[n.ID()])) + to := make([]graph.Node, len(g.to[id])) i := 0 - for id := range g.to[n.ID()] { - to[i] = g.nodes[id] + for uid := range g.to[id] { + to[i] = g.nodes[uid] i++ } return to @@ -218,9 +218,7 @@ func (g *DirectedGraph) To(n graph.Node) []graph.Node { // HasEdgeBetween returns whether an edge exists between nodes x and y without // considering direction. -func (g *DirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g *DirectedGraph) HasEdgeBetween(xid, yid int64) bool { if _, ok := g.from[xid][yid]; ok { return true } @@ -231,8 +229,8 @@ func (g *DirectedGraph) HasEdgeBetween(x, y graph.Node) bool { // Edge returns the edge 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 returned graph.Edge is a multi.Edge if an edge exists. -func (g *DirectedGraph) Edge(u, v graph.Node) graph.Edge { - lines := g.Lines(u, v) +func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge { + lines := g.Lines(uid, vid) if len(lines) == 0 { return nil } @@ -241,8 +239,8 @@ func (g *DirectedGraph) Edge(u, v graph.Node) graph.Edge { // Lines returns the lines from u to v if such any such lines exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *DirectedGraph) Lines(u, v graph.Node) []graph.Line { - edge := g.from[u.ID()][v.ID()] +func (g *DirectedGraph) Lines(uid, vid int64) []graph.Line { + edge := g.from[uid][vid] if len(edge) == 0 { return nil } @@ -254,21 +252,21 @@ func (g *DirectedGraph) Lines(u, v graph.Node) []graph.Line { } // HasEdgeFromTo returns whether an edge exists in the graph from u to v. -func (g *DirectedGraph) HasEdgeFromTo(u, v graph.Node) bool { - _, ok := g.from[u.ID()][v.ID()] +func (g *DirectedGraph) HasEdgeFromTo(uid, vid int64) bool { + _, ok := g.from[uid][vid] return ok } // Degree returns the in+out degree of n in g. -func (g *DirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *DirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } var deg int - for _, e := range g.from[n.ID()] { + for _, e := range g.from[id] { deg += len(e) } - for _, e := range g.to[n.ID()] { + for _, e := range g.to[id] { deg += len(e) } return deg diff --git a/graph/multi/directed_test.go b/graph/multi/directed_test.go index c25a6c7f..487ec9a8 100644 --- a/graph/multi/directed_test.go +++ b/graph/multi/directed_test.go @@ -21,7 +21,7 @@ var ( func TestEdgeOvercounting(t *testing.T) { g := generateDummyGraph() - if neigh := g.From(Node(Node(2))); len(neigh) != 2 { + if neigh := 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)) } } diff --git a/graph/multi/undirected.go b/graph/multi/undirected.go index fedd7162..fb2343a1 100644 --- a/graph/multi/undirected.go +++ b/graph/multi/undirected.go @@ -95,13 +95,13 @@ func (g *UndirectedGraph) SetLine(l graph.Line) { lid = l.ID() ) - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } if g.lines[fid][tid] == nil { g.lines[fid][tid] = make(map[int64]graph.Line) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } if g.lines[tid][fid] == nil { @@ -138,8 +138,8 @@ func (g *UndirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *UndirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *UndirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -185,14 +185,14 @@ func (g *UndirectedGraph) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *UndirectedGraph) From(n graph.Node) []graph.Node { - if !g.Has(n) { +func (g *UndirectedGraph) From(id int64) []graph.Node { + if !g.Has(id) { return nil } - nodes := make([]graph.Node, len(g.lines[n.ID()])) + nodes := make([]graph.Node, len(g.lines[id])) i := 0 - for from := range g.lines[n.ID()] { + for from := range g.lines[id] { nodes[i] = g.nodes[from] i++ } @@ -200,21 +200,21 @@ func (g *UndirectedGraph) From(n graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *UndirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - _, ok := g.lines[x.ID()][y.ID()] +func (g *UndirectedGraph) HasEdgeBetween(xid, yid int64) bool { + _, ok := g.lines[xid][yid] return ok } // EdgeBetween returns the edge between nodes x and y. -func (g *UndirectedGraph) EdgeBetween(x, y graph.Node) graph.Edge { - return g.Edge(x, y) +func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge { + return g.Edge(xid, yid) } // Edge returns the edge 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 returned graph.Edge is a multi.Edge if an edge exists. -func (g *UndirectedGraph) Edge(u, v graph.Node) graph.Edge { - lines := g.LinesBetween(u, v) +func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge { + lines := g.LinesBetween(uid, vid) if len(lines) == 0 { return nil } @@ -223,26 +223,26 @@ func (g *UndirectedGraph) Edge(u, v graph.Node) graph.Edge { // Lines returns the lines from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *UndirectedGraph) Lines(u, v graph.Node) []graph.Line { - return g.LinesBetween(u, v) +func (g *UndirectedGraph) Lines(uid, vid int64) []graph.Line { + return g.LinesBetween(uid, vid) } // LinesBetween returns the lines between nodes x and y. -func (g *UndirectedGraph) LinesBetween(x, y graph.Node) []graph.Line { +func (g *UndirectedGraph) LinesBetween(xid, yid int64) []graph.Line { var lines []graph.Line - for _, l := range g.lines[x.ID()][y.ID()] { + for _, l := range g.lines[xid][yid] { lines = append(lines, l) } return lines } // Degree returns the degree of n in g. -func (g *UndirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *UndirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } var deg int - for _, e := range g.lines[n.ID()] { + for _, e := range g.lines[id] { deg += len(e) } return deg diff --git a/graph/multi/undirected_test.go b/graph/multi/undirected_test.go index eba32f8b..a3a1bfc0 100644 --- a/graph/multi/undirected_test.go +++ b/graph/multi/undirected_test.go @@ -30,7 +30,7 @@ func TestMaxID(t *testing.T) { delete(nodes, Node(2)) n := g.NewNode() g.AddNode(n) - if !g.Has(n) { + if !g.Has(n.ID()) { t.Error("added node does not exist in graph") } if _, exists := nodes[n]; exists { diff --git a/graph/multi/weighted_directed.go b/graph/multi/weighted_directed.go index de96f5c8..c8f86298 100644 --- a/graph/multi/weighted_directed.go +++ b/graph/multi/weighted_directed.go @@ -107,13 +107,13 @@ func (g *WeightedDirectedGraph) SetWeightedLine(l graph.WeightedLine) { lid = l.ID() ) - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } if g.from[fid][tid] == nil { g.from[fid][tid] = make(map[int64]graph.WeightedLine) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } if g.to[tid][fid] == nil { @@ -153,8 +153,8 @@ func (g *WeightedDirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *WeightedDirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *WeightedDirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -213,30 +213,30 @@ func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge { } // From returns all nodes in g that can be reached directly from n. -func (g *WeightedDirectedGraph) From(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *WeightedDirectedGraph) From(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - from := make([]graph.Node, len(g.from[n.ID()])) + from := make([]graph.Node, len(g.from[id])) i := 0 - for id := range g.from[n.ID()] { - from[i] = g.nodes[id] + for vid := range g.from[id] { + from[i] = g.nodes[vid] i++ } return from } // To returns all nodes in g that can reach directly to n. -func (g *WeightedDirectedGraph) To(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *WeightedDirectedGraph) To(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - to := make([]graph.Node, len(g.to[n.ID()])) + to := make([]graph.Node, len(g.to[id])) i := 0 - for id := range g.to[n.ID()] { - to[i] = g.nodes[id] + for uid := range g.to[id] { + to[i] = g.nodes[uid] i++ } return to @@ -244,9 +244,7 @@ func (g *WeightedDirectedGraph) To(n graph.Node) []graph.Node { // HasEdgeBetween returns whether an edge exists between nodes x and y without // considering direction. -func (g *WeightedDirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g *WeightedDirectedGraph) HasEdgeBetween(xid, yid int64) bool { if _, ok := g.from[xid][yid]; ok { return true } @@ -255,8 +253,8 @@ func (g *WeightedDirectedGraph) HasEdgeBetween(x, y graph.Node) bool { } // HasEdgeFromTo returns whether an edge exists in the graph from u to v. -func (g *WeightedDirectedGraph) HasEdgeFromTo(u, v graph.Node) bool { - if _, ok := g.from[u.ID()][v.ID()]; !ok { +func (g *WeightedDirectedGraph) HasEdgeFromTo(uid, vid int64) bool { + if _, ok := g.from[uid][vid]; !ok { return false } return true @@ -265,15 +263,15 @@ func (g *WeightedDirectedGraph) HasEdgeFromTo(u, v graph.Node) bool { // Edge returns the edge 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 returned graph.Edge is a multi.WeightedEdge if an edge exists. -func (g *WeightedDirectedGraph) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdge(u, v) +func (g *WeightedDirectedGraph) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdge(uid, vid) } // WeightedEdge returns the weighted edge 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 returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists. -func (g *WeightedDirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - lines := g.WeightedLines(u, v) +func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { + lines := g.WeightedLines(uid, vid) if len(lines) == 0 { return nil } @@ -282,8 +280,8 @@ func (g *WeightedDirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEdge // Lines returns the lines from u to v if such any such lines exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedDirectedGraph) Lines(u, v graph.Node) []graph.Line { - edge := g.from[u.ID()][v.ID()] +func (g *WeightedDirectedGraph) Lines(uid, vid int64) []graph.Line { + edge := g.from[uid][vid] if len(edge) == 0 { return nil } @@ -296,8 +294,8 @@ func (g *WeightedDirectedGraph) Lines(u, v graph.Node) []graph.Line { // WeightedLines returns the weighted lines from u to v if such any such lines exists // and nil otherwise. The node v must be directly reachable from u as defined by the From method. -func (g *WeightedDirectedGraph) WeightedLines(u, v graph.Node) []graph.WeightedLine { - edge := g.from[u.ID()][v.ID()] +func (g *WeightedDirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLine { + edge := g.from[uid][vid] if len(edge) == 0 { return nil } @@ -310,21 +308,21 @@ func (g *WeightedDirectedGraph) WeightedLines(u, v graph.Node) []graph.WeightedL // Weight returns the weight for the lines between x and y summarised by the receiver's // EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise. -func (g *WeightedDirectedGraph) Weight(u, v graph.Node) (w float64, ok bool) { - lines := g.WeightedLines(u, v) +func (g *WeightedDirectedGraph) Weight(uid, vid int64) (w float64, ok bool) { + lines := g.WeightedLines(uid, vid) return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0 } // Degree returns the in+out degree of n in g. -func (g *WeightedDirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *WeightedDirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } var deg int - for _, e := range g.from[n.ID()] { + for _, e := range g.from[id] { deg += len(e) } - for _, e := range g.to[n.ID()] { + for _, e := range g.to[id] { deg += len(e) } return deg diff --git a/graph/multi/weighted_directed_test.go b/graph/multi/weighted_directed_test.go index 84d18af8..d4e564be 100644 --- a/graph/multi/weighted_directed_test.go +++ b/graph/multi/weighted_directed_test.go @@ -22,7 +22,7 @@ var ( func TestWeightedEdgeOvercounting(t *testing.T) { g := generateDummyWeightedGraph() - if neigh := g.From(Node(Node(2))); len(neigh) != 2 { + if neigh := 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)) } } diff --git a/graph/multi/weighted_undirected.go b/graph/multi/weighted_undirected.go index 3118833e..28546c83 100644 --- a/graph/multi/weighted_undirected.go +++ b/graph/multi/weighted_undirected.go @@ -99,13 +99,13 @@ func (g *WeightedUndirectedGraph) SetWeighted(l graph.WeightedLine) { lid = l.ID() ) - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } if g.lines[fid][tid] == nil { g.lines[fid][tid] = make(map[int64]graph.WeightedLine) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } if g.lines[tid][fid] == nil { @@ -142,8 +142,8 @@ func (g *WeightedUndirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *WeightedUndirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *WeightedUndirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -189,14 +189,14 @@ func (g *WeightedUndirectedGraph) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *WeightedUndirectedGraph) From(n graph.Node) []graph.Node { - if !g.Has(n) { +func (g *WeightedUndirectedGraph) From(id int64) []graph.Node { + if !g.Has(id) { return nil } - nodes := make([]graph.Node, len(g.lines[n.ID()])) + nodes := make([]graph.Node, len(g.lines[id])) i := 0 - for from := range g.lines[n.ID()] { + for from := range g.lines[id] { nodes[i] = g.nodes[from] i++ } @@ -204,20 +204,20 @@ func (g *WeightedUndirectedGraph) From(n graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *WeightedUndirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - _, ok := g.lines[x.ID()][y.ID()] +func (g *WeightedUndirectedGraph) HasEdgeBetween(xid, yid int64) bool { + _, ok := g.lines[xid][yid] return ok } // Lines returns the lines from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedUndirectedGraph) Lines(u, v graph.Node) []graph.Line { - return g.LinesBetween(u, v) +func (g *WeightedUndirectedGraph) Lines(uid, vid int64) []graph.Line { + return g.LinesBetween(uid, vid) } // LinesBetween returns the lines between nodes x and y. -func (g *WeightedUndirectedGraph) LinesBetween(x, y graph.Node) []graph.Line { - edge := g.lines[x.ID()][y.ID()] +func (g *WeightedUndirectedGraph) LinesBetween(xid, yid int64) []graph.Line { + edge := g.lines[xid][yid] if len(edge) == 0 { return nil } @@ -237,20 +237,20 @@ func (g *WeightedUndirectedGraph) LinesBetween(x, y graph.Node) []graph.Line { // Edge returns the edge 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 returned graph.Edge is a multi.WeightedEdge if an edge exists. -func (g *WeightedUndirectedGraph) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdge(u, v) +func (g *WeightedUndirectedGraph) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdge(uid, vid) } // EdgeBetween returns the edge between nodes x and y. -func (g *WeightedUndirectedGraph) EdgeBetween(x, y graph.Node) graph.Edge { - return g.WeightedEdge(x, y) +func (g *WeightedUndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge { + return g.WeightedEdge(xid, yid) } // WeightedEdge returns the weighted edge 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 returned graph.WeightedEdge is a multi.WeightedEdge if an edge exists. -func (g *WeightedUndirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - lines := g.WeightedLines(u, v) +func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { + lines := g.WeightedLines(uid, vid) if len(lines) == 0 { return nil } @@ -258,19 +258,19 @@ func (g *WeightedUndirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEd } // WeightedEdgeBetween returns the weighted edge between nodes x and y. -func (g *WeightedUndirectedGraph) WeightedEdgeBetween(x, y graph.Node) graph.WeightedEdge { - return g.WeightedEdge(x, y) +func (g *WeightedUndirectedGraph) WeightedEdgeBetween(xid, yid int64) graph.WeightedEdge { + return g.WeightedEdge(xid, yid) } // WeightedLines returns the lines from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedUndirectedGraph) WeightedLines(u, v graph.Node) []graph.WeightedLine { - return g.WeightedLinesBetween(u, v) +func (g *WeightedUndirectedGraph) WeightedLines(uid, vid int64) []graph.WeightedLine { + return g.WeightedLinesBetween(uid, vid) } // WeightedLinesBetween returns the lines between nodes x and y. -func (g *WeightedUndirectedGraph) WeightedLinesBetween(x, y graph.Node) []graph.WeightedLine { - edge := g.lines[x.ID()][y.ID()] +func (g *WeightedUndirectedGraph) WeightedLinesBetween(xid, yid int64) []graph.WeightedLine { + edge := g.lines[xid][yid] if len(edge) == 0 { return nil } @@ -289,18 +289,18 @@ func (g *WeightedUndirectedGraph) WeightedLinesBetween(x, y graph.Node) []graph. // Weight returns the weight for the lines between x and y summarised by the receiver's // EdgeWeightFunc. Weight returns true if an edge exists between x and y, false otherwise. -func (g *WeightedUndirectedGraph) Weight(x, y graph.Node) (w float64, ok bool) { - lines := g.WeightedLines(x, y) +func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) { + lines := g.WeightedLines(xid, yid) return WeightedEdge{Lines: lines, WeightFunc: g.EdgeWeightFunc}.Weight(), len(lines) != 0 } // Degree returns the degree of n in g. -func (g *WeightedUndirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *WeightedUndirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } var deg int - for _, e := range g.lines[n.ID()] { + for _, e := range g.lines[id] { deg += len(e) } return deg diff --git a/graph/multi/weighted_undirected_test.go b/graph/multi/weighted_undirected_test.go index dc32a45c..df190902 100644 --- a/graph/multi/weighted_undirected_test.go +++ b/graph/multi/weighted_undirected_test.go @@ -23,7 +23,7 @@ func TestWeightedMaxID(t *testing.T) { delete(nodes, Node(2)) n := g.NewNode() g.AddNode(n) - if !g.Has(n) { + if !g.Has(n.ID()) { t.Error("added node does not exist in graph") } if _, exists := nodes[n]; exists { diff --git a/graph/multigraph.go b/graph/multigraph.go index 67adaf59..249f9294 100644 --- a/graph/multigraph.go +++ b/graph/multigraph.go @@ -19,43 +19,46 @@ type WeightedLine interface { // Multigraph is a generalized multigraph. type Multigraph interface { - // Has returns whether the node exists within the multigraph. - Has(Node) bool + // Has returns whether the node with the given ID exists + // within the multigraph. + Has(id int64) bool // Nodes returns all the nodes in the multigraph. Nodes() []Node // From returns all nodes that can be reached directly - // from the given node. - From(Node) []Node + // from the node with the given ID. + From(id int64) []Node // HasEdgeBetween returns whether an edge exists between - // nodes x and y without considering direction. - HasEdgeBetween(x, y Node) bool + // nodes with IDs xid and yid without considering direction. + HasEdgeBetween(xid, yid int64) bool - // Lines returns the lines from u to v if any such lines - // exist and nil otherwise. The node v must be directly - // reachable from u as defined by the From method. - Lines(u, v Node) []Line + // Lines returns the lines from u to v, with IDs uid and + // vid, if any such lines exist and nil otherwise. The + // node v must be directly reachable from u as defined by + // the From method. + Lines(uid, vid int64) []Line } // WeightedMultigraph is a weighted multigraph. type WeightedMultigraph interface { Multigraph - // WeightedLines returns the weighted lines from u to v if - // any such lines exist and nil otherwise. The node v must - // be directly reachable from u as defined by the - // From method. - WeightedLines(u, v Node) []WeightedLine + // WeightedLines returns the weighted lines from u to v + // with IDs uid and vid if any such lines exist and nil + // otherwise. The node v must be directly reachable + // from u as defined by the From method. + WeightedLines(uid, vid int64) []WeightedLine } // UndirectedMultigraph is an undirected multigraph. type UndirectedMultigraph interface { Multigraph - // LinesBetween returns the lines between nodes x and y. - LinesBetween(x, y Node) []Line + // LinesBetween returns the lines between nodes x and y + // with IDs xid and yid. + LinesBetween(xid, yid int64) []Line } // WeightedUndirectedMultigraph is a weighted undirected multigraph. @@ -63,8 +66,8 @@ type WeightedUndirectedMultigraph interface { WeightedMultigraph // WeightedLinesBetween returns the lines between nodes - // x and y. - WeightedLinesBetween(x, y Node) []WeightedLine + // x and y with IDs xid and yid. + WeightedLinesBetween(xid, yid int64) []WeightedLine } // DirectedMultigraph is a directed multigraph. @@ -72,12 +75,13 @@ type DirectedMultigraph interface { Multigraph // HasEdgeFromTo returns whether an edge exists - // in the multigraph from u to v. - HasEdgeFromTo(u, v Node) bool + // in the multigraph from u to v with IDs uid + // and vid. + HasEdgeFromTo(uid, vid int64) bool // To returns all nodes that can reach directly - // to the given node. - To(Node) []Node + // to the node with the given ID. + To(id int64) []Node } // WeightedDirectedMultigraph is a weighted directed multigraph. @@ -85,12 +89,13 @@ type WeightedDirectedMultigraph interface { WeightedMultigraph // HasEdgeFromTo returns whether an edge exists - // in the multigraph from u to v. - HasEdgeFromTo(u, v Node) bool + // in the multigraph from u to v with IDs uid + // and vid. + HasEdgeFromTo(uid, vid int64) bool // To returns all nodes that can reach directly - // to the given node. - To(Node) []Node + // to the node with the given ID. + To(id int64) []Node } // LineAdder is an interface for adding lines to a multigraph. diff --git a/graph/network/betweenness.go b/graph/network/betweenness.go index abb3face..70b816c9 100644 --- a/graph/network/betweenness.go +++ b/graph/network/betweenness.go @@ -115,17 +115,19 @@ func brandes(g graph.Graph, accumulate func(s graph.Node, stack linear.NodeStack queue.Enqueue(s) for queue.Len() != 0 { v := queue.Dequeue() + vid := v.ID() stack.Push(v) - for _, w := range g.From(v) { + for _, w := range g.From(vid) { + wid := w.ID() // w found for the first time? - if d[w.ID()] < 0 { + if d[wid] < 0 { queue.Enqueue(w) - d[w.ID()] = d[v.ID()] + 1 + d[wid] = d[vid] + 1 } // shortest path to w via v? - if d[w.ID()] == d[v.ID()]+1 { - sigma[w.ID()] += sigma[v.ID()] - p[w.ID()] = append(p[w.ID()], v) + if d[wid] == d[vid]+1 { + sigma[wid] += sigma[vid] + p[wid] = append(p[wid], v) } } } @@ -151,18 +153,20 @@ func BetweennessWeighted(g graph.Weighted, p path.AllShortest) map[int64]float64 nodes := g.Nodes() for i, s := range nodes { + sid := s.ID() for j, t := range nodes { if i == j { continue } - d := p.Weight(s, t) + tid := t.ID() + d := p.Weight(sid, tid) if math.IsInf(d, 0) { continue } // If we have a unique path, don't do the // extra work needed to get all paths. - path, _, unique := p.Between(s, t) + path, _, unique := p.Between(sid, tid) if unique { for _, v := range path[1 : len(path)-1] { // For undirected graphs we double count @@ -174,7 +178,7 @@ func BetweennessWeighted(g graph.Weighted, p path.AllShortest) map[int64]float64 } // Otherwise iterate over all paths. - paths, _ := p.AllBetween(s, t) + paths, _ := p.AllBetween(sid, tid) stFrac := 1 / float64(len(paths)) for _, path := range paths { for _, v := range path[1 : len(path)-1] { @@ -203,18 +207,20 @@ func EdgeBetweennessWeighted(g graph.Weighted, p path.AllShortest) map[[2]int64] _, isUndirected := g.(graph.Undirected) nodes := g.Nodes() for i, s := range nodes { + sid := s.ID() for j, t := range nodes { if i == j { continue } - d := p.Weight(s, t) + tid := t.ID() + d := p.Weight(sid, tid) if math.IsInf(d, 0) { continue } // If we have a unique path, don't do the // extra work needed to get all paths. - path, _, unique := p.Between(s, t) + path, _, unique := p.Between(sid, tid) if unique { for k, v := range path[1:] { // For undirected graphs we double count @@ -231,7 +237,7 @@ func EdgeBetweennessWeighted(g graph.Weighted, p path.AllShortest) map[[2]int64] } // Otherwise iterate over all paths. - paths, _ := p.AllBetween(s, t) + paths, _ := p.AllBetween(sid, tid) stFrac := 1 / float64(len(paths)) for _, path := range paths { for k, v := range path[1:] { diff --git a/graph/network/betweenness_test.go b/graph/network/betweenness_test.go index bcea1461..75864733 100644 --- a/graph/network/betweenness_test.go +++ b/graph/network/betweenness_test.go @@ -179,7 +179,7 @@ func TestBetweenness(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -208,7 +208,7 @@ func TestEdgeBetweenness(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -240,7 +240,7 @@ func TestBetweennessWeighted(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, math.Inf(1)) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -276,7 +276,7 @@ func TestEdgeBetweennessWeighted(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, math.Inf(1)) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/network/diffusion.go b/graph/network/diffusion.go index 11b5832f..087c4b8a 100644 --- a/graph/network/diffusion.go +++ b/graph/network/diffusion.go @@ -122,9 +122,9 @@ func NewLaplacian(g graph.Undirected) Laplacian { l := mat.NewSymDense(len(nodes), nil) for j, u := range nodes { - to := g.From(u) - l.SetSym(j, j, float64(len(to))) uid := u.ID() + to := g.From(uid) + l.SetSym(j, j, float64(len(to))) for _, v := range to { vid := v.ID() if uid == vid { @@ -155,12 +155,12 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian { l := mat.NewSymDense(len(nodes), nil) for j, u := range nodes { - to := g.From(u) + uid := u.ID() + to := g.From(uid) if len(to) == 0 { continue } l.SetSym(j, j, 1) - uid := u.ID() squdeg := math.Sqrt(float64(len(to))) for _, v := range to { vid := v.ID() @@ -168,7 +168,7 @@ func NewSymNormLaplacian(g graph.Undirected) Laplacian { panic("network: self edge in graph") } if uid < vid { - l.SetSym(indexOf[vid], j, -1/(squdeg*math.Sqrt(float64(len(g.From(v)))))) + l.SetSym(indexOf[vid], j, -1/(squdeg*math.Sqrt(float64(len(g.From(vid)))))) } } } @@ -193,7 +193,7 @@ func NewRandomWalkLaplacian(g graph.Graph, damp float64) Laplacian { l := mat.NewDense(len(nodes), len(nodes), nil) for j, u := range nodes { uid := u.ID() - to := g.From(u) + to := g.From(uid) if len(to) == 0 { continue } diff --git a/graph/network/diffusion_test.go b/graph/network/diffusion_test.go index b5bc993f..e8b2f8e3 100644 --- a/graph/network/diffusion_test.go +++ b/graph/network/diffusion_test.go @@ -152,7 +152,7 @@ func TestDiffuse(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -278,7 +278,7 @@ func TestRandomWalkLaplacian(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -466,7 +466,7 @@ func TestDiffuseToEquilibrium(t *testing.T) { g := test.builder for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/network/distance.go b/graph/network/distance.go index ce37ddca..a97695c5 100644 --- a/graph/network/distance.go +++ b/graph/network/distance.go @@ -22,12 +22,14 @@ func Closeness(g graph.Graph, p path.AllShortest) map[int64]float64 { nodes := g.Nodes() c := make(map[int64]float64, len(nodes)) for _, u := range nodes { + uid := u.ID() var sum float64 for _, v := range nodes { + vid := v.ID() // The ordering here is not relevant for // undirected graphs, but we make sure we // are counting incoming paths. - d := p.Weight(v, u) + d := p.Weight(vid, uid) if math.IsInf(d, 0) { continue } @@ -49,12 +51,14 @@ func Farness(g graph.Graph, p path.AllShortest) map[int64]float64 { nodes := g.Nodes() f := make(map[int64]float64, len(nodes)) for _, u := range nodes { + uid := u.ID() var sum float64 for _, v := range nodes { + vid := v.ID() // The ordering here is not relevant for // undirected graphs, but we make sure we // are counting incoming paths. - d := p.Weight(v, u) + d := p.Weight(vid, uid) if math.IsInf(d, 0) { continue } @@ -76,12 +80,14 @@ func Harmonic(g graph.Graph, p path.AllShortest) map[int64]float64 { nodes := g.Nodes() h := make(map[int64]float64, len(nodes)) for i, u := range nodes { + uid := u.ID() var sum float64 for j, v := range nodes { + vid := v.ID() // The ordering here is not relevant for // undirected graphs, but we make sure we // are counting incoming paths. - d := p.Weight(v, u) + d := p.Weight(vid, uid) if math.IsInf(d, 0) { continue } @@ -105,12 +111,14 @@ func Residual(g graph.Graph, p path.AllShortest) map[int64]float64 { nodes := g.Nodes() r := make(map[int64]float64, len(nodes)) for i, u := range nodes { + uid := u.ID() var sum float64 for j, v := range nodes { + vid := v.ID() // The ordering here is not relevant for // undirected graphs, but we make sure we // are counting incoming paths. - d := p.Weight(v, u) + d := p.Weight(vid, uid) if math.IsInf(d, 0) { continue } diff --git a/graph/network/distance_test.go b/graph/network/distance_test.go index f2e8ec2c..02bb724d 100644 --- a/graph/network/distance_test.go +++ b/graph/network/distance_test.go @@ -146,7 +146,7 @@ func TestDistanceCentralityUndirected(t *testing.T) { g := simple.NewWeightedUndirectedGraph(0, math.Inf(1)) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -336,7 +336,7 @@ func TestDistanceCentralityDirected(t *testing.T) { g := simple.NewWeightedDirectedGraph(0, math.Inf(1)) for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/network/hits.go b/graph/network/hits.go index 763a32ee..fe8d05ca 100644 --- a/graph/network/hits.go +++ b/graph/network/hits.go @@ -32,10 +32,11 @@ func HITS(g graph.Directed, tol float64) map[int64]HubAuthority { nodesLinkingTo := make([][]int, len(nodes)) nodesLinkedFrom := make([][]int, len(nodes)) for i, n := range nodes { - for _, u := range g.To(n) { + id := n.ID() + for _, u := range g.To(id) { nodesLinkingTo[i] = append(nodesLinkingTo[i], indexOf[u.ID()]) } - for _, v := range g.From(n) { + for _, v := range g.From(id) { nodesLinkedFrom[i] = append(nodesLinkedFrom[i], indexOf[v.ID()]) } } diff --git a/graph/network/hits_test.go b/graph/network/hits_test.go index a2ec30c7..af1ea087 100644 --- a/graph/network/hits_test.go +++ b/graph/network/hits_test.go @@ -46,7 +46,7 @@ func TestHITS(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/network/page.go b/graph/network/page.go index 980ff6e7..7d7ee0a5 100644 --- a/graph/network/page.go +++ b/graph/network/page.go @@ -35,7 +35,7 @@ func PageRank(g graph.Directed, damp, tol float64) map[int64]float64 { m := mat.NewDense(len(nodes), len(nodes), nil) dangling := damp / float64(len(nodes)) for j, u := range nodes { - to := g.From(u) + to := g.From(u.ID()) f := damp / float64(len(to)) for _, v := range to { m.Set(indexOf[v.ID()], j, f) @@ -109,7 +109,7 @@ func PageRankSparse(g graph.Directed, damp, tol float64) map[int64]float64 { var dangling compressedRow df := damp / float64(len(nodes)) for j, u := range nodes { - to := g.From(u) + to := g.From(u.ID()) f := damp / float64(len(to)) for _, v := range to { m.addTo(indexOf[v.ID()], j, f) diff --git a/graph/network/page_test.go b/graph/network/page_test.go index 3d37fd8e..ac53148e 100644 --- a/graph/network/page_test.go +++ b/graph/network/page_test.go @@ -84,7 +84,7 @@ func TestPageRank(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -108,7 +108,7 @@ func TestPageRankSparse(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/path/a_star.go b/graph/path/a_star.go index b4e2609e..a91da5f6 100644 --- a/graph/path/a_star.go +++ b/graph/path/a_star.go @@ -24,7 +24,7 @@ import ( // falling back to NullHeuristic otherwise. If the graph does not implement graph.Weighter, // UniformCost is used. AStar will panic if g has an A*-reachable negative edge weight. func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded int) { - if !g.Has(s) || !g.Has(t) { + if !g.Has(s.ID()) || !g.Has(t.ID()) { return Shortest{from: s}, 0 } var weight Weighting @@ -59,14 +59,14 @@ func AStar(s, t graph.Node, g graph.Graph, h Heuristic) (path Shortest, expanded } visited.Add(uid) - for _, v := range g.From(u.node) { + for _, v := range g.From(u.node.ID()) { vid := v.ID() if visited.Has(vid) { continue } j := path.indexOf[vid] - w, ok := weight(u.node, v) + w, ok := weight(u.node.ID(), vid) if !ok { panic("A*: unexpected invalid weight") } diff --git a/graph/path/a_star_test.go b/graph/path/a_star_test.go index 88e2ea47..e4bd3c57 100644 --- a/graph/path/a_star_test.go +++ b/graph/path/a_star_test.go @@ -129,7 +129,7 @@ func TestAStar(t *testing.T) { for _, test := range aStarTests { pt, _ := AStar(simple.Node(test.s), simple.Node(test.t), test.g, test.heuristic) - p, cost := pt.To(simple.Node(test.t)) + p, cost := pt.To(test.t) if !topo.IsPathIn(test.g, p) { t.Errorf("got path that is not path in input graph for %q", test.name) @@ -139,7 +139,7 @@ func TestAStar(t *testing.T) { if !ok { t.Fatalf("unexpected negative cycle in %q", test.name) } - if want := bfp.WeightTo(simple.Node(test.t)); cost != want { + if want := bfp.WeightTo(test.t); cost != want { t.Errorf("unexpected cost for %q: got:%v want:%v", test.name, cost, want) } @@ -196,8 +196,8 @@ func TestExhaustiveAStar(t *testing.T) { for _, start := range g.Nodes() { for _, goal := range g.Nodes() { pt, _ := AStar(start, goal, g, heuristic) - gotPath, gotWeight := pt.To(goal) - wantPath, wantWeight, _ := ps.Between(start, goal) + gotPath, gotWeight := pt.To(goal.ID()) + wantPath, wantWeight, _ := ps.Between(start.ID(), goal.ID()) if gotWeight != wantWeight { t.Errorf("unexpected path weight from %v to %v result: got:%f want:%f", start, goal, gotWeight, wantWeight) @@ -231,7 +231,7 @@ func isMonotonic(g UndirectedWeightLister, h Heuristic) (ok bool, at graph.Edge, for _, edge := range g.WeightedEdges() { from := edge.From() to := edge.To() - w, ok := g.Weight(from, to) + w, ok := g.Weight(from.ID(), to.ID()) if !ok { panic("A*: unexpected invalid weight") } @@ -275,12 +275,12 @@ func TestAStarNullHeuristic(t *testing.T) { t.Fatalf("%q: unexpected from node ID: got:%d want:%d", test.Name, pt.From().ID(), test.Query.From().ID()) } - p, weight := pt.To(test.Query.To()) + p, weight := pt.To(test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) } - if weight := pt.WeightTo(test.Query.To()); weight != test.Weight { + if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.Name, weight, test.Weight) } @@ -301,7 +301,7 @@ func TestAStarNullHeuristic(t *testing.T) { test.Name, p, test.WantPaths) } - np, weight := pt.To(test.NoPathFor.To()) + np, weight := pt.To(test.NoPathFor.To().ID()) if pt.From().ID() == test.NoPathFor.From().ID() && (np != nil || !math.IsInf(weight, 1)) { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f\nwant:path= weight=+Inf", test.Name, np, weight) diff --git a/graph/path/bellman_ford_moore.go b/graph/path/bellman_ford_moore.go index 03e37428..9786175b 100644 --- a/graph/path/bellman_ford_moore.go +++ b/graph/path/bellman_ford_moore.go @@ -12,7 +12,7 @@ import "gonum.org/v1/gonum/graph" // // The time complexity of BellmanFordFrom is O(|V|.|E|). func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) { - if !g.Has(u) { + if !g.Has(u.ID()) { return Shortest{from: u}, true } var weight Weighting @@ -32,9 +32,11 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) { for i := 1; i < len(nodes); i++ { changed := false for j, u := range nodes { - for _, v := range g.From(u) { - k := path.indexOf[v.ID()] - w, ok := weight(u, v) + uid := u.ID() + for _, v := range g.From(uid) { + vid := v.ID() + k := path.indexOf[vid] + w, ok := weight(uid, vid) if !ok { panic("bellman-ford: unexpected invalid weight") } @@ -51,9 +53,11 @@ func BellmanFordFrom(u graph.Node, g graph.Graph) (path Shortest, ok bool) { } for j, u := range nodes { - for _, v := range g.From(u) { - k := path.indexOf[v.ID()] - w, ok := weight(u, v) + uid := u.ID() + for _, v := range g.From(uid) { + vid := v.ID() + k := path.indexOf[vid] + w, ok := weight(uid, vid) if !ok { panic("bellman-ford: unexpected invalid weight") } diff --git a/graph/path/bellman_ford_moore_test.go b/graph/path/bellman_ford_moore_test.go index c9416448..2d4b9c2b 100644 --- a/graph/path/bellman_ford_moore_test.go +++ b/graph/path/bellman_ford_moore_test.go @@ -35,12 +35,12 @@ func TestBellmanFordFrom(t *testing.T) { t.Fatalf("%q: unexpected from node ID: got:%d want:%d", test.Name, pt.From().ID(), test.Query.From().ID()) } - p, weight := pt.To(test.Query.To()) + p, weight := pt.To(test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) } - if weight := pt.WeightTo(test.Query.To()); weight != test.Weight { + if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.Name, weight, test.Weight) } @@ -61,7 +61,7 @@ func TestBellmanFordFrom(t *testing.T) { test.Name, p, test.WantPaths) } - np, weight := pt.To(test.NoPathFor.To()) + np, weight := pt.To(test.NoPathFor.To().ID()) if pt.From().ID() == test.NoPathFor.From().ID() && (np != nil || !math.IsInf(weight, 1)) { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f\nwant:path= weight=+Inf", test.Name, np, weight) diff --git a/graph/path/control_flow_bench_test.go b/graph/path/control_flow_bench_test.go index cdb50ef5..d2506aef 100644 --- a/graph/path/control_flow_bench_test.go +++ b/graph/path/control_flow_bench_test.go @@ -201,7 +201,7 @@ func BenchmarkRandomGraphDominators(b *testing.B) { if v == nil { v = unordered[ui][rnd.Intn(len(unordered[ui]))] } - if !g.HasEdgeFromTo(u, v) { + if !g.HasEdgeFromTo(u.ID(), v.ID()) { g.SetEdge(g.NewEdge(u, v)) } } @@ -253,16 +253,16 @@ type undirected struct { *simple.DirectedGraph } -func (g undirected) From(n graph.Node) []graph.Node { - return append(g.DirectedGraph.From(n), g.DirectedGraph.To(n)...) +func (g undirected) From(id int64) []graph.Node { + return append(g.DirectedGraph.From(id), g.DirectedGraph.To(id)...) } -func (g undirected) HasEdgeBetween(x, y graph.Node) bool { - return g.DirectedGraph.HasEdgeFromTo(x, y) +func (g undirected) HasEdgeBetween(xid, yid int64) bool { + return g.DirectedGraph.HasEdgeFromTo(xid, yid) } -func (g undirected) EdgeBetween(x, y graph.Node) graph.Edge { - return g.DirectedGraph.Edge(x, y) +func (g undirected) EdgeBetween(xid, yid int64) graph.Edge { + return g.DirectedGraph.Edge(xid, yid) } func (g undirected) SetEdge(e graph.Edge) { diff --git a/graph/path/control_flow_lt.go b/graph/path/control_flow_lt.go index 0062c1a2..7bb66980 100644 --- a/graph/path/control_flow_lt.go +++ b/graph/path/control_flow_lt.go @@ -140,7 +140,7 @@ func (lt *lengauerTarjan) dfs(g graph.Directed, v graph.Node) { ltv.label = ltv lt.nodes = append(lt.nodes, ltv) - for _, w := range g.From(v) { + for _, w := range g.From(v.ID()) { wid := w.ID() idx, ok := lt.indexOf[wid] diff --git a/graph/path/control_flow_slt.go b/graph/path/control_flow_slt.go index 2b6a995f..379b92d9 100644 --- a/graph/path/control_flow_slt.go +++ b/graph/path/control_flow_slt.go @@ -162,7 +162,7 @@ func (lt *sLengauerTarjan) dfs(g graph.Directed, v graph.Node) { ltv.label = ltv lt.nodes = append(lt.nodes, ltv) - for _, w := range g.From(v) { + for _, w := range g.From(v.ID()) { wid := w.ID() idx, ok := lt.indexOf[wid] diff --git a/graph/path/dijkstra.go b/graph/path/dijkstra.go index bdbc49bd..6a8a37eb 100644 --- a/graph/path/dijkstra.go +++ b/graph/path/dijkstra.go @@ -16,7 +16,7 @@ import ( // // The time complexity of DijkstrFrom is O(|E|.log|V|). func DijkstraFrom(u graph.Node, g graph.Graph) Shortest { - if !g.Has(u) { + if !g.Has(u.ID()) { return Shortest{from: u} } var weight Weighting @@ -46,9 +46,11 @@ func DijkstraFrom(u graph.Node, g graph.Graph) Shortest { if mid.dist > path.dist[k] { continue } - for _, v := range g.From(mid.node) { - j := path.indexOf[v.ID()] - w, ok := weight(mid.node, v) + mnid := mid.node.ID() + for _, v := range g.From(mnid) { + vid := v.ID() + j := path.indexOf[vid] + w, ok := weight(mnid, vid) if !ok { panic("dijkstra: unexpected invalid weight") } @@ -106,9 +108,11 @@ func dijkstraAllPaths(g graph.Graph, paths AllShortest) { if mid.dist < paths.dist.At(i, k) { paths.dist.Set(i, k, mid.dist) } - for _, v := range g.From(mid.node) { - j := paths.indexOf[v.ID()] - w, ok := weight(mid.node, v) + mnid := mid.node.ID() + for _, v := range g.From(mnid) { + vid := v.ID() + j := paths.indexOf[vid] + w, ok := weight(mnid, vid) if !ok { panic("dijkstra: unexpected invalid weight") } diff --git a/graph/path/dijkstra_test.go b/graph/path/dijkstra_test.go index 54a2d23c..fe6450e2 100644 --- a/graph/path/dijkstra_test.go +++ b/graph/path/dijkstra_test.go @@ -47,12 +47,12 @@ func TestDijkstraFrom(t *testing.T) { t.Fatalf("%q: unexpected from node ID: got:%d want:%d", test.Name, pt.From().ID(), test.Query.From().ID()) } - p, weight := pt.To(test.Query.To()) + p, weight := pt.To(test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) } - if weight := pt.WeightTo(test.Query.To()); weight != test.Weight { + if weight := pt.WeightTo(test.Query.To().ID()); weight != test.Weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.Name, weight, test.Weight) } @@ -73,7 +73,7 @@ func TestDijkstraFrom(t *testing.T) { test.Name, p, test.WantPaths) } - np, weight := pt.To(test.NoPathFor.To()) + np, weight := pt.To(test.NoPathFor.To().ID()) if pt.From().ID() == test.NoPathFor.From().ID() && (np != nil || !math.IsInf(weight, 1)) { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f\nwant:path= weight=+Inf", test.Name, np, weight) @@ -111,12 +111,12 @@ func TestDijkstraAllPaths(t *testing.T) { // Check all random paths returned are OK. for i := 0; i < 10; i++ { - p, weight, unique := pt.Between(test.Query.From(), test.Query.To()) + p, weight, unique := pt.Between(test.Query.From().ID(), test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) } - if weight := pt.Weight(test.Query.From(), test.Query.To()); weight != test.Weight { + if weight := pt.Weight(test.Query.From().ID(), test.Query.To().ID()); weight != test.Weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.Name, weight, test.Weight) } @@ -142,13 +142,13 @@ func TestDijkstraAllPaths(t *testing.T) { } } - np, weight, unique := pt.Between(test.NoPathFor.From(), test.NoPathFor.To()) + np, weight, unique := pt.Between(test.NoPathFor.From().ID(), test.NoPathFor.To().ID()) if np != nil || !math.IsInf(weight, 1) || unique { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f unique=%t\nwant:path= weight=+Inf unique=false", test.Name, np, weight, unique) } - paths, weight := pt.AllBetween(test.Query.From(), test.Query.To()) + paths, weight := pt.AllBetween(test.Query.From().ID(), test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) @@ -169,7 +169,7 @@ func TestDijkstraAllPaths(t *testing.T) { test.Name, got, test.WantPaths) } - nps, weight := pt.AllBetween(test.NoPathFor.From(), test.NoPathFor.To()) + nps, weight := pt.AllBetween(test.NoPathFor.From().ID(), test.NoPathFor.To().ID()) if nps != nil || !math.IsInf(weight, 1) { t.Errorf("%q: unexpected path:\ngot: paths=%v weight=%f\nwant:path= weight=+Inf", test.Name, nps, weight) diff --git a/graph/path/dynamic/dstarlite.go b/graph/path/dynamic/dstarlite.go index 5e0a82dd..70b23dfc 100644 --- a/graph/path/dynamic/dstarlite.go +++ b/graph/path/dynamic/dstarlite.go @@ -99,12 +99,14 @@ func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel } } for _, u := range d.model.Nodes() { - for _, v := range g.From(u) { - w := edgeWeight(d.weight, u, v) + uid := u.ID() + for _, v := range g.From(uid) { + vid := v.ID() + w := edgeWeight(d.weight, uid, vid) if w < 0 { panic("D* Lite: negative edge weight") } - d.model.SetWeightedEdge(simple.WeightedEdge{F: u, T: d.model.Node(v.ID()), W: w}) + d.model.SetWeightedEdge(simple.WeightedEdge{F: u, T: d.model.Node(vid), W: w}) } } @@ -120,8 +122,8 @@ func NewDStarLite(s, t graph.Node, g graph.Graph, h path.Heuristic, m WorldModel // edgeWeight is a helper function that returns the weight of the edge between // two connected nodes, u and v, using the provided weight function. It panics // if there is no edge between u and v. -func edgeWeight(weight path.Weighting, u, v graph.Node) float64 { - w, ok := weight(u, v) +func edgeWeight(weight path.Weighting, uid, vid int64) float64 { + w, ok := weight(uid, vid) if !ok { panic("D* Lite: unexpected invalid weight") } @@ -187,29 +189,33 @@ func (d *DStarLite) findShortestPath() { if !u.key.less(d.keyFor(d.s)) && d.s.rhs <= d.s.g { break } + uid := u.ID() switch kNew := d.keyFor(u); { case u.key.less(kNew): d.queue.update(u, kNew) case u.g > u.rhs: u.g = u.rhs d.queue.remove(u) - for _, _s := range d.model.To(u) { + for _, _s := range d.model.To(uid) { s := _s.(*dStarLiteNode) - if s.ID() != d.t.ID() { - s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, s, u)+u.g) + sid := s.ID() + if sid != d.t.ID() { + s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, uid)+u.g) } d.update(s) } default: gOld := u.g u.g = math.Inf(1) - for _, _s := range append(d.model.To(u), u) { + for _, _s := range append(d.model.To(uid), u) { s := _s.(*dStarLiteNode) - if s.rhs == edgeWeight(d.model.Weight, s, u)+gOld { + sid := s.ID() + if s.rhs == edgeWeight(d.model.Weight, sid, uid)+gOld { if s.ID() != d.t.ID() { s.rhs = math.Inf(1) - for _, t := range d.model.From(s) { - s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, s, t)+t.(*dStarLiteNode).g) + for _, t := range d.model.From(sid) { + tid := t.ID() + s.rhs = math.Min(s.rhs, edgeWeight(d.model.Weight, sid, tid)+t.(*dStarLiteNode).g) } } } @@ -243,9 +249,10 @@ func (d *DStarLite) Step() bool { min := math.Inf(1) var next *dStarLiteNode - for _, _s := range d.model.From(d.s) { + dsid := d.s.ID() + for _, _s := range d.model.From(dsid) { s := _s.(*dStarLiteNode) - w := edgeWeight(d.model.Weight, d.s, s) + s.g + w := edgeWeight(d.model.Weight, dsid, s.ID()) + s.g if w < min || (w == min && s.rhs < rhs) { next = s min = w @@ -294,24 +301,27 @@ func (d *DStarLite) UpdateWorld(changes []graph.Edge) { d.last = d.s for _, e := range changes { from := e.From() + fid := from.ID() to := e.To() - c, _ := d.weight(from, to) + tid := to.ID() + c, _ := d.weight(fid, tid) if c < 0 { panic("D* Lite: negative edge weight") } - cOld, _ := d.model.Weight(from, to) + cOld, _ := d.model.Weight(fid, tid) u := d.worldNodeFor(from) v := d.worldNodeFor(to) d.model.SetWeightedEdge(simple.WeightedEdge{F: u, T: v, W: c}) + uid := u.ID() if cOld > c { - if u.ID() != d.t.ID() { + if uid != d.t.ID() { u.rhs = math.Min(u.rhs, c+v.g) } } else if u.rhs == cOld+v.g { - if u.ID() != d.t.ID() { + if uid != d.t.ID() { u.rhs = math.Inf(1) - for _, t := range d.model.From(u) { - u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, u, t)+t.(*dStarLiteNode).g) + for _, t := range d.model.From(uid) { + u.rhs = math.Min(u.rhs, edgeWeight(d.model.Weight, uid, t.ID())+t.(*dStarLiteNode).g) } } } @@ -354,9 +364,11 @@ func (d *DStarLite) Path() (p []graph.Node, weight float64) { next *dStarLiteNode cost float64 ) - for _, _v := range d.model.From(u) { + uid := u.ID() + for _, _v := range d.model.From(uid) { v := _v.(*dStarLiteNode) - w := edgeWeight(d.model.Weight, u, v) + vid := v.ID() + w := edgeWeight(d.model.Weight, uid, vid) if rhs := w + v.g; rhs < min || (rhs == min && v.rhs < rhsMin) { next = v min = rhs diff --git a/graph/path/dynamic/dstarlite_test.go b/graph/path/dynamic/dstarlite_test.go index 495b31fc..90442d7b 100644 --- a/graph/path/dynamic/dstarlite_test.go +++ b/graph/path/dynamic/dstarlite_test.go @@ -389,7 +389,8 @@ var dynamicDStarLiteTests = []struct { all := l.Grid.AllVisible l.Grid.AllVisible = false for _, n := range l.Nodes() { - l.Known[n.ID()] = !l.Grid.Has(n) + id := n.ID() + l.Known[id] = !l.Grid.Has(id) } l.Grid.AllVisible = all @@ -401,19 +402,21 @@ var dynamicDStarLiteTests = []struct { // Check we have a correctly modified representation. for _, u := range l.Nodes() { + uid := u.ID() for _, v := range l.Nodes() { - if l.HasEdgeBetween(u, v) != l.Grid.HasEdgeBetween(u, v) { - ur, uc := l.RowCol(u.ID()) - vr, vc := l.RowCol(v.ID()) + vid := v.ID() + if l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) { + ur, uc := l.RowCol(uid) + vr, vc := l.RowCol(vid) if (ur == wallRow && uc == wallCol) || (vr == wallRow && vc == wallCol) { - if !l.HasEdgeBetween(u, v) { + if !l.HasEdgeBetween(uid, vid) { panic(fmt.Sprintf("expected to believe edge between %v (%d,%d) and %v (%d,%d) is passable", u, v, ur, uc, vr, vc)) } continue } panic(fmt.Sprintf("disagreement about edge between %v (%d,%d) and %v (%d,%d): got:%t want:%t", - u, v, ur, uc, vr, vc, l.HasEdgeBetween(u, v), l.Grid.HasEdgeBetween(u, v))) + u, v, ur, uc, vr, vc, l.HasEdgeBetween(uid, vid), l.Grid.HasEdgeBetween(uid, vid))) } } } @@ -574,8 +577,8 @@ func TestDStarLiteDynamic(t *testing.T) { l.MoveTo(test.s) heuristic := func(a, b graph.Node) float64 { - ax, ay := l.XY(a) - bx, by := l.XY(b) + ax, ay := l.XY(a.ID()) + bx, by := l.XY(b.ID()) return test.heuristic(ax-bx, ay-by) } @@ -657,7 +660,7 @@ func weightOf(path []graph.Node, g graph.Weighted) float64 { var w float64 if len(path) > 1 { for p, n := range path[1:] { - ew, ok := g.Weight(path[p], n) + ew, ok := g.Weight(path[p].ID(), n.ID()) if !ok { return math.Inf(1) } @@ -673,7 +676,7 @@ func simpleWeightedEdgesOf(g graph.Weighted, edges []graph.Edge) []simple.Weight for i, e := range edges { w[i].F = e.From() w[i].T = e.To() - ew, _ := g.Weight(e.From(), e.To()) + ew, _ := g.Weight(e.From().ID(), e.To().ID()) w[i].W = ew } return w diff --git a/graph/path/dynamic/dumper_test.go b/graph/path/dynamic/dumper_test.go index 950d613e..b7fa9392 100644 --- a/graph/path/dynamic/dumper_test.go +++ b/graph/path/dynamic/dumper_test.go @@ -61,7 +61,7 @@ func (d *dumper) dump(withpath bool) { switch ln { case 0: if n.ID() == d.grid.Location.ID() { - if d.grid.Grid.HasOpen(n) { + if d.grid.Grid.HasOpen(n.ID()) { fmt.Fprintf(w, "id:%2d >@<", n.ID()) } else { // Mark location as illegal. @@ -70,19 +70,19 @@ func (d *dumper) dump(withpath bool) { } else if n.ID() == d.dStarLite.t.ID() { fmt.Fprintf(w, "id:%2d G", n.ID()) // Mark goal cell as illegal. - if !d.grid.Grid.HasOpen(n) { + if !d.grid.Grid.HasOpen(n.ID()) { fmt.Fprint(w, "!") } } else if pathStep[n.ID()] > 0 { fmt.Fprintf(w, "id:%2d %2d", n.ID(), pathStep[n.ID()]) // Mark path cells with an obstruction. - if !d.grid.Grid.HasOpen(n) { + if !d.grid.Grid.HasOpen(n.ID()) { fmt.Fprint(w, "!") } } else { fmt.Fprintf(w, "id:%2d", n.ID()) // Mark cells with an obstruction. - if !d.grid.Grid.HasOpen(n) { + if !d.grid.Grid.HasOpen(n.ID()) { fmt.Fprint(w, " *") } } diff --git a/graph/path/floydwarshall.go b/graph/path/floydwarshall.go index 1ffe25bd..db9d9148 100644 --- a/graph/path/floydwarshall.go +++ b/graph/path/floydwarshall.go @@ -23,9 +23,11 @@ func FloydWarshall(g graph.Graph) (paths AllShortest, ok bool) { paths = newAllShortest(nodes, true) for i, u := range nodes { paths.dist.Set(i, i, 0) - for _, v := range g.From(u) { - j := paths.indexOf[v.ID()] - w, ok := weight(u, v) + uid := u.ID() + for _, v := range g.From(uid) { + vid := v.ID() + j := paths.indexOf[vid] + w, ok := weight(uid, vid) if !ok { panic("floyd-warshall: unexpected invalid weight") } diff --git a/graph/path/floydwarshall_test.go b/graph/path/floydwarshall_test.go index 3947ad2e..45dc1b2e 100644 --- a/graph/path/floydwarshall_test.go +++ b/graph/path/floydwarshall_test.go @@ -35,12 +35,12 @@ func TestFloydWarshall(t *testing.T) { // Check all random paths returned are OK. for i := 0; i < 10; i++ { - p, weight, unique := pt.Between(test.Query.From(), test.Query.To()) + p, weight, unique := pt.Between(test.Query.From().ID(), test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) } - if weight := pt.Weight(test.Query.From(), test.Query.To()); weight != test.Weight { + if weight := pt.Weight(test.Query.From().ID(), test.Query.To().ID()); weight != test.Weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.Name, weight, test.Weight) } @@ -66,13 +66,13 @@ func TestFloydWarshall(t *testing.T) { } } - np, weight, unique := pt.Between(test.NoPathFor.From(), test.NoPathFor.To()) + np, weight, unique := pt.Between(test.NoPathFor.From().ID(), test.NoPathFor.To().ID()) if np != nil || !math.IsInf(weight, 1) || unique { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f unique=%t\nwant:path= weight=+Inf unique=false", test.Name, np, weight, unique) } - paths, weight := pt.AllBetween(test.Query.From(), test.Query.To()) + paths, weight := pt.AllBetween(test.Query.From().ID(), test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) @@ -93,7 +93,7 @@ func TestFloydWarshall(t *testing.T) { test.Name, got, test.WantPaths) } - nps, weight := pt.AllBetween(test.NoPathFor.From(), test.NoPathFor.To()) + nps, weight := pt.AllBetween(test.NoPathFor.From().ID(), test.NoPathFor.To().ID()) if nps != nil || !math.IsInf(weight, 1) { t.Errorf("%q: unexpected path:\ngot: paths=%v weight=%f\nwant:path= weight=+Inf", test.Name, nps, weight) diff --git a/graph/path/internal/grid.go b/graph/path/internal/grid.go index cc7e1366..f062c049 100644 --- a/graph/path/internal/grid.go +++ b/graph/path/internal/grid.go @@ -104,17 +104,12 @@ func (g *Grid) Nodes() []graph.Node { // Has returns whether n is a node in the grid. The state of // the AllVisible field determines whether a non-open node is // present. -func (g *Grid) Has(n graph.Node) bool { - return g.has(n.ID()) -} - -func (g *Grid) has(id int64) bool { +func (g *Grid) Has(id int64) bool { return 0 <= id && id < int64(len(g.open)) && (g.AllVisible || g.open[id]) } // HasOpen returns whether n is an open node in the grid. -func (g *Grid) HasOpen(n graph.Node) bool { - id := n.ID() +func (g *Grid) HasOpen(id int64) bool { return 0 <= id && id < int64(len(g.open)) && g.open[id] } @@ -145,11 +140,11 @@ func (g *Grid) RowCol(id int64) (r, c int) { // XY returns the cartesian coordinates of n. If n is not a node // in the grid, (NaN, NaN) is returned. -func (g *Grid) XY(n graph.Node) (x, y float64) { - if !g.Has(n) { +func (g *Grid) XY(id int64) (x, y float64) { + if !g.Has(id) { return math.NaN(), math.NaN() } - r, c := g.RowCol(n.ID()) + r, c := g.RowCol(id) return float64(c), float64(r) } @@ -163,15 +158,15 @@ func (g *Grid) NodeAt(r, c int) graph.Node { // From returns all the nodes reachable from u. Reachabilty requires that both // ends of an edge must be open. -func (g *Grid) From(u graph.Node) []graph.Node { - if !g.HasOpen(u) { +func (g *Grid) From(uid int64) []graph.Node { + if !g.HasOpen(uid) { return nil } - nr, nc := g.RowCol(u.ID()) + nr, nc := g.RowCol(uid) var to []graph.Node for r := nr - 1; r <= nr+1; r++ { for c := nc - 1; c <= nc+1; c++ { - if v := g.NodeAt(r, c); v != nil && g.HasEdgeBetween(u, v) { + if v := g.NodeAt(r, c); v != nil && g.HasEdgeBetween(uid, v.ID()) { to = append(to, v) } } @@ -180,12 +175,12 @@ func (g *Grid) From(u graph.Node) []graph.Node { } // HasEdgeBetween returns whether there is an edge between u and v. -func (g *Grid) HasEdgeBetween(u, v graph.Node) bool { - if !g.HasOpen(u) || !g.HasOpen(v) || u.ID() == v.ID() { +func (g *Grid) HasEdgeBetween(uid, vid int64) bool { + if !g.HasOpen(uid) || !g.HasOpen(vid) || uid == vid { return false } - ur, uc := g.RowCol(u.ID()) - vr, vc := g.RowCol(v.ID()) + ur, uc := g.RowCol(uid) + vr, vc := g.RowCol(vid) if abs(ur-vr) > 1 || abs(uc-vc) > 1 { return false } @@ -200,47 +195,47 @@ func abs(i int) int { } // Edge returns the edge between u and v. -func (g *Grid) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g *Grid) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdge returns the weighted edge between u and v. -func (g *Grid) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - return g.WeightedEdgeBetween(u, v) +func (g *Grid) WeightedEdge(uid, vid int64) graph.WeightedEdge { + return g.WeightedEdgeBetween(uid, vid) } // EdgeBetween returns the edge between u and v. -func (g *Grid) EdgeBetween(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g *Grid) EdgeBetween(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdgeBetween returns the weighted edge between u and v. -func (g *Grid) WeightedEdgeBetween(u, v graph.Node) graph.WeightedEdge { - if g.HasEdgeBetween(u, v) { +func (g *Grid) WeightedEdgeBetween(uid, vid int64) graph.WeightedEdge { + if g.HasEdgeBetween(uid, vid) { if !g.AllowDiagonal || g.UnitEdgeWeight { - return simple.WeightedEdge{F: u, T: v, W: 1} + return simple.WeightedEdge{F: simple.Node(uid), T: simple.Node(vid), W: 1} } - ux, uy := g.XY(u) - vx, vy := g.XY(v) - return simple.WeightedEdge{F: u, T: v, W: math.Hypot(ux-vx, uy-vy)} + ux, uy := g.XY(uid) + vx, vy := g.XY(vid) + return simple.WeightedEdge{F: simple.Node(uid), T: simple.Node(vid), W: math.Hypot(ux-vx, uy-vy)} } return nil } // Weight returns the weight of the given edge. -func (g *Grid) Weight(x, y graph.Node) (w float64, ok bool) { - if x.ID() == y.ID() { +func (g *Grid) Weight(xid, yid int64) (w float64, ok bool) { + if xid == yid { return 0, true } - if !g.HasEdgeBetween(x, y) { + if !g.HasEdgeBetween(xid, yid) { return math.Inf(1), false } - if e := g.EdgeBetween(x, y); e != nil { + if e := g.EdgeBetween(xid, yid); e != nil { if !g.AllowDiagonal || g.UnitEdgeWeight { return 1, true } - ux, uy := g.XY(e.From()) - vx, vy := g.XY(e.To()) + ux, uy := g.XY(e.From().ID()) + vx, vy := g.XY(e.To().ID()) return math.Hypot(ux-vx, uy-vy), true } return math.Inf(1), true @@ -274,15 +269,15 @@ func (g *Grid) Render(path []graph.Node) ([]byte, error) { // We don't use topo.IsPathIn at the outset because we // want to draw as much as possible before failing. for i, n := range path { - if !g.Has(n) || (i != 0 && !g.HasEdgeBetween(path[i-1], n)) { - id := n.ID() + id := n.ID() + if !g.Has(id) || (i != 0 && !g.HasEdgeBetween(path[i-1].ID(), id)) { if 0 <= id && id < int64(len(g.open)) { - r, c := g.RowCol(n.ID()) + r, c := g.RowCol(id) b[r*(g.c+1)+c] = '!' } return b, errors.New("grid: not a path in graph") } - r, c := g.RowCol(n.ID()) + r, c := g.RowCol(id) switch i { case len(path) - 1: b[r*(g.c+1)+c] = 'G' diff --git a/graph/path/internal/grid_test.go b/graph/path/internal/grid_test.go index 3034bba3..7132ffed 100644 --- a/graph/path/internal/grid_test.go +++ b/graph/path/internal/grid_test.go @@ -249,7 +249,7 @@ func TestGrid(t *testing.T) { } for _, test := range reach { g.AllowDiagonal = test.diagonal - got := g.From(test.from) + got := g.From(test.from.ID()) if !reflect.DeepEqual(got, test.to) { t.Fatalf("unexpected nodes from %d with allow diagonal=%t:\ngot: %v\nwant:%v", test.from, test.diagonal, got, test.to) diff --git a/graph/path/internal/limited.go b/graph/path/internal/limited.go index 8f86a473..6e2cfcdb 100644 --- a/graph/path/internal/limited.go +++ b/graph/path/internal/limited.go @@ -47,23 +47,25 @@ func (l *LimitedVisionGrid) MoveTo(n graph.Node) (new, old []graph.Edge) { if u == nil { continue } - ux, uy := l.XY(u) + uid := u.ID() + ux, uy := l.XY(uid) if math.Hypot(x-ux, y-uy) > l.VisionRadius { continue } - for _, v := range l.allPossibleFrom(u) { - if seen[[2]int64{u.ID(), v.ID()}] { + for _, v := range l.allPossibleFrom(uid) { + vid := v.ID() + if seen[[2]int64{uid, vid}] { continue } - seen[[2]int64{u.ID(), v.ID()}] = true + seen[[2]int64{uid, vid}] = true - vx, vy := l.XY(v) - if !l.Known[v.ID()] && math.Hypot(x-vx, y-vy) > l.VisionRadius { + vx, vy := l.XY(vid) + if !l.Known[vid] && math.Hypot(x-vx, y-vy) > l.VisionRadius { continue } e := simple.Edge{F: u, T: v} - if !l.Known[u.ID()] || !l.Known[v.ID()] { + if !l.Known[uid] || !l.Known[vid] { new = append(new, e) } else { old = append(old, e) @@ -79,18 +81,20 @@ func (l *LimitedVisionGrid) MoveTo(n graph.Node) (new, old []graph.Edge) { if u == nil { continue } - ux, uy := l.XY(u) + uid := u.ID() + ux, uy := l.XY(uid) if math.Hypot(x-ux, y-uy) > l.VisionRadius { continue } - for _, v := range l.allPossibleFrom(u) { - vx, vy := l.XY(v) + for _, v := range l.allPossibleFrom(uid) { + vid := v.ID() + vx, vy := l.XY(vid) if math.Hypot(x-vx, y-vy) > l.VisionRadius { continue } - l.Known[v.ID()] = true + l.Known[vid] = true } - l.Known[u.ID()] = true + l.Known[uid] = true } } @@ -100,19 +104,19 @@ func (l *LimitedVisionGrid) MoveTo(n graph.Node) (new, old []graph.Edge) { } // allPossibleFrom returns all the nodes possibly reachable from u. -func (l *LimitedVisionGrid) allPossibleFrom(u graph.Node) []graph.Node { - if !l.Has(u) { +func (l *LimitedVisionGrid) allPossibleFrom(uid int64) []graph.Node { + if !l.Has(uid) { return nil } - nr, nc := l.RowCol(u.ID()) + nr, nc := l.RowCol(uid) var to []graph.Node for r := nr - 1; r <= nr+1; r++ { for c := nc - 1; c <= nc+1; c++ { v := l.NodeAt(r, c) - if v == nil || u.ID() == v.ID() { + if v == nil || uid == v.ID() { continue } - ur, uc := l.RowCol(u.ID()) + ur, uc := l.RowCol(uid) vr, vc := l.RowCol(v.ID()) if abs(ur-vr) > 1 || abs(uc-vc) > 1 { continue @@ -134,11 +138,11 @@ func (l *LimitedVisionGrid) RowCol(id int64) (r, c int) { // XY returns the cartesian coordinates of n. If n is not a node // in the grid, (NaN, NaN) is returned. -func (l *LimitedVisionGrid) XY(n graph.Node) (x, y float64) { - if !l.Has(n) { +func (l *LimitedVisionGrid) XY(id int64) (x, y float64) { + if !l.Has(id) { return math.NaN(), math.NaN() } - r, c := l.RowCol(n.ID()) + r, c := l.RowCol(id) return float64(c), float64(r) } @@ -157,25 +161,21 @@ func (l *LimitedVisionGrid) NodeAt(r, c int) graph.Node { } // Has returns whether n is a node in the grid. -func (l *LimitedVisionGrid) Has(n graph.Node) bool { - return l.has(n.ID()) -} - -func (l *LimitedVisionGrid) has(id int64) bool { +func (l *LimitedVisionGrid) Has(id int64) bool { return 0 <= id && id < int64(len(l.Grid.open)) } // From returns nodes that are optimistically reachable from u. -func (l *LimitedVisionGrid) From(u graph.Node) []graph.Node { - if !l.Has(u) { +func (l *LimitedVisionGrid) From(uid int64) []graph.Node { + if !l.Has(uid) { return nil } - nr, nc := l.RowCol(u.ID()) + nr, nc := l.RowCol(uid) var to []graph.Node for r := nr - 1; r <= nr+1; r++ { for c := nc - 1; c <= nc+1; c++ { - if v := l.NodeAt(r, c); v != nil && l.HasEdgeBetween(u, v) { + if v := l.NodeAt(r, c); v != nil && l.HasEdgeBetween(uid, v.ID()) { to = append(to, v) } } @@ -184,12 +184,12 @@ func (l *LimitedVisionGrid) From(u graph.Node) []graph.Node { } // HasEdgeBetween optimistically returns whether an edge is exists between u and v. -func (l *LimitedVisionGrid) HasEdgeBetween(u, v graph.Node) bool { - if u.ID() == v.ID() { +func (l *LimitedVisionGrid) HasEdgeBetween(uid, vid int64) bool { + if uid == vid { return false } - ur, uc := l.RowCol(u.ID()) - vr, vc := l.RowCol(v.ID()) + ur, uc := l.RowCol(uid) + vr, vc := l.RowCol(vid) if abs(ur-vr) > 1 || abs(uc-vc) > 1 { return false } @@ -197,66 +197,66 @@ func (l *LimitedVisionGrid) HasEdgeBetween(u, v graph.Node) bool { return false } - x, y := l.XY(l.Location) - ux, uy := l.XY(u) - vx, vy := l.XY(v) - uKnown := l.Known[u.ID()] || math.Hypot(x-ux, y-uy) <= l.VisionRadius - vKnown := l.Known[v.ID()] || math.Hypot(x-vx, y-vy) <= l.VisionRadius + x, y := l.XY(l.Location.ID()) + ux, uy := l.XY(uid) + vx, vy := l.XY(vid) + uKnown := l.Known[uid] || math.Hypot(x-ux, y-uy) <= l.VisionRadius + vKnown := l.Known[vid] || math.Hypot(x-vx, y-vy) <= l.VisionRadius switch { case uKnown && vKnown: - return l.Grid.HasEdgeBetween(u, v) + return l.Grid.HasEdgeBetween(uid, vid) case uKnown: - return l.Grid.HasOpen(u) + return l.Grid.HasOpen(uid) case vKnown: - return l.Grid.HasOpen(v) + return l.Grid.HasOpen(vid) default: return true } } // Edge optimistically returns the edge from u to v. -func (l *LimitedVisionGrid) Edge(u, v graph.Node) graph.Edge { - return l.WeightedEdgeBetween(u, v) +func (l *LimitedVisionGrid) Edge(uid, vid int64) graph.Edge { + return l.WeightedEdgeBetween(uid, vid) } // Edge optimistically returns the weighted edge from u to v. -func (l *LimitedVisionGrid) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - return l.WeightedEdgeBetween(u, v) +func (l *LimitedVisionGrid) WeightedEdge(uid, vid int64) graph.WeightedEdge { + return l.WeightedEdgeBetween(uid, vid) } // WeightedEdgeBetween optimistically returns the edge between u and v. -func (l *LimitedVisionGrid) EdgeBetween(u, v graph.Node) graph.Edge { - return l.WeightedEdgeBetween(u, v) +func (l *LimitedVisionGrid) EdgeBetween(uid, vid int64) graph.Edge { + return l.WeightedEdgeBetween(uid, vid) } // WeightedEdgeBetween optimistically returns the weighted edge between u and v. -func (l *LimitedVisionGrid) WeightedEdgeBetween(u, v graph.Node) graph.WeightedEdge { - if l.HasEdgeBetween(u, v) { +func (l *LimitedVisionGrid) WeightedEdgeBetween(uid, vid int64) graph.WeightedEdge { + if l.HasEdgeBetween(uid, vid) { if !l.Grid.AllowDiagonal || l.Grid.UnitEdgeWeight { - return simple.WeightedEdge{F: u, T: v, W: 1} + return simple.WeightedEdge{F: simple.Node(uid), T: simple.Node(vid), W: 1} } - ux, uy := l.XY(u) - vx, vy := l.XY(v) - return simple.WeightedEdge{F: u, T: v, W: math.Hypot(ux-vx, uy-vy)} + ux, uy := l.XY(uid) + vx, vy := l.XY(vid) + return simple.WeightedEdge{F: simple.Node(uid), T: simple.Node(vid), W: math.Hypot(ux-vx, uy-vy)} } return nil } // Weight returns the weight of the given edge. -func (l *LimitedVisionGrid) Weight(x, y graph.Node) (w float64, ok bool) { - if x.ID() == y.ID() { +func (l *LimitedVisionGrid) Weight(xid, yid int64) (w float64, ok bool) { + if xid == yid { return 0, true } - if !l.HasEdgeBetween(x, y) { + if !l.HasEdgeBetween(xid, yid) { return math.Inf(1), false } - if e := l.EdgeBetween(x, y); e != nil { + if e := l.EdgeBetween(xid, yid); e != nil { if !l.Grid.AllowDiagonal || l.Grid.UnitEdgeWeight { return 1, true } - ux, uy := l.XY(e.From()) - vx, vy := l.XY(e.To()) + ux, uy := l.XY(e.From().ID()) + vx, vy := l.XY(e.To().ID()) return math.Hypot(ux-vx, uy-vy), true } @@ -294,15 +294,15 @@ func (l *LimitedVisionGrid) Render(path []graph.Node) ([]byte, error) { // We don't use topo.IsPathIn at the outset because we // want to draw as much as possible before failing. for i, n := range path { - if !l.Has(n) || (i != 0 && !l.HasEdgeBetween(path[i-1], n)) { - id := n.ID() + id := n.ID() + if !l.Has(id) || (i != 0 && !l.HasEdgeBetween(path[i-1].ID(), id)) { if 0 <= id && id < int64(len(l.Grid.open)) { - r, c := l.RowCol(n.ID()) + r, c := l.RowCol(id) b[r*(cols+1)+c] = '!' } return b, errors.New("grid: not a path in graph") } - r, c := l.RowCol(n.ID()) + r, c := l.RowCol(id) switch i { case len(path) - 1: b[r*(cols+1)+c] = 'G' diff --git a/graph/path/internal/limited_test.go b/graph/path/internal/limited_test.go index 8fa3efde..6372471d 100644 --- a/graph/path/internal/limited_test.go +++ b/graph/path/internal/limited_test.go @@ -1165,23 +1165,25 @@ func TestLimitedVisionGrid(t *testing.T) { } l.Grid.AllowDiagonal = test.diag - x, y := l.XY(test.path[0]) + x, y := l.XY(test.path[0].ID()) for _, u := range l.Nodes() { - ux, uy := l.XY(u) + uid := u.ID() + ux, uy := l.XY(uid) uNear := math.Hypot(x-ux, y-uy) <= test.radius for _, v := range l.Nodes() { - vx, vy := l.XY(v) + vid := v.ID() + vx, vy := l.XY(vid) vNear := math.Hypot(x-vx, y-vy) <= test.radius - if u.ID() == v.ID() && l.HasEdgeBetween(u, v) { + if u.ID() == v.ID() && l.HasEdgeBetween(uid, vid) { t.Errorf("unexpected self edge: %v -- %v", u, v) } - if !uNear && !vNear && !l.HasEdgeBetween(u, v) && couldConnectIn(l, u, v) { + if !uNear && !vNear && !l.HasEdgeBetween(uid, vid) && couldConnectIn(l, uid, vid) { t.Errorf("unexpected pessimism: no hope in distant edge between %v and %v for test %d", u, v, i) } - if (uNear && vNear) && l.HasEdgeBetween(u, v) != l.Grid.HasEdgeBetween(u, v) { + if (uNear && vNear) && l.HasEdgeBetween(uid, vid) != l.Grid.HasEdgeBetween(uid, vid) { t.Errorf("unrealistic optimism: disagreement about edge between %v and %v for test %d: got:%t want:%t", - u, v, i, l.HasEdgeBetween(u, v), l.Grid.HasEdgeBetween(u, v)) + u, v, i, l.HasEdgeBetween(uid, vid), l.Grid.HasEdgeBetween(uid, vid)) } } } @@ -1205,7 +1207,7 @@ func asConcreteEdges(changes []graph.Edge, in graph.Weighted) []simple.WeightedE for i, e := range changes { we[i].F = e.From() we[i].T = e.To() - w, ok := in.Weight(e.From(), e.To()) + w, ok := in.Weight(e.From().ID(), e.To().ID()) if !ok && !math.IsInf(w, 1) { panic("unexpected invalid finite weight") } @@ -1214,13 +1216,13 @@ func asConcreteEdges(changes []graph.Edge, in graph.Weighted) []simple.WeightedE return we } -func couldConnectIn(l *LimitedVisionGrid, u, v graph.Node) bool { - if u.ID() == v.ID() { +func couldConnectIn(l *LimitedVisionGrid, uid, vid int64) bool { + if uid == vid { return false } - ur, uc := l.RowCol(u.ID()) - vr, vc := l.RowCol(v.ID()) + ur, uc := l.RowCol(uid) + vr, vc := l.RowCol(vid) if abs(ur-vr) > 1 || abs(uc-vc) > 1 { return false } @@ -1228,13 +1230,13 @@ func couldConnectIn(l *LimitedVisionGrid, u, v graph.Node) bool { return false } - if !l.Known[u.ID()] && !l.Known[v.ID()] { + if !l.Known[uid] && !l.Known[vid] { return true } - if l.Known[u.ID()] && !l.Grid.HasOpen(u) { + if l.Known[uid] && !l.Grid.HasOpen(uid) { return false } - if l.Known[v.ID()] && !l.Grid.HasOpen(v) { + if l.Known[vid] && !l.Grid.HasOpen(vid) { return false } diff --git a/graph/path/johnson_apsp.go b/graph/path/johnson_apsp.go index cf064696..4902d323 100644 --- a/graph/path/johnson_apsp.go +++ b/graph/path/johnson_apsp.go @@ -52,12 +52,12 @@ func JohnsonAllPaths(g graph.Graph) (paths AllShortest, ok bool) { dijkstraAllPaths(jg, paths) for i, u := range paths.nodes { - hu := jg.adjustBy.WeightTo(u) + hu := jg.adjustBy.WeightTo(u.ID()) for j, v := range paths.nodes { if i == j { continue } - hv := jg.adjustBy.WeightTo(v) + hv := jg.adjustBy.WeightTo(v.ID()) paths.dist.Set(i, j, paths.dist.At(i, j)-hu+hv) } } @@ -69,8 +69,8 @@ type johnsonWeightAdjuster struct { q int64 g graph.Graph - from func(graph.Node) []graph.Node - edgeTo func(graph.Node, graph.Node) graph.Edge + from func(id int64) []graph.Node + edgeTo func(uid, vid int64) graph.Edge weight Weighting bellmanFord bool @@ -86,11 +86,11 @@ var ( _ graph.Weighted = johnsonWeightAdjuster{} ) -func (g johnsonWeightAdjuster) Has(n graph.Node) bool { - if g.bellmanFord && n.ID() == g.q { +func (g johnsonWeightAdjuster) Has(id int64) bool { + if g.bellmanFord && id == g.q { return true } - return g.g.Has(n) + return g.g.Has(id) } @@ -101,40 +101,40 @@ func (g johnsonWeightAdjuster) Nodes() []graph.Node { return g.g.Nodes() } -func (g johnsonWeightAdjuster) From(n graph.Node) []graph.Node { - if g.bellmanFord && n.ID() == g.q { +func (g johnsonWeightAdjuster) From(id int64) []graph.Node { + if g.bellmanFord && id == g.q { return g.g.Nodes() } - return g.from(n) + return g.from(id) } -func (g johnsonWeightAdjuster) WeightedEdge(u, v graph.Node) graph.WeightedEdge { +func (g johnsonWeightAdjuster) WeightedEdge(_, _ int64) graph.WeightedEdge { panic("path: unintended use of johnsonWeightAdjuster") } -func (g johnsonWeightAdjuster) Edge(u, v graph.Node) graph.Edge { - if g.bellmanFord && u.ID() == g.q && g.g.Has(v) { - return simple.Edge{F: johnsonGraphNode(g.q), T: v} +func (g johnsonWeightAdjuster) Edge(uid, vid int64) graph.Edge { + if g.bellmanFord && uid == g.q && g.g.Has(vid) { + return simple.Edge{F: johnsonGraphNode(g.q), T: simple.Node(vid)} } - return g.edgeTo(u, v) + return g.edgeTo(uid, vid) } -func (g johnsonWeightAdjuster) Weight(x, y graph.Node) (w float64, ok bool) { +func (g johnsonWeightAdjuster) Weight(xid, yid int64) (w float64, ok bool) { if g.bellmanFord { switch g.q { - case x.ID(): + case xid: return 0, true - case y.ID(): + case yid: return math.Inf(1), false default: - return g.weight(x, y) + return g.weight(xid, yid) } } - w, ok = g.weight(x, y) - return w + g.adjustBy.WeightTo(x) - g.adjustBy.WeightTo(y), ok + w, ok = g.weight(xid, yid) + return w + g.adjustBy.WeightTo(xid) - g.adjustBy.WeightTo(yid), ok } -func (johnsonWeightAdjuster) HasEdgeBetween(_, _ graph.Node) bool { +func (johnsonWeightAdjuster) HasEdgeBetween(_, _ int64) bool { panic("path: unintended use of johnsonWeightAdjuster") } diff --git a/graph/path/johnson_apsp_test.go b/graph/path/johnson_apsp_test.go index fcf62046..ca764863 100644 --- a/graph/path/johnson_apsp_test.go +++ b/graph/path/johnson_apsp_test.go @@ -35,12 +35,12 @@ func TestJohnsonAllPaths(t *testing.T) { // Check all random paths returned are OK. for i := 0; i < 10; i++ { - p, weight, unique := pt.Between(test.Query.From(), test.Query.To()) + p, weight, unique := pt.Between(test.Query.From().ID(), test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) } - if weight := pt.Weight(test.Query.From(), test.Query.To()); weight != test.Weight { + if weight := pt.Weight(test.Query.From().ID(), test.Query.To().ID()); weight != test.Weight { t.Errorf("%q: unexpected weight from Weight: got:%f want:%f", test.Name, weight, test.Weight) } @@ -66,13 +66,13 @@ func TestJohnsonAllPaths(t *testing.T) { } } - np, weight, unique := pt.Between(test.NoPathFor.From(), test.NoPathFor.To()) + np, weight, unique := pt.Between(test.NoPathFor.From().ID(), test.NoPathFor.To().ID()) if np != nil || !math.IsInf(weight, 1) || unique { t.Errorf("%q: unexpected path:\ngot: path=%v weight=%f unique=%t\nwant:path= weight=+Inf unique=false", test.Name, np, weight, unique) } - paths, weight := pt.AllBetween(test.Query.From(), test.Query.To()) + paths, weight := pt.AllBetween(test.Query.From().ID(), test.Query.To().ID()) if weight != test.Weight { t.Errorf("%q: unexpected weight from Between: got:%f want:%f", test.Name, weight, test.Weight) @@ -93,7 +93,7 @@ func TestJohnsonAllPaths(t *testing.T) { test.Name, got, test.WantPaths) } - nps, weight := pt.AllBetween(test.NoPathFor.From(), test.NoPathFor.To()) + nps, weight := pt.AllBetween(test.NoPathFor.From().ID(), test.NoPathFor.To().ID()) if nps != nil || !math.IsInf(weight, 1) { t.Errorf("%q: unexpected path:\ngot: paths=%v weight=%f\nwant:path= weight=+Inf", test.Name, nps, weight) diff --git a/graph/path/negative_cycles_example_test.go b/graph/path/negative_cycles_example_test.go index 91273dc4..f70d3f43 100644 --- a/graph/path/negative_cycles_example_test.go +++ b/graph/path/negative_cycles_example_test.go @@ -47,7 +47,7 @@ func ExampleBellmanFordFrom_negativecycles() { return } for _, n := range []simple.Node{'a', 'b', 'c', 'd', 'e', 'f'} { - p, w := pt.To(n) + p, w := pt.To(n.ID()) if math.IsNaN(w) { fmt.Printf("negative cycle in path to %c path:%c\n", n, p) } diff --git a/graph/path/shortest.go b/graph/path/shortest.go index 33876c6f..c2d120d4 100644 --- a/graph/path/shortest.go +++ b/graph/path/shortest.go @@ -93,8 +93,8 @@ func (p Shortest) From() graph.Node { return p.from } // WeightTo returns the weight of the minimum path to v. If the path to v includes // a negative cycle, the returned weight will not reflect the true path weight. -func (p Shortest) WeightTo(v graph.Node) float64 { - to, toOK := p.indexOf[v.ID()] +func (p Shortest) WeightTo(vid int64) float64 { + to, toOK := p.indexOf[vid] if !toOK { return math.Inf(1) } @@ -104,8 +104,8 @@ func (p Shortest) WeightTo(v graph.Node) float64 { // To returns a shortest path to v and the weight of the path. If the path // to v includes a negative cycle, one pass through the cycle will be included // in path and weight will be returned as NaN. -func (p Shortest) To(v graph.Node) (path []graph.Node, weight float64) { - to, toOK := p.indexOf[v.ID()] +func (p Shortest) To(vid int64) (path []graph.Node, weight float64) { + to, toOK := p.indexOf[vid] if !toOK || math.IsInf(p.dist[to], 1) { return nil, math.Inf(1) } @@ -136,7 +136,7 @@ func (p Shortest) To(v graph.Node) (path []graph.Node, weight float64) { } } ordered.Reverse(path) - return path, math.Min(weight, p.dist[p.indexOf[v.ID()]]) + return path, math.Min(weight, p.dist[p.indexOf[vid]]) } // AllShortest is a shortest-path tree created by the DijkstraAllPaths, FloydWarshall @@ -221,9 +221,9 @@ loop: // These are likely to be rare, so just loop over collisions. } // Weight returns the weight of the minimum path between u and v. -func (p AllShortest) Weight(u, v graph.Node) float64 { - from, fromOK := p.indexOf[u.ID()] - to, toOK := p.indexOf[v.ID()] +func (p AllShortest) Weight(uid, vid int64) float64 { + from, fromOK := p.indexOf[uid] + to, toOK := p.indexOf[vid] if !fromOK || !toOK { return math.Inf(1) } @@ -234,11 +234,11 @@ func (p AllShortest) Weight(u, v graph.Node) float64 { // one shortest path exists between u and v, a randomly chosen path will be returned and // unique is returned false. If a cycle with zero weight exists in the path, it will not // be included, but unique will be returned false. -func (p AllShortest) Between(u, v graph.Node) (path []graph.Node, weight float64, unique bool) { - from, fromOK := p.indexOf[u.ID()] - to, toOK := p.indexOf[v.ID()] +func (p AllShortest) Between(uid, vid int64) (path []graph.Node, weight float64, unique bool) { + from, fromOK := p.indexOf[uid] + to, toOK := p.indexOf[vid] if !fromOK || !toOK || len(p.at(from, to)) == 0 { - if u.ID() == v.ID() { + if uid == vid { return []graph.Node{p.nodes[from]}, 0, true } return nil, math.Inf(1), false @@ -290,11 +290,11 @@ func (p AllShortest) Between(u, v graph.Node) (path []graph.Node, weight float64 // AllBetween returns all shortest paths from u to v and the weight of the paths. Paths // containing zero-weight cycles are not returned. -func (p AllShortest) AllBetween(u, v graph.Node) (paths [][]graph.Node, weight float64) { - from, fromOK := p.indexOf[u.ID()] - to, toOK := p.indexOf[v.ID()] +func (p AllShortest) AllBetween(uid, vid int64) (paths [][]graph.Node, weight float64) { + from, fromOK := p.indexOf[uid] + to, toOK := p.indexOf[vid] if !fromOK || !toOK || len(p.at(from, to)) == 0 { - if u.ID() == v.ID() { + if uid == vid { return [][]graph.Node{{p.nodes[from]}}, 0 } return nil, math.Inf(1) @@ -302,9 +302,9 @@ func (p AllShortest) AllBetween(u, v graph.Node) (paths [][]graph.Node, weight f var n graph.Node if p.forward { - n = u + n = p.nodes[from] } else { - n = v + n = p.nodes[to] } seen := make([]bool, len(p.nodes)) paths = p.allBetween(from, to, seen, []graph.Node{n}, nil) diff --git a/graph/path/spanning_tree.go b/graph/path/spanning_tree.go index 79c83638..cee154d3 100644 --- a/graph/path/spanning_tree.go +++ b/graph/path/spanning_tree.go @@ -48,8 +48,9 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 { } u := nodes[0] - for _, v := range g.From(u) { - w, ok := g.Weight(u, v) + uid := u.ID() + for _, v := range g.From(uid) { + w, ok := g.Weight(uid, v.ID()) if !ok { panic("prim: unexpected invalid weight") } @@ -59,15 +60,16 @@ func Prim(dst WeightedBuilder, g graph.WeightedUndirected) float64 { var w float64 for q.Len() > 0 { e := heap.Pop(q).(simple.WeightedEdge) - if e.To() != nil && g.HasEdgeBetween(e.From(), e.To()) { - dst.SetWeightedEdge(g.WeightedEdge(e.From(), e.To())) + if e.To() != nil && g.HasEdgeBetween(e.From().ID(), e.To().ID()) { + dst.SetWeightedEdge(g.WeightedEdge(e.From().ID(), e.To().ID())) w += e.Weight() } u = e.From() - for _, n := range g.From(u) { + uid := u.ID() + for _, n := range g.From(uid) { if key, ok := q.key(n); ok { - w, ok := g.Weight(u, n) + w, ok := g.Weight(uid, n.ID()) if !ok { panic("prim: unexpected invalid weight") } @@ -173,7 +175,7 @@ func Kruskal(dst WeightedBuilder, g UndirectedWeightLister) float64 { for _, e := range edges { if s1, s2 := ds.find(e.From().ID()), ds.find(e.To().ID()); s1 != s2 { ds.union(s1, s2) - dst.SetWeightedEdge(g.WeightedEdge(e.From(), e.To())) + dst.SetWeightedEdge(g.WeightedEdge(e.From().ID(), e.To().ID())) w += e.Weight() } } diff --git a/graph/path/spanning_tree_test.go b/graph/path/spanning_tree_test.go index 007485f3..522579b6 100644 --- a/graph/path/spanning_tree_test.go +++ b/graph/path/spanning_tree_test.go @@ -268,7 +268,7 @@ func testMinumumSpanning(mst func(dst WeightedBuilder, g spanningGraph) float64, test.name, len(gotEdges), len(test.treeEdges)) } for _, e := range test.treeEdges { - w, ok := dst.Weight(e.From(), e.To()) + w, ok := dst.Weight(e.From().ID(), e.To().ID()) if !ok { t.Errorf("spanning tree edge not found in graph for %q: %+v", test.name, e) diff --git a/graph/path/weight.go b/graph/path/weight.go index 23b71f90..d5e416e9 100644 --- a/graph/path/weight.go +++ b/graph/path/weight.go @@ -12,18 +12,16 @@ import ( // Weighting is a mapping between a pair of nodes and a weight. It follows the // semantics of the Weighter interface. -type Weighting func(x, y graph.Node) (w float64, ok bool) +type Weighting func(xid, yid int64) (w float64, ok bool) // UniformCost returns a Weighting that returns an edge cost of 1 for existing // edges, zero for node identity and Inf for otherwise absent edges. func UniformCost(g graph.Graph) Weighting { - return func(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() + return func(xid, yid int64) (w float64, ok bool) { if xid == yid { return 0, true } - if e := g.Edge(x, y); e != nil { + if e := g.Edge(xid, yid); e != nil { return 1, true } return math.Inf(1), false diff --git a/graph/simple/dense_directed_matrix.go b/graph/simple/dense_directed_matrix.go index c393cace..366cee61 100644 --- a/graph/simple/dense_directed_matrix.go +++ b/graph/simple/dense_directed_matrix.go @@ -75,8 +75,8 @@ func (g *DirectedMatrix) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *DirectedMatrix) Has(n graph.Node) bool { - return g.has(n.ID()) +func (g *DirectedMatrix) Has(id int64) bool { + return g.has(id) } func (g *DirectedMatrix) has(id int64) bool { @@ -117,8 +117,7 @@ func (g *DirectedMatrix) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *DirectedMatrix) From(n graph.Node) []graph.Node { - id := n.ID() +func (g *DirectedMatrix) From(id int64) []graph.Node { if !g.has(id) { return nil } @@ -137,8 +136,7 @@ func (g *DirectedMatrix) From(n graph.Node) []graph.Node { } // To returns all nodes in g that can reach directly to n. -func (g *DirectedMatrix) To(n graph.Node) []graph.Node { - id := n.ID() +func (g *DirectedMatrix) To(id int64) []graph.Node { if !g.has(id) { return nil } @@ -158,12 +156,10 @@ func (g *DirectedMatrix) To(n graph.Node) []graph.Node { // HasEdgeBetween returns whether an edge exists between nodes x and y without // considering direction. -func (g *DirectedMatrix) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() +func (g *DirectedMatrix) HasEdgeBetween(xid, yid int64) bool { if !g.has(xid) { return false } - yid := y.ID() if !g.has(yid) { return false } @@ -173,27 +169,25 @@ func (g *DirectedMatrix) HasEdgeBetween(x, y graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *DirectedMatrix) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdge(u, v) +func (g *DirectedMatrix) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdge(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *DirectedMatrix) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - if g.HasEdgeFromTo(u, v) { - // x.ID() and y.ID() are not greater than maximum int by this point. - return WeightedEdge{F: g.Node(u.ID()), T: g.Node(v.ID()), W: g.mat.At(int(u.ID()), int(v.ID()))} +func (g *DirectedMatrix) WeightedEdge(uid, vid int64) graph.WeightedEdge { + if g.HasEdgeFromTo(uid, vid) { + // xid and yid are not greater than maximum int by this point. + return WeightedEdge{F: g.Node(uid), T: g.Node(vid), W: g.mat.At(int(uid), int(vid))} } return nil } // HasEdgeFromTo returns whether an edge exists in the graph from u to v. -func (g *DirectedMatrix) HasEdgeFromTo(u, v graph.Node) bool { - uid := u.ID() +func (g *DirectedMatrix) HasEdgeFromTo(uid, vid int64) bool { if !g.has(uid) { return false } - vid := v.ID() if !g.has(vid) { return false } @@ -205,9 +199,7 @@ func (g *DirectedMatrix) HasEdgeFromTo(u, v graph.Node) bool { // If x and y are the same node or there is no joining edge between the two nodes the weight // value returned is either the graph's absent or self value. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g *DirectedMatrix) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g *DirectedMatrix) Weight(xid, yid int64) (w float64, ok bool) { if xid == yid { return g.self, true } @@ -262,8 +254,7 @@ func (g *DirectedMatrix) RemoveEdge(e graph.Edge) { } // Degree returns the in+out degree of n in g. -func (g *DirectedMatrix) Degree(n graph.Node) int { - id := n.ID() +func (g *DirectedMatrix) Degree(id int64) int { if !g.has(id) { return 0 } diff --git a/graph/simple/dense_undirected_matrix.go b/graph/simple/dense_undirected_matrix.go index 9276d9e6..97142ab1 100644 --- a/graph/simple/dense_undirected_matrix.go +++ b/graph/simple/dense_undirected_matrix.go @@ -75,8 +75,8 @@ func (g *UndirectedMatrix) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *UndirectedMatrix) Has(n graph.Node) bool { - return g.has(n.ID()) +func (g *UndirectedMatrix) Has(id int64) bool { + return g.has(id) } func (g *UndirectedMatrix) has(id int64) bool { @@ -114,8 +114,7 @@ func (g *UndirectedMatrix) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *UndirectedMatrix) From(n graph.Node) []graph.Node { - id := n.ID() +func (g *UndirectedMatrix) From(id int64) []graph.Node { if !g.has(id) { return nil } @@ -134,12 +133,10 @@ func (g *UndirectedMatrix) From(n graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *UndirectedMatrix) HasEdgeBetween(u, v graph.Node) bool { - uid := u.ID() +func (g *UndirectedMatrix) HasEdgeBetween(uid, vid int64) bool { if !g.has(uid) { return false } - vid := v.ID() if !g.has(vid) { return false } @@ -149,26 +146,26 @@ func (g *UndirectedMatrix) HasEdgeBetween(u, v graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *UndirectedMatrix) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g *UndirectedMatrix) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *UndirectedMatrix) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - return g.WeightedEdgeBetween(u, v) +func (g *UndirectedMatrix) WeightedEdge(uid, vid int64) graph.WeightedEdge { + return g.WeightedEdgeBetween(uid, vid) } // EdgeBetween returns the edge between nodes x and y. -func (g *UndirectedMatrix) EdgeBetween(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g *UndirectedMatrix) EdgeBetween(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdgeBetween returns the weighted edge between nodes x and y. -func (g *UndirectedMatrix) WeightedEdgeBetween(u, v graph.Node) graph.WeightedEdge { - if g.HasEdgeBetween(u, v) { - // u.ID() and v.ID() are not greater than maximum int by this point. - return WeightedEdge{F: g.Node(u.ID()), T: g.Node(v.ID()), W: g.mat.At(int(u.ID()), int(v.ID()))} +func (g *UndirectedMatrix) WeightedEdgeBetween(uid, vid int64) graph.WeightedEdge { + if g.HasEdgeBetween(uid, vid) { + // uid and vid are not greater than maximum int by this point. + return WeightedEdge{F: g.Node(uid), T: g.Node(vid), W: g.mat.At(int(uid), int(vid))} } return nil } @@ -177,9 +174,7 @@ func (g *UndirectedMatrix) WeightedEdgeBetween(u, v graph.Node) graph.WeightedEd // If x and y are the same node or there is no joining edge between the two nodes the weight // value returned is either the graph's absent or self value. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g *UndirectedMatrix) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g *UndirectedMatrix) Weight(xid, yid int64) (w float64, ok bool) { if xid == yid { return g.self, true } @@ -234,8 +229,7 @@ func (g *UndirectedMatrix) RemoveEdge(e graph.Edge) { } // Degree returns the degree of n in g. -func (g *UndirectedMatrix) Degree(n graph.Node) int { - id := n.ID() +func (g *UndirectedMatrix) Degree(id int64) int { if !g.has(id) { return 0 } diff --git a/graph/simple/densegraph_test.go b/graph/simple/densegraph_test.go index 51cba241..595b4e9a 100644 --- a/graph/simple/densegraph_test.go +++ b/graph/simple/densegraph_test.go @@ -34,17 +34,17 @@ func TestBasicDenseImpassable(t *testing.T) { } for i := 0; i < 5; i++ { - if !dg.Has(Node(i)) { + if !dg.Has(int64(i)) { t.Errorf("Node that should exist doesn't: %d", i) } - if degree := dg.Degree(Node(i)); degree != 0 { + if degree := dg.Degree(int64(i)); degree != 0 { t.Errorf("Node in impassable graph has a neighbor. Node: %d Degree: %d", i, degree) } } for i := 5; i < 10; i++ { - if dg.Has(Node(i)) { + if dg.Has(int64(i)) { t.Errorf("Node exists that shouldn't: %d", i) } } @@ -57,17 +57,17 @@ func TestBasicDensePassable(t *testing.T) { } for i := 0; i < 5; i++ { - if !dg.Has(Node(i)) { + if !dg.Has(int64(i)) { t.Errorf("Node that should exist doesn't: %d", i) } - if degree := dg.Degree(Node(i)); degree != 4 { + if degree := dg.Degree(int64(i)); degree != 4 { t.Errorf("Node in passable graph missing neighbors. Node: %d Degree: %d", i, degree) } } for i := 5; i < 10; i++ { - if dg.Has(Node(i)) { + if dg.Has(int64(i)) { t.Errorf("Node exists that shouldn't: %d", i) } } @@ -77,29 +77,29 @@ func TestDirectedDenseAddRemove(t *testing.T) { dg := NewDirectedMatrix(10, math.Inf(1), 0, math.Inf(1)) dg.SetWeightedEdge(WeightedEdge{F: Node(0), T: Node(2), W: 1}) - if neighbors := dg.From(Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 || - dg.Edge(Node(0), Node(2)) == nil { + if neighbors := dg.From(int64(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 || + dg.Edge(int64(0), int64(2)) == nil { t.Errorf("Adding edge didn't create successor") } dg.RemoveEdge(Edge{F: Node(0), T: Node(2)}) - if neighbors := dg.From(Node(0)); len(neighbors) != 0 || dg.Edge(Node(0), Node(2)) != nil { + if neighbors := dg.From(int64(0)); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil { t.Errorf("Removing edge didn't properly remove successor") } - if neighbors := dg.To(Node(2)); len(neighbors) != 0 || dg.Edge(Node(0), Node(2)) != nil { + if neighbors := dg.To(int64(2)); len(neighbors) != 0 || dg.Edge(int64(0), int64(2)) != nil { t.Errorf("Removing directed edge wrongly kept predecessor") } dg.SetWeightedEdge(WeightedEdge{F: Node(0), T: Node(2), W: 2}) // I figure we've torture tested From/To at this point // so we'll just use the bool functions now - if dg.Edge(Node(0), Node(2)) == nil { + if dg.Edge(int64(0), int64(2)) == nil { t.Fatal("Adding directed edge didn't change successor back") } - c1, _ := dg.Weight(Node(2), Node(0)) - c2, _ := dg.Weight(Node(0), Node(2)) + c1, _ := dg.Weight(int64(2), int64(0)) + c2, _ := dg.Weight(int64(0), int64(2)) if c1 == c2 { t.Error("Adding directed edge affected cost in undirected manner") } @@ -109,13 +109,13 @@ func TestUndirectedDenseAddRemove(t *testing.T) { dg := NewUndirectedMatrix(10, math.Inf(1), 0, math.Inf(1)) dg.SetEdge(Edge{F: Node(0), T: Node(2)}) - if neighbors := dg.From(Node(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 || - dg.EdgeBetween(Node(0), Node(2)) == nil { + if neighbors := dg.From(int64(0)); len(neighbors) != 1 || neighbors[0].ID() != 2 || + dg.EdgeBetween(int64(0), int64(2)) == nil { t.Errorf("Couldn't add neighbor") } - if neighbors := dg.From(Node(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 || - dg.EdgeBetween(Node(2), Node(0)) == nil { + if neighbors := dg.From(int64(2)); len(neighbors) != 1 || neighbors[0].ID() != 0 || + dg.EdgeBetween(int64(2), int64(0)) == nil { t.Errorf("Adding an undirected neighbor didn't add it reciprocally") } } diff --git a/graph/simple/directed.go b/graph/simple/directed.go index 06500f17..d2a58f31 100644 --- a/graph/simple/directed.go +++ b/graph/simple/directed.go @@ -94,10 +94,10 @@ func (g *DirectedGraph) SetEdge(e graph.Edge) { panic("simple: adding self edge") } - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } @@ -126,8 +126,8 @@ func (g *DirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *DirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *DirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -157,30 +157,30 @@ func (g *DirectedGraph) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *DirectedGraph) From(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *DirectedGraph) From(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - from := make([]graph.Node, len(g.from[n.ID()])) + from := make([]graph.Node, len(g.from[id])) i := 0 - for id := range g.from[n.ID()] { - from[i] = g.nodes[id] + for vid := range g.from[id] { + from[i] = g.nodes[vid] i++ } return from } // To returns all nodes in g that can reach directly to n. -func (g *DirectedGraph) To(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *DirectedGraph) To(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - to := make([]graph.Node, len(g.to[n.ID()])) + to := make([]graph.Node, len(g.to[id])) i := 0 - for id := range g.to[n.ID()] { - to[i] = g.nodes[id] + for uid := range g.to[id] { + to[i] = g.nodes[uid] i++ } return to @@ -188,9 +188,7 @@ func (g *DirectedGraph) To(n graph.Node) []graph.Node { // HasEdgeBetween returns whether an edge exists between nodes x and y without // considering direction. -func (g *DirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g *DirectedGraph) HasEdgeBetween(xid, yid int64) bool { if _, ok := g.from[xid][yid]; ok { return true } @@ -200,8 +198,8 @@ func (g *DirectedGraph) HasEdgeBetween(x, y graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *DirectedGraph) Edge(u, v graph.Node) graph.Edge { - edge, ok := g.from[u.ID()][v.ID()] +func (g *DirectedGraph) Edge(uid, vid int64) graph.Edge { + edge, ok := g.from[uid][vid] if !ok { return nil } @@ -209,17 +207,17 @@ func (g *DirectedGraph) Edge(u, v graph.Node) graph.Edge { } // HasEdgeFromTo returns whether an edge exists in the graph from u to v. -func (g *DirectedGraph) HasEdgeFromTo(u, v graph.Node) bool { - if _, ok := g.from[u.ID()][v.ID()]; !ok { +func (g *DirectedGraph) HasEdgeFromTo(uid, vid int64) bool { + if _, ok := g.from[uid][vid]; !ok { return false } return true } // Degree returns the in+out degree of n in g. -func (g *DirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *DirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } - return len(g.from[n.ID()]) + len(g.to[n.ID()]) + return len(g.from[id]) + len(g.to[id]) } diff --git a/graph/simple/directed_test.go b/graph/simple/directed_test.go index 45bc5b1c..0de3fbd3 100644 --- a/graph/simple/directed_test.go +++ b/graph/simple/directed_test.go @@ -21,7 +21,7 @@ var ( func TestEdgeOvercounting(t *testing.T) { g := generateDummyGraph() - if neigh := g.From(Node(Node(2))); len(neigh) != 2 { + if neigh := 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)) } } diff --git a/graph/simple/undirected.go b/graph/simple/undirected.go index 818e506b..a43fcf16 100644 --- a/graph/simple/undirected.go +++ b/graph/simple/undirected.go @@ -86,10 +86,10 @@ func (g *UndirectedGraph) SetEdge(e graph.Edge) { panic("simple: adding self edge") } - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } @@ -118,8 +118,8 @@ func (g *UndirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *UndirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *UndirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -160,14 +160,14 @@ func (g *UndirectedGraph) Edges() []graph.Edge { } // From returns all nodes in g that can be reached directly from n. -func (g *UndirectedGraph) From(n graph.Node) []graph.Node { - if !g.Has(n) { +func (g *UndirectedGraph) From(id int64) []graph.Node { + if !g.Has(id) { return nil } - nodes := make([]graph.Node, len(g.edges[n.ID()])) + nodes := make([]graph.Node, len(g.edges[id])) i := 0 - for from := range g.edges[n.ID()] { + for from := range g.edges[id] { nodes[i] = g.nodes[from] i++ } @@ -175,20 +175,20 @@ func (g *UndirectedGraph) From(n graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *UndirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - _, ok := g.edges[x.ID()][y.ID()] +func (g *UndirectedGraph) HasEdgeBetween(xid, yid int64) bool { + _, ok := g.edges[xid][yid] return ok } // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *UndirectedGraph) Edge(u, v graph.Node) graph.Edge { - return g.EdgeBetween(u, v) +func (g *UndirectedGraph) Edge(uid, vid int64) graph.Edge { + return g.EdgeBetween(uid, vid) } // EdgeBetween returns the edge between nodes x and y. -func (g *UndirectedGraph) EdgeBetween(x, y graph.Node) graph.Edge { - edge, ok := g.edges[x.ID()][y.ID()] +func (g *UndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge { + edge, ok := g.edges[xid][yid] if !ok { return nil } @@ -196,9 +196,9 @@ func (g *UndirectedGraph) EdgeBetween(x, y graph.Node) graph.Edge { } // Degree returns the degree of n in g. -func (g *UndirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *UndirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } - return len(g.edges[n.ID()]) + return len(g.edges[id]) } diff --git a/graph/simple/undirected_test.go b/graph/simple/undirected_test.go index 63719aa0..9251bfe7 100644 --- a/graph/simple/undirected_test.go +++ b/graph/simple/undirected_test.go @@ -37,7 +37,7 @@ func TestMaxID(t *testing.T) { delete(nodes, Node(2)) n := g.NewNode() g.AddNode(n) - if !g.Has(n) { + if !g.Has(n.ID()) { t.Error("added node does not exist in graph") } if _, exists := nodes[n]; exists { diff --git a/graph/simple/weighted_directed.go b/graph/simple/weighted_directed.go index 93767930..004cb852 100644 --- a/graph/simple/weighted_directed.go +++ b/graph/simple/weighted_directed.go @@ -100,10 +100,10 @@ func (g *WeightedDirectedGraph) SetWeightedEdge(e graph.WeightedEdge) { panic("simple: adding self edge") } - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } @@ -132,8 +132,8 @@ func (g *WeightedDirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *WeightedDirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *WeightedDirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -174,30 +174,30 @@ func (g *WeightedDirectedGraph) WeightedEdges() []graph.WeightedEdge { } // From returns all nodes in g that can be reached directly from n. -func (g *WeightedDirectedGraph) From(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *WeightedDirectedGraph) From(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - from := make([]graph.Node, len(g.from[n.ID()])) + from := make([]graph.Node, len(g.from[id])) i := 0 - for id := range g.from[n.ID()] { - from[i] = g.nodes[id] + for vid := range g.from[id] { + from[i] = g.nodes[vid] i++ } return from } // To returns all nodes in g that can reach directly to n. -func (g *WeightedDirectedGraph) To(n graph.Node) []graph.Node { - if _, ok := g.from[n.ID()]; !ok { +func (g *WeightedDirectedGraph) To(id int64) []graph.Node { + if _, ok := g.from[id]; !ok { return nil } - to := make([]graph.Node, len(g.to[n.ID()])) + to := make([]graph.Node, len(g.to[id])) i := 0 - for id := range g.to[n.ID()] { - to[i] = g.nodes[id] + for uid := range g.to[id] { + to[i] = g.nodes[uid] i++ } return to @@ -205,9 +205,7 @@ func (g *WeightedDirectedGraph) To(n graph.Node) []graph.Node { // HasEdgeBetween returns whether an edge exists between nodes x and y without // considering direction. -func (g *WeightedDirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - xid := x.ID() - yid := y.ID() +func (g *WeightedDirectedGraph) HasEdgeBetween(xid, yid int64) bool { if _, ok := g.from[xid][yid]; ok { return true } @@ -217,14 +215,14 @@ func (g *WeightedDirectedGraph) HasEdgeBetween(x, y graph.Node) bool { // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedDirectedGraph) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdge(u, v) +func (g *WeightedDirectedGraph) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdge(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedDirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - edge, ok := g.from[u.ID()][v.ID()] +func (g *WeightedDirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { + edge, ok := g.from[uid][vid] if !ok { return nil } @@ -232,8 +230,8 @@ func (g *WeightedDirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEdge } // HasEdgeFromTo returns whether an edge exists in the graph from u to v. -func (g *WeightedDirectedGraph) HasEdgeFromTo(u, v graph.Node) bool { - if _, ok := g.from[u.ID()][v.ID()]; !ok { +func (g *WeightedDirectedGraph) HasEdgeFromTo(uid, vid int64) bool { + if _, ok := g.from[uid][vid]; !ok { return false } return true @@ -243,9 +241,7 @@ func (g *WeightedDirectedGraph) HasEdgeFromTo(u, v graph.Node) bool { // If x and y are the same node or there is no joining edge between the two nodes the weight // value returned is either the graph's absent or self value. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g *WeightedDirectedGraph) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g *WeightedDirectedGraph) Weight(xid, yid int64) (w float64, ok bool) { if xid == yid { return g.self, true } @@ -258,9 +254,9 @@ func (g *WeightedDirectedGraph) Weight(x, y graph.Node) (w float64, ok bool) { } // Degree returns the in+out degree of n in g. -func (g *WeightedDirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *WeightedDirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } - return len(g.from[n.ID()]) + len(g.to[n.ID()]) + return len(g.from[id]) + len(g.to[id]) } diff --git a/graph/simple/weighted_directed_test.go b/graph/simple/weighted_directed_test.go index e7d210d1..18c87414 100644 --- a/graph/simple/weighted_directed_test.go +++ b/graph/simple/weighted_directed_test.go @@ -23,7 +23,7 @@ var ( func TestWeightedEdgeOvercounting(t *testing.T) { g := generateDummyGraph() - if neigh := g.From(Node(Node(2))); len(neigh) != 2 { + if neigh := 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)) } } diff --git a/graph/simple/weighted_undirected.go b/graph/simple/weighted_undirected.go index 78fe81f6..9bf02f28 100644 --- a/graph/simple/weighted_undirected.go +++ b/graph/simple/weighted_undirected.go @@ -92,10 +92,10 @@ func (g *WeightedUndirectedGraph) SetWeightedEdge(e graph.WeightedEdge) { panic("simple: adding self edge") } - if !g.Has(from) { + if !g.Has(fid) { g.AddNode(from) } - if !g.Has(to) { + if !g.Has(tid) { g.AddNode(to) } @@ -124,8 +124,8 @@ func (g *WeightedUndirectedGraph) Node(id int64) graph.Node { } // Has returns whether the node exists within the graph. -func (g *WeightedUndirectedGraph) Has(n graph.Node) bool { - _, ok := g.nodes[n.ID()] +func (g *WeightedUndirectedGraph) Has(id int64) bool { + _, ok := g.nodes[id] return ok } @@ -185,14 +185,14 @@ func (g *WeightedUndirectedGraph) WeightedEdges() []graph.WeightedEdge { } // From returns all nodes in g that can be reached directly from n. -func (g *WeightedUndirectedGraph) From(n graph.Node) []graph.Node { - if !g.Has(n) { +func (g *WeightedUndirectedGraph) From(id int64) []graph.Node { + if !g.Has(id) { return nil } - nodes := make([]graph.Node, len(g.edges[n.ID()])) + nodes := make([]graph.Node, len(g.edges[id])) i := 0 - for from := range g.edges[n.ID()] { + for from := range g.edges[id] { nodes[i] = g.nodes[from] i++ } @@ -200,31 +200,31 @@ func (g *WeightedUndirectedGraph) From(n graph.Node) []graph.Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g *WeightedUndirectedGraph) HasEdgeBetween(x, y graph.Node) bool { - _, ok := g.edges[x.ID()][y.ID()] +func (g *WeightedUndirectedGraph) HasEdgeBetween(xid, yid int64) bool { + _, ok := g.edges[xid][yid] return ok } // Edge returns the edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedUndirectedGraph) Edge(u, v graph.Node) graph.Edge { - return g.WeightedEdgeBetween(u, v) +func (g *WeightedUndirectedGraph) Edge(uid, vid int64) graph.Edge { + return g.WeightedEdgeBetween(uid, vid) } // WeightedEdge returns the weighted edge from u to v if such an edge exists and nil otherwise. // The node v must be directly reachable from u as defined by the From method. -func (g *WeightedUndirectedGraph) WeightedEdge(u, v graph.Node) graph.WeightedEdge { - return g.WeightedEdgeBetween(u, v) +func (g *WeightedUndirectedGraph) WeightedEdge(uid, vid int64) graph.WeightedEdge { + return g.WeightedEdgeBetween(uid, vid) } // EdgeBetween returns the edge between nodes x and y. -func (g *WeightedUndirectedGraph) EdgeBetween(x, y graph.Node) graph.Edge { - return g.WeightedEdgeBetween(x, y) +func (g *WeightedUndirectedGraph) EdgeBetween(xid, yid int64) graph.Edge { + return g.WeightedEdgeBetween(xid, yid) } // WeightedEdgeBetween returns the weighted edge between nodes x and y. -func (g *WeightedUndirectedGraph) WeightedEdgeBetween(x, y graph.Node) graph.WeightedEdge { - edge, ok := g.edges[x.ID()][y.ID()] +func (g *WeightedUndirectedGraph) WeightedEdgeBetween(xid, yid int64) graph.WeightedEdge { + edge, ok := g.edges[xid][yid] if !ok { return nil } @@ -235,9 +235,7 @@ func (g *WeightedUndirectedGraph) WeightedEdgeBetween(x, y graph.Node) graph.Wei // If x and y are the same node or there is no joining edge between the two nodes the weight // value returned is either the graph's absent or self value. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g *WeightedUndirectedGraph) Weight(x, y graph.Node) (w float64, ok bool) { - xid := x.ID() - yid := y.ID() +func (g *WeightedUndirectedGraph) Weight(xid, yid int64) (w float64, ok bool) { if xid == yid { return g.self, true } @@ -250,9 +248,9 @@ func (g *WeightedUndirectedGraph) Weight(x, y graph.Node) (w float64, ok bool) { } // Degree returns the degree of n in g. -func (g *WeightedUndirectedGraph) Degree(n graph.Node) int { - if _, ok := g.nodes[n.ID()]; !ok { +func (g *WeightedUndirectedGraph) Degree(id int64) int { + if _, ok := g.nodes[id]; !ok { return 0 } - return len(g.edges[n.ID()]) + return len(g.edges[id]) } diff --git a/graph/simple/weighted_undirected_test.go b/graph/simple/weighted_undirected_test.go index a885821e..5111fb16 100644 --- a/graph/simple/weighted_undirected_test.go +++ b/graph/simple/weighted_undirected_test.go @@ -39,7 +39,7 @@ func TestWeightedMaxID(t *testing.T) { delete(nodes, Node(2)) n := g.NewNode() g.AddNode(n) - if !g.Has(n) { + if !g.Has(n.ID()) { t.Error("added node does not exist in graph") } if _, exists := nodes[n]; exists { diff --git a/graph/topo/2sat_example_test.go b/graph/topo/2sat_example_test.go index 4b89093a..e7fc784b 100644 --- a/graph/topo/2sat_example_test.go +++ b/graph/topo/2sat_example_test.go @@ -123,7 +123,7 @@ func twoSat(r io.Reader) (state map[string]bool, ok bool) { // Check for tautology. if variables[0].negated().ID() == variables[1].ID() { for _, v := range variables { - if !g.Has(v) { + if !g.Has(v.ID()) { g.AddNode(v) } } diff --git a/graph/topo/bron_kerbosch.go b/graph/topo/bron_kerbosch.go index ced15f15..c602b13a 100644 --- a/graph/topo/bron_kerbosch.go +++ b/graph/topo/bron_kerbosch.go @@ -60,9 +60,10 @@ func degeneracyOrdering(g graph.Undirected) (l []graph.Node, s []int) { neighbours = make(map[int64][]graph.Node) ) for _, n := range nodes { - adj := g.From(n) - neighbours[n.ID()] = adj - dv[n.ID()] = len(adj) + id := n.ID() + adj := g.From(id) + neighbours[id] = adj + dv[id] = len(adj) if len(adj) > maxDegree { maxDegree = len(adj) } @@ -146,7 +147,7 @@ func BronKerbosch(g graph.Undirected) [][]graph.Node { order, _ := degeneracyOrdering(g) ordered.Reverse(order) for _, v := range order { - neighbours := g.From(v) + neighbours := g.From(v.ID()) nv := make(set.Nodes, len(neighbours)) for _, n := range neighbours { nv.Add(n) @@ -175,7 +176,8 @@ func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p if nu.Has(v) { continue } - neighbours := g.From(v) + vid := v.ID() + neighbours := g.From(vid) nv := make(set.Nodes, len(neighbours)) for _, n := range neighbours { nv.Add(n) @@ -183,7 +185,7 @@ func (bk *bronKerbosch) maximalCliquePivot(g graph.Undirected, r []graph.Node, p var found bool for _, n := range r { - if n.ID() == v.ID() { + if n.ID() == vid { found = true break } @@ -205,10 +207,10 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb // compile time option. if !tomitaTanakaTakahashi { for _, n := range p { - return g.From(n) + return g.From(n.ID()) } for _, n := range x { - return g.From(n) + return g.From(n.ID()) } panic("bronKerbosch: empty set") } @@ -220,7 +222,7 @@ func (*bronKerbosch) choosePivotFrom(g graph.Undirected, p, x set.Nodes) (neighb maxNeighbors := func(s set.Nodes) { outer: for _, u := range s { - nb := g.From(u) + nb := g.From(u.ID()) c := len(nb) if c <= max { continue diff --git a/graph/topo/bron_kerbosch_test.go b/graph/topo/bron_kerbosch_test.go index e04e06dc..93d50167 100644 --- a/graph/topo/bron_kerbosch_test.go +++ b/graph/topo/bron_kerbosch_test.go @@ -53,7 +53,7 @@ func TestDegeneracyOrdering(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -93,7 +93,7 @@ func TestKCore(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -175,7 +175,7 @@ func TestBronKerbosch(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/topo/clique_graph_test.go b/graph/topo/clique_graph_test.go index 4ae692ca..1f2cd608 100644 --- a/graph/topo/clique_graph_test.go +++ b/graph/topo/clique_graph_test.go @@ -92,7 +92,7 @@ func TestCliqueGraph(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/topo/johnson_cycles.go b/graph/topo/johnson_cycles.go index 60390fb7..8122a57e 100644 --- a/graph/topo/johnson_cycles.go +++ b/graph/topo/johnson_cycles.go @@ -142,14 +142,15 @@ func johnsonGraphFrom(g graph.Directed) johnsonGraph { succ: make(map[int64]set.Int64s), } for i, u := range nodes { - c.index[u.ID()] = i - for _, v := range g.From(u) { - if c.succ[u.ID()] == nil { - c.succ[u.ID()] = make(set.Int64s) - c.nodes.Add(u.ID()) + uid := u.ID() + c.index[uid] = i + for _, v := range g.From(uid) { + if c.succ[uid] == nil { + c.succ[uid] = make(set.Int64s) + c.nodes.Add(uid) } c.nodes.Add(v.ID()) - c.succ[u.ID()].Add(v.ID()) + c.succ[uid].Add(v.ID()) } } return c @@ -247,31 +248,31 @@ func (g johnsonGraph) Nodes() []graph.Node { } // Successors is required to satisfy Tarjan. -func (g johnsonGraph) From(n graph.Node) []graph.Node { - adj := g.succ[n.ID()] +func (g johnsonGraph) From(id int64) []graph.Node { + adj := g.succ[id] if len(adj) == 0 { return nil } succ := make([]graph.Node, 0, len(adj)) - for n := range adj { - succ = append(succ, johnsonGraphNode(n)) + for id := range adj { + succ = append(succ, johnsonGraphNode(id)) } return succ } -func (johnsonGraph) Has(graph.Node) bool { +func (johnsonGraph) Has(int64) bool { panic("topo: unintended use of johnsonGraph") } -func (johnsonGraph) HasEdgeBetween(_, _ graph.Node) bool { +func (johnsonGraph) HasEdgeBetween(_, _ int64) bool { panic("topo: unintended use of johnsonGraph") } -func (johnsonGraph) Edge(_, _ graph.Node) graph.Edge { +func (johnsonGraph) Edge(_, _ int64) graph.Edge { panic("topo: unintended use of johnsonGraph") } -func (johnsonGraph) HasEdgeFromTo(_, _ graph.Node) bool { +func (johnsonGraph) HasEdgeFromTo(_, _ int64) bool { panic("topo: unintended use of johnsonGraph") } -func (johnsonGraph) To(graph.Node) []graph.Node { +func (johnsonGraph) To(int64) []graph.Node { panic("topo: unintended use of johnsonGraph") } diff --git a/graph/topo/johnson_cycles_test.go b/graph/topo/johnson_cycles_test.go index 31c296fb..2f47d3a1 100644 --- a/graph/topo/johnson_cycles_test.go +++ b/graph/topo/johnson_cycles_test.go @@ -88,7 +88,7 @@ func TestDirectedCyclesIn(t *testing.T) { g.AddNode(simple.Node(-10)) // Make sure we test graphs with sparse IDs. for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/topo/paton_cycles.go b/graph/topo/paton_cycles.go index 3c1c2a8c..5d4d0751 100644 --- a/graph/topo/paton_cycles.go +++ b/graph/topo/paton_cycles.go @@ -35,7 +35,7 @@ func UndirectedCyclesIn(g graph.Undirected) [][]graph.Node { u := tree.Pop() uid := u.ID() adj := from[uid] - for _, v := range g.From(u) { + for _, v := range g.From(uid) { vid := v.ID() switch { case uid == vid: diff --git a/graph/topo/paton_cycles_test.go b/graph/topo/paton_cycles_test.go index 2afe2a24..620558ed 100644 --- a/graph/topo/paton_cycles_test.go +++ b/graph/topo/paton_cycles_test.go @@ -77,7 +77,7 @@ func TestUndirectedCyclesIn(t *testing.T) { g.AddNode(simple.Node(-10)) // Make sure we test graphs with sparse IDs. for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/topo/tarjan.go b/graph/topo/tarjan.go index 99f9ec2e..28f50e24 100644 --- a/graph/topo/tarjan.go +++ b/graph/topo/tarjan.go @@ -95,15 +95,15 @@ func TarjanSCC(g graph.Directed) [][]graph.Node { func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.Node { nodes := g.Nodes() - var succ func(graph.Node) []graph.Node + var succ func(id int64) []graph.Node if order == nil { succ = g.From } else { order(nodes) ordered.Reverse(nodes) - succ = func(n graph.Node) []graph.Node { - to := g.From(n) + succ = func(id int64) []graph.Node { + to := g.From(id) order(to) ordered.Reverse(to) return to @@ -131,7 +131,7 @@ func tarjanSCCstabilized(g graph.Directed, order func([]graph.Node)) [][]graph.N // http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm?oldid=642744644 // type tarjan struct { - succ func(graph.Node) []graph.Node + succ func(id int64) []graph.Node index int indexTable map[int64]int @@ -156,7 +156,7 @@ func (t *tarjan) strongconnect(v graph.Node) { t.onStack.Add(vID) // Consider successors of v. - for _, w := range t.succ(v) { + for _, w := range t.succ(vID) { wID := w.ID() if t.indexTable[wID] == 0 { // Successor w has not yet been visited; recur on it. diff --git a/graph/topo/tarjan_test.go b/graph/topo/tarjan_test.go index 9ddb8b25..01a08363 100644 --- a/graph/topo/tarjan_test.go +++ b/graph/topo/tarjan_test.go @@ -132,7 +132,7 @@ func TestSort(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -163,7 +163,7 @@ func TestTarjanSCC(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -290,7 +290,7 @@ func TestSortStabilized(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { diff --git a/graph/topo/topo.go b/graph/topo/topo.go index 335f7b9a..80945cf6 100644 --- a/graph/topo/topo.go +++ b/graph/topo/topo.go @@ -18,9 +18,9 @@ func IsPathIn(g graph.Graph, path []graph.Node) bool { case 0: return true case 1: - return g.Has(path[0]) + return g.Has(path[0].ID()) default: - var canReach func(u, v graph.Node) bool + var canReach func(uid, vid int64) bool switch g := g.(type) { case graph.Directed: canReach = g.HasEdgeFromTo @@ -29,7 +29,7 @@ func IsPathIn(g graph.Graph, path []graph.Node) bool { } for i, u := range path[:len(path)-1] { - if !canReach(u, path[i+1]) { + if !canReach(u.ID(), path[i+1].ID()) { return false } } diff --git a/graph/topo/topo_test.go b/graph/topo/topo_test.go index bd43bcff..0f32dad6 100644 --- a/graph/topo/topo_test.go +++ b/graph/topo/topo_test.go @@ -71,11 +71,11 @@ func TestPathExistsInUndirected(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { - if !g.Has(simple.Node(v)) { + if !g.Has(int64(v)) { g.AddNode(simple.Node(v)) } g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) @@ -110,11 +110,11 @@ func TestPathExistsInDirected(t *testing.T) { g := simple.NewDirectedGraph() for u, e := range test.g { - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { - if !g.Has(simple.Node(v)) { + if !g.Has(int64(v)) { g.AddNode(simple.Node(v)) } g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) @@ -147,11 +147,11 @@ func TestConnectedComponents(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { - if !g.Has(simple.Node(v)) { + if !g.Has(int64(v)) { g.AddNode(simple.Node(v)) } g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) diff --git a/graph/traverse/traverse.go b/graph/traverse/traverse.go index ffdcbb81..b26a10ad 100644 --- a/graph/traverse/traverse.go +++ b/graph/traverse/traverse.go @@ -40,17 +40,19 @@ func (b *BreadthFirst) Walk(g graph.Graph, from graph.Node, until func(n graph.N if until != nil && until(t, depth) { return t } - for _, n := range g.From(t) { - if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(t, n)) { + tid := t.ID() + for _, n := range g.From(tid) { + nid := n.ID() + if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(tid, nid)) { continue } - if b.visited.Has(n.ID()) { + if b.visited.Has(nid) { continue } if b.Visit != nil { b.Visit(t, n) } - b.visited.Add(n.ID()) + b.visited.Add(nid) children++ b.queue.Enqueue(n) } @@ -125,17 +127,19 @@ func (d *DepthFirst) Walk(g graph.Graph, from graph.Node, until func(graph.Node) if until != nil && until(t) { return t } - for _, n := range g.From(t) { - if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(t, n)) { + tid := t.ID() + for _, n := range g.From(tid) { + nid := n.ID() + if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(tid, nid)) { continue } - if d.visited.Has(n.ID()) { + if d.visited.Has(nid) { continue } if d.Visit != nil { d.Visit(t, n) } - d.visited.Add(n.ID()) + d.visited.Add(nid) d.stack.Push(n) } } diff --git a/graph/traverse/traverse_test.go b/graph/traverse/traverse_test.go index 8a5819a1..cc2cd652 100644 --- a/graph/traverse/traverse_test.go +++ b/graph/traverse/traverse_test.go @@ -136,7 +136,7 @@ func TestBreadthFirst(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -224,7 +224,7 @@ func TestDepthFirst(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { // Add nodes that are not defined by an edge. - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { @@ -285,11 +285,11 @@ func TestWalkAll(t *testing.T) { g := simple.NewUndirectedGraph() for u, e := range test.g { - if !g.Has(simple.Node(u)) { + if !g.Has(int64(u)) { g.AddNode(simple.Node(u)) } for v := range e { - if !g.Has(simple.Node(v)) { + if !g.Has(int64(v)) { g.AddNode(simple.Node(v)) } g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) diff --git a/graph/undirect.go b/graph/undirect.go index 97d47a06..49ba62bd 100644 --- a/graph/undirect.go +++ b/graph/undirect.go @@ -12,20 +12,20 @@ type Undirect struct { var _ Undirected = Undirect{} // Has returns whether the node exists within the graph. -func (g Undirect) Has(n Node) bool { return g.G.Has(n) } +func (g Undirect) Has(id int64) bool { return g.G.Has(id) } // Nodes returns all the nodes in the graph. func (g Undirect) Nodes() []Node { return g.G.Nodes() } // From returns all nodes in g that can be reached directly from u. -func (g Undirect) From(u Node) []Node { +func (g Undirect) From(uid int64) []Node { var nodes []Node seen := make(map[int64]struct{}) - for _, n := range g.G.From(u) { + for _, n := range g.G.From(uid) { seen[n.ID()] = struct{}{} nodes = append(nodes, n) } - for _, n := range g.G.To(u) { + for _, n := range g.G.To(uid) { id := n.ID() if _, ok := seen[id]; ok { continue @@ -37,21 +37,21 @@ func (g Undirect) From(u Node) []Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g Undirect) HasEdgeBetween(x, y Node) bool { return g.G.HasEdgeBetween(x, y) } +func (g Undirect) HasEdgeBetween(xid, yid int64) bool { return g.G.HasEdgeBetween(xid, yid) } // Edge returns the edge 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. // If an edge exists, the Edge returned is an EdgePair. The weight of // the edge is determined by applying the Merge func to the weights of the // edges between u and v. -func (g Undirect) Edge(u, v Node) Edge { return g.EdgeBetween(u, v) } +func (g Undirect) Edge(uid, vid int64) Edge { return g.EdgeBetween(uid, vid) } // EdgeBetween returns the edge between nodes x and y. If an edge exists, the // Edge returned is an EdgePair. The weight of the edge is determined by // applying the Merge func to the weights of edges between x and y. -func (g Undirect) EdgeBetween(x, y Node) Edge { - fe := g.G.Edge(x, y) - re := g.G.Edge(y, x) +func (g Undirect) EdgeBetween(xid, yid int64) Edge { + fe := g.G.Edge(xid, yid) + re := g.G.Edge(yid, xid) if fe == nil && re == nil { return nil } @@ -91,20 +91,20 @@ var ( ) // Has returns whether the node exists within the graph. -func (g UndirectWeighted) Has(n Node) bool { return g.G.Has(n) } +func (g UndirectWeighted) Has(id int64) bool { return g.G.Has(id) } // Nodes returns all the nodes in the graph. func (g UndirectWeighted) Nodes() []Node { return g.G.Nodes() } // From returns all nodes in g that can be reached directly from u. -func (g UndirectWeighted) From(u Node) []Node { +func (g UndirectWeighted) From(uid int64) []Node { var nodes []Node seen := make(map[int64]struct{}) - for _, n := range g.G.From(u) { + for _, n := range g.G.From(uid) { seen[n.ID()] = struct{}{} nodes = append(nodes, n) } - for _, n := range g.G.To(u) { + for _, n := range g.G.To(uid) { id := n.ID() if _, ok := seen[id]; ok { continue @@ -116,44 +116,46 @@ func (g UndirectWeighted) From(u Node) []Node { } // HasEdgeBetween returns whether an edge exists between nodes x and y. -func (g UndirectWeighted) HasEdgeBetween(x, y Node) bool { return g.G.HasEdgeBetween(x, y) } +func (g UndirectWeighted) HasEdgeBetween(xid, yid int64) bool { return g.G.HasEdgeBetween(xid, yid) } // Edge returns the edge 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. // If an edge exists, the Edge returned is an EdgePair. The weight of // the edge is determined by applying the Merge func to the weights of the // edges between u and v. -func (g UndirectWeighted) Edge(u, v Node) Edge { return g.WeightedEdgeBetween(u, v) } +func (g UndirectWeighted) Edge(uid, vid int64) Edge { return g.WeightedEdgeBetween(uid, vid) } // WeightedEdge returns the weighted edge 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. // If an edge exists, the Edge returned is an EdgePair. The weight of // the edge is determined by applying the Merge func to the weights of the // edges between u and v. -func (g UndirectWeighted) WeightedEdge(u, v Node) WeightedEdge { return g.WeightedEdgeBetween(u, v) } +func (g UndirectWeighted) WeightedEdge(uid, vid int64) WeightedEdge { + return g.WeightedEdgeBetween(uid, vid) +} // EdgeBetween returns the edge between nodes x and y. If an edge exists, the // Edge returned is an EdgePair. The weight of the edge is determined by // applying the Merge func to the weights of edges between x and y. -func (g UndirectWeighted) EdgeBetween(x, y Node) Edge { - return g.WeightedEdgeBetween(x, y) +func (g UndirectWeighted) EdgeBetween(xid, yid int64) Edge { + return g.WeightedEdgeBetween(xid, yid) } // WeightedEdgeBetween returns the weighted edge between nodes x and y. If an edge exists, the // Edge returned is an EdgePair. The weight of the edge is determined by // applying the Merge func to the weights of edges between x and y. -func (g UndirectWeighted) WeightedEdgeBetween(x, y Node) WeightedEdge { - fe := g.G.Edge(x, y) - re := g.G.Edge(y, x) +func (g UndirectWeighted) WeightedEdgeBetween(xid, yid int64) WeightedEdge { + fe := g.G.Edge(xid, yid) + re := g.G.Edge(yid, xid) if fe == nil && re == nil { return nil } - f, ok := g.G.Weight(x, y) + f, ok := g.G.Weight(xid, yid) if !ok { f = g.Absent } - r, ok := g.G.Weight(y, x) + r, ok := g.G.Weight(yid, xid) if !ok { r = g.Absent } @@ -171,15 +173,15 @@ func (g UndirectWeighted) WeightedEdgeBetween(x, y Node) WeightedEdge { // If x and y are the same node the internal node weight is returned. If there is no joining // edge between the two nodes the weight value returned is zero. Weight returns true if an edge // exists between x and y or if x and y have the same ID, false otherwise. -func (g UndirectWeighted) Weight(x, y Node) (w float64, ok bool) { - fe := g.G.Edge(x, y) - re := g.G.Edge(y, x) +func (g UndirectWeighted) Weight(xid, yid int64) (w float64, ok bool) { + fe := g.G.Edge(xid, yid) + re := g.G.Edge(yid, xid) - f, fOk := g.G.Weight(x, y) + f, fOk := g.G.Weight(xid, yid) if !fOk { f = g.Absent } - r, rOK := g.G.Weight(y, x) + r, rOK := g.G.Weight(yid, xid) if !rOK { r = g.Absent } diff --git a/graph/undirect_test.go b/graph/undirect_test.go index 462de2e7..5304e834 100644 --- a/graph/undirect_test.go +++ b/graph/undirect_test.go @@ -123,8 +123,8 @@ func TestUndirect(t *testing.T) { src := graph.Undirect{G: g} dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0) for _, u := range src.Nodes() { - for _, v := range src.From(u) { - dst.SetEdge(src.Edge(u, v)) + for _, v := range src.From(u.ID()) { + dst.SetEdge(src.Edge(u.ID(), v.ID())) } } @@ -148,8 +148,8 @@ func TestUndirectWeighted(t *testing.T) { src := graph.UndirectWeighted{G: g, Absent: test.absent, Merge: test.merge} dst := simple.NewUndirectedMatrixFrom(src.Nodes(), 0, 0, 0) for _, u := range src.Nodes() { - for _, v := range src.From(u) { - dst.SetWeightedEdge(src.WeightedEdge(u, v)) + for _, v := range src.From(u.ID()) { + dst.SetWeightedEdge(src.WeightedEdge(u.ID(), v.ID())) } }