From 592ce92c72b2db2256a79f9ccba3a3aec5aab44c Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 9 Jan 2019 09:14:45 +0100 Subject: [PATCH] autopilot/interface+prefattach: scale node scores to range [0.0, 1.0] To prepare for combinning scores from multiple heuristics, we require the scores returned from the NodeSores API to be in the range [0.0, 1.0]. The prefAttach heuristic is altered to scale the returned scores such that the most connected node in the grpah is given a score of 1.0. --- autopilot/interface.go | 11 ++++++----- autopilot/prefattach.go | 26 ++++++++++++++++---------- autopilot/prefattach_test.go | 4 ++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/autopilot/interface.go b/autopilot/interface.go index 51405e7fc..ec80d6a25 100644 --- a/autopilot/interface.go +++ b/autopilot/interface.go @@ -125,11 +125,12 @@ type AttachmentHeuristic interface { // returned channel candidates maps the NodeID to a NodeScore for the // node. // - // The scores will be in the range [0, M], where 0 indicates no - // improvement in connectivity if a channel is opened to this node, - // while M is the maximum possible improvement in connectivity. The - // size of M is up to the implementation of this interface, so scores - // must be normalized if compared against other implementations. + // The returned scores will be in the range [0, 1.0], where 0 indicates + // no improvement in connectivity if a channel is opened to this node, + // while 1.0 is the maximum possible improvement in connectivity. The + // implementation of this interface must return scores in this range to + // properly allow the autopilot agent to make a reasonable choice based + // on the score from multiple heuristics. // // NOTE: A NodeID not found in the returned map is implicitly given a // score of 0. diff --git a/autopilot/prefattach.go b/autopilot/prefattach.go index af5f45695..bb010239a 100644 --- a/autopilot/prefattach.go +++ b/autopilot/prefattach.go @@ -43,9 +43,10 @@ func NewNodeID(pub *btcec.PublicKey) NodeID { return n } -// NodeScores is a method that given the current channel graph and -// current set of local channels, scores the given nodes according to -// the preference of opening a channel of the given size with them. +// NodeScores is a method that given the current channel graph and current set +// of local channels, scores the given nodes according to the preference of +// opening a channel of the given size with them. The returned channel +// candidates maps the NodeID to a NodeScore for the node. // // The heuristic employed by this method is one that attempts to promote a // scale-free network globally, via local attachment preferences for new nodes @@ -64,21 +65,25 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []Channel, chanSize btcutil.Amount, nodes map[NodeID]struct{}) ( map[NodeID]*NodeScore, error) { - // Count the number of channels in the graph. We'll also count the - // number of channels as we go for the nodes we are interested in. - var graphChans int + // Count the number of channels for each particular node in the graph. + var maxChans int nodeChanNum := make(map[NodeID]int) if err := g.ForEachNode(func(n Node) error { var nodeChans int err := n.ForEachChannel(func(_ ChannelEdge) error { nodeChans++ - graphChans++ return nil }) if err != nil { return err } + // We keep track of the highest-degree node we've seen, as this + // will be given the max score. + if nodeChans > maxChans { + maxChans = nodeChans + } + // If this node is not among our nodes to score, we can return // early. nID := NodeID(n.PubKey()) @@ -97,7 +102,7 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []Channel, // If there are no channels in the graph we cannot determine any // preferences, so we return, indicating all candidates get a score of // zero. - if graphChans == 0 { + if maxChans == 0 { return nil, nil } @@ -127,8 +132,9 @@ func (p *PrefAttachment) NodeScores(g ChannelGraph, chans []Channel, } // Otherwise we score the node according to its fraction of - // channels in the graph. - score := float64(nodeChans) / float64(graphChans) + // channels in the graph, scaled such that the highest-degree + // node will be given a score of 1.0. + score := float64(nodeChans) / float64(maxChans) candidates[nID] = &NodeScore{ NodeID: nID, Score: score, diff --git a/autopilot/prefattach_test.go b/autopilot/prefattach_test.go index b34910417..f987d70eb 100644 --- a/autopilot/prefattach_test.go +++ b/autopilot/prefattach_test.go @@ -249,8 +249,8 @@ func TestPrefAttachmentSelectTwoVertexes(t *testing.T) { // Since each of the nodes has 1 channel, out // of only one channel in the graph, we expect - // their score to be 0.5. - expScore := float64(0.5) + // their score to be 1.0. + expScore := float64(1.0) if candidate.Score != expScore { t1.Fatalf("expected candidate score "+ "to be %v, instead was %v",