github.com/decred/dcrlnd@v0.7.6/autopilot/top_centrality.go (about) 1 package autopilot 2 3 import ( 4 "runtime" 5 6 "github.com/decred/dcrd/dcrutil/v4" 7 ) 8 9 // TopCentrality is a simple greedy technique to create connections to nodes 10 // with the top betweenness centrality value. This algorithm is usually 11 // referred to as TopK in the literature. The idea is that by opening channels 12 // to nodes with top betweenness centrality we also increase our own betweenness 13 // centrality (given we already have at least one channel, or create at least 14 // two new channels). 15 // A different and much better approach is instead of selecting nodes with top 16 // centrality value, we extend the graph in a loop by inserting a new non 17 // existing edge and recalculate the betweenness centrality of each node. This 18 // technique is usually referred to as "greedy" algorithm and gives better 19 // results than TopK but is considerably slower too. 20 type TopCentrality struct { 21 centralityMetric *BetweennessCentrality 22 } 23 24 // A compile time assertion to ensure TopCentrality meets the 25 // AttachmentHeuristic interface. 26 var _ AttachmentHeuristic = (*TopCentrality)(nil) 27 28 // NewTopCentrality constructs and returns a new TopCentrality heuristic. 29 func NewTopCentrality() *TopCentrality { 30 metric, err := NewBetweennessCentralityMetric( 31 runtime.NumCPU(), 32 ) 33 if err != nil { 34 panic(err) 35 } 36 37 return &TopCentrality{ 38 centralityMetric: metric, 39 } 40 } 41 42 // Name returns the name of the heuristic. 43 func (g *TopCentrality) Name() string { 44 return "top_centrality" 45 } 46 47 // NodeScores will return a [0,1] normalized map of scores for the given nodes 48 // except for the ones we already have channels with. The scores will simply 49 // be the betweenness centrality values of the nodes. 50 // As our current implementation of betweenness centrality is non-incremental, 51 // NodeScores will recalculate the centrality values on every call, which is 52 // slow for large graphs. 53 func (g *TopCentrality) NodeScores(graph ChannelGraph, chans []LocalChannel, 54 chanSize dcrutil.Amount, nodes map[NodeID]struct{}) ( 55 map[NodeID]*NodeScore, error) { 56 57 // Calculate betweenness centrality for the whole graph. 58 if err := g.centralityMetric.Refresh(graph); err != nil { 59 return nil, err 60 } 61 62 normalize := true 63 centrality := g.centralityMetric.GetMetric(normalize) 64 65 // Create a map of the existing peers for faster filtering. 66 existingPeers := make(map[NodeID]struct{}) 67 for _, c := range chans { 68 existingPeers[c.Node] = struct{}{} 69 } 70 71 result := make(map[NodeID]*NodeScore, len(nodes)) 72 for nodeID := range nodes { 73 // Skip nodes we already have channel with. 74 if _, ok := existingPeers[nodeID]; ok { 75 continue 76 } 77 78 // Skip passed nodes not in the graph. This could happen if 79 // the graph changed before computing the centrality values as 80 // the nodes we iterate are prefiltered by the autopilot agent. 81 score, ok := centrality[nodeID] 82 if !ok { 83 continue 84 } 85 86 result[nodeID] = &NodeScore{ 87 NodeID: nodeID, 88 Score: score, 89 } 90 } 91 92 return result, nil 93 }