Skip to content

Commit

Permalink
fix neg link bug and add visuals
Browse files Browse the repository at this point in the history
  • Loading branch information
balasan committed Apr 9, 2021
1 parent 7f7c921 commit 33e8029
Show file tree
Hide file tree
Showing 11 changed files with 545 additions and 153 deletions.
92 changes: 46 additions & 46 deletions detrep/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ type Node struct {

// Graph holds node and edge data.
type Graph struct {
nodes map[string]*Node
negNodes map[string]*Node
edges map[string](map[string]sdk.Uint)
params RankParams
negConsumer Node
Nodes map[string]*Node
NegNodes map[string]*Node
Edges map[string](map[string]sdk.Uint)
Params RankParams
NegConsumer Node
Precision sdk.Uint
MaxNegOffset sdk.Uint
}
Expand All @@ -65,21 +65,21 @@ type Graph struct {
// personalization is the personalization vector (can be nil for non-personalized pr)
type RankParams struct {
α, ε sdk.Uint
personalization []string
Personalization []string
}

// NewGraph initializes and returns a new graph.
func NewGraph(α sdk.Uint, ε sdk.Uint, negConsumerRank sdk.Uint) *Graph {
return &Graph{
nodes: make(map[string]*Node),
negNodes: make(map[string]*Node),
edges: make(map[string](map[string]sdk.Uint)),
params: RankParams{
Nodes: make(map[string]*Node),
NegNodes: make(map[string]*Node),
Edges: make(map[string](map[string]sdk.Uint)),
Params: RankParams{
α: α,
ε: ε,
personalization: make([]string, 0),
Personalization: make([]string, 0),
},
negConsumer: Node{ID: "negConsumer", PRank: negConsumerRank, NRank: sdk.ZeroUint()},
NegConsumer: Node{ID: "negConsumer", PRank: negConsumerRank, NRank: sdk.ZeroUint()},
Precision: sdk.NewUintFromBigInt(sdk.NewIntWithDecimal(1, Decimals).BigInt()),
MaxNegOffset: sdk.NewUintFromBigInt(sdk.NewIntWithDecimal(MaxNegOffset, Decimals).BigInt()),
}
Expand All @@ -94,7 +94,7 @@ func NewNode(id string, pRank sdk.Uint, nRank sdk.Uint) Node {
// these nodes will have high rank by default and all other rank will stem from them
// this makes non-personalaziation nodes sybil resistant (they cannot increase their own rank)
func (graph *Graph) AddPersonalizationNode(pNode Node) {
graph.params.personalization = append(graph.params.personalization, pNode.ID)
graph.Params.Personalization = append(graph.Params.Personalization, pNode.ID)
// this to ensures source nodes exist
graph.InitPosNode(pNode)
}
Expand Down Expand Up @@ -131,15 +131,15 @@ func (graph *Graph) Link(source, target Node, weight sdk.Int) {

sourceNode.degree = sourceNode.degree.Add(weightUint)

if _, ok := graph.edges[sourceKey]; ok == false {
graph.edges[sourceKey] = map[string]sdk.Uint{}
if _, ok := graph.Edges[sourceKey]; ok == false {
graph.Edges[sourceKey] = map[string]sdk.Uint{}
}

if _, ok := graph.edges[sourceKey][targetKey]; ok == false {
graph.edges[sourceKey][targetKey] = sdk.ZeroUint()
if _, ok := graph.Edges[sourceKey][targetKey]; ok == false {
graph.Edges[sourceKey][targetKey] = sdk.ZeroUint()
}

graph.edges[sourceKey][targetKey] = graph.edges[sourceKey][targetKey].Add(weightUint)
graph.Edges[sourceKey][targetKey] = graph.Edges[sourceKey][targetKey].Add(weightUint)

// note: use target.id here to make sure we reference the original id
graph.cancelOpposites(*sourceNode, target.ID, nodeType)
Expand All @@ -154,22 +154,22 @@ func (graph *Graph) Finalize() {
// if they have a negative counterpart
// this reduces the weight of the outgoing links from low-ranking nodes
func (graph *Graph) processNegatives() {
negConsumerInput := graph.negConsumer
negConsumerInput := graph.NegConsumer

for _, negNode := range graph.negNodes {
for _, negNode := range graph.NegNodes {
// positive node doesn't exist
if _, ok := graph.nodes[negNode.ID]; ok == false {
return
if _, ok := graph.Nodes[negNode.ID]; ok == false {
continue
}

// node has no outgpoing links
if graph.nodes[negNode.ID].degree.IsZero() {
return
if graph.Nodes[negNode.ID].degree.IsZero() {
continue
}

posNode := graph.nodes[negNode.ID]
posNode := graph.Nodes[negNode.ID]
if posNode.PRank.IsZero() || negNode.PRank.IsZero() {
return
continue
}
negConsumer := graph.initNode(negConsumerInput.ID, negConsumerInput, Positive)

Expand All @@ -189,19 +189,19 @@ func (graph *Graph) processNegatives() {
negMultiple = one.Mul(graph.Precision).Quo(denom).Sub(one)
}
// cap the vote decrease at 10x
negWeight := negMultiple.Mul(graph.nodes[negNode.ID].degree).Quo(graph.Precision)
negWeight := negMultiple.Mul(graph.Nodes[negNode.ID].degree).Quo(graph.Precision)

// this should actually never happen if degree is > 0
if _, ok := graph.edges[negNode.ID]; ok == false {
graph.edges[negNode.ID] = map[string]sdk.Uint{}
if _, ok := graph.Edges[negNode.ID]; ok == false {
graph.Edges[negNode.ID] = map[string]sdk.Uint{}
}

if _, ok := graph.edges[negNode.ID][negConsumer.ID]; ok == false {
graph.edges[negNode.ID][negConsumer.ID] = sdk.ZeroUint()
if _, ok := graph.Edges[negNode.ID][negConsumer.ID]; ok == false {
graph.Edges[negNode.ID][negConsumer.ID] = sdk.ZeroUint()
}

graph.edges[negNode.ID][negConsumer.ID] = graph.edges[negNode.ID][negConsumer.ID].Add(negWeight)
graph.nodes[negNode.ID].degree = graph.nodes[negNode.ID].degree.Add(negWeight)
graph.Edges[negNode.ID][negConsumer.ID] = graph.Edges[negNode.ID][negConsumer.ID].Add(negWeight)
graph.Nodes[negNode.ID].degree = graph.Nodes[negNode.ID].degree.Add(negWeight)
}
}

Expand All @@ -213,23 +213,23 @@ func (graph *Graph) cancelOpposites(sourceNode Node, target string, nodeType Nod
oppositeKey = getKey(target, Negative)
}

if _, ok := graph.edges[sourceNode.ID][oppositeKey]; ok == false {
if _, ok := graph.Edges[sourceNode.ID][oppositeKey]; ok == false {
return
}

edge := graph.edges[sourceNode.ID][key]
opositeEdge := graph.edges[sourceNode.ID][oppositeKey]
edge := graph.Edges[sourceNode.ID][key]
opositeEdge := graph.Edges[sourceNode.ID][oppositeKey]

switch {
case opositeEdge.GT(edge):
graph.removeEdge(sourceNode.ID, key)
graph.edges[sourceNode.ID][oppositeKey] = opositeEdge.Sub(edge)
graph.Edges[sourceNode.ID][oppositeKey] = opositeEdge.Sub(edge)
// remove degree from both delete node and the adjustment
sourceNode.degree = sourceNode.degree.Sub(edge.Mul(sdk.NewUint(2)))

case edge.GT(opositeEdge):
graph.removeEdge(sourceNode.ID, oppositeKey)
graph.edges[sourceNode.ID][key] = edge.Sub(opositeEdge)
graph.Edges[sourceNode.ID][key] = edge.Sub(opositeEdge)
// remove degree from both delete node and the adjustment
sourceNode.degree = sourceNode.degree.Sub(opositeEdge.Mul(sdk.NewUint(2)))

Expand All @@ -248,8 +248,8 @@ func (graph *Graph) InitPosNode(inputNode Node) *Node {

// initNode initialized a node
func (graph *Graph) initNode(key string, inputNode Node, nodeType NodeType) *Node {
if _, ok := graph.nodes[key]; ok == false {
graph.nodes[key] = &Node{
if _, ok := graph.Nodes[key]; ok == false {
graph.Nodes[key] = &Node{
ID: inputNode.ID, // id is independent of pos/neg keys
degree: sdk.ZeroUint(),
PRank: sdk.ZeroUint(),
Expand All @@ -258,23 +258,23 @@ func (graph *Graph) initNode(key string, inputNode Node, nodeType NodeType) *Nod
}
// store negative nodes so we can easily merge them later
if nodeType == Negative {
graph.negNodes[key] = graph.nodes[key]
graph.NegNodes[key] = graph.Nodes[key]
}
}
// update rank here in case we initilized with 0 early on
var prevRank sdk.Uint
if prevRank = inputNode.PRank; nodeType == Negative {
prevRank = inputNode.NRank
}
graph.nodes[key].PRank = prevRank
return graph.nodes[key]
graph.Nodes[key].PRank = prevRank
return graph.Nodes[key]
}

// removeEdge removes edge from graph
func (graph *Graph) removeEdge(source string, target string) {
delete(graph.edges[source], target)
if len(graph.edges[source]) == 0 {
delete(graph.edges, source)
delete(graph.Edges[source], target)
if len(graph.Edges[source]) == 0 {
delete(graph.Edges, source)
}
}

Expand Down
52 changes: 26 additions & 26 deletions detrep/pagerank.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ func (graph Graph) Rank(callback func(key string, pRank sdk.Uint, nRank sdk.Uint
one := graph.Precision

Δ := one
N := sdk.NewUint(uint64(len(graph.nodes)))
pVector := graph.params.personalization
ε := graph.params.ε
α := graph.params.α
N := sdk.NewUint(uint64(len(graph.Nodes)))
pVector := graph.Params.Personalization
ε := graph.Params.ε
α := graph.Params.α

personalized := len(pVector) > 0

Expand All @@ -27,10 +27,10 @@ func (graph Graph) Rank(callback func(key string, pRank sdk.Uint, nRank sdk.Uint
pWeights := graph.initPersonalizationNodes()

// Normalize all the edge weights so that their sum amounts to 1.
for source := range graph.nodes {
if graph.nodes[source].degree.GT(sdk.ZeroUint()) {
for target := range graph.edges[source] {
graph.edges[source][target] = graph.edges[source][target].Mul(graph.Precision).Quo(graph.nodes[source].degree)
for source := range graph.Nodes {
if graph.Nodes[source].degree.GT(sdk.ZeroUint()) {
for target := range graph.Edges[source] {
graph.Edges[source][target] = graph.Edges[source][target].Mul(graph.Precision).Quo(graph.Nodes[source].degree)
}
}
}
Expand All @@ -42,40 +42,40 @@ func (graph Graph) Rank(callback func(key string, pRank sdk.Uint, nRank sdk.Uint
danglingWeight := sdk.ZeroUint()
nodes := map[string]sdk.Uint{}

for key, value := range graph.nodes {
for key, value := range graph.Nodes {
nodes[key] = value.PRank

if value.degree.IsZero() {
danglingWeight = danglingWeight.Add(value.PRank)
}

graph.nodes[key].PRank = sdk.ZeroUint()
graph.Nodes[key].PRank = sdk.ZeroUint()
}

danglingWeight = danglingWeight.Mul(α).Quo(graph.Precision)

for source := range graph.nodes {
for target, weight := range graph.edges[source] {
for source := range graph.Nodes {
for target, weight := range graph.Edges[source] {
addWeight := α.Mul(nodes[source]).Quo(graph.Precision).Mul(weight).Quo(graph.Precision)
graph.nodes[target].PRank = graph.nodes[target].PRank.Add(addWeight)
graph.Nodes[target].PRank = graph.Nodes[target].PRank.Add(addWeight)
}

if !personalized {
graph.nodes[source].PRank = graph.nodes[source].PRank.Add(one.Sub(α).Quo(N).Add(danglingWeight.Quo(N)))
graph.Nodes[source].PRank = graph.Nodes[source].PRank.Add(one.Sub(α).Quo(N).Add(danglingWeight.Quo(N)))
}
}

// random jump + dangling weights are transferred to admins
// this makes pagerank sybil resistant
if personalized {
for i, root := range pVector {
graph.nodes[root].PRank = graph.nodes[root].PRank.Add((one.Sub(α).Add(danglingWeight)).Mul(pWeights[i])).Quo(graph.Precision)
graph.Nodes[root].PRank = graph.Nodes[root].PRank.Add((one.Sub(α).Add(danglingWeight)).Mul(pWeights[i])).Quo(graph.Precision)
}
}

Δ = sdk.ZeroUint()

for key, value := range graph.nodes {
for key, value := range graph.Nodes {
var diff sdk.Uint
// if _, ok := nodes[key]; ok == false {
// nodes[key] = sdk.ZeroUint()
Expand All @@ -98,9 +98,9 @@ func (graph Graph) Rank(callback func(key string, pRank sdk.Uint, nRank sdk.Uint
// we initialze the start scores to optimize the computation
func (graph Graph) initScores(N sdk.Uint, pWeights []sdk.Uint) {
// get sum of all node scores
personalization := graph.params.personalization
personalization := graph.Params.Personalization
totalScore := sdk.ZeroUint()
for _, node := range graph.nodes {
for _, node := range graph.Nodes {
totalScore = totalScore.Add(node.PRank)
}

Expand All @@ -112,41 +112,41 @@ func (graph Graph) initScores(N sdk.Uint, pWeights []sdk.Uint) {
// TODO use prev scores for initialization
if len(pWeights) == 0 {
// initialize all nodes if there is no personalizeation vector
for key := range graph.nodes {
graph.nodes[key].PRank = graph.nodes[key].PRank.Add(graph.Precision.Sub(totalScore).Quo(N))
for key := range graph.Nodes {
graph.Nodes[key].PRank = graph.Nodes[key].PRank.Add(graph.Precision.Sub(totalScore).Quo(N))
}
return
}
// initialize personalization vector
for i, root := range personalization {
graph.nodes[root].PRank = graph.nodes[root].PRank.Add(graph.Precision.Sub(totalScore).Mul(graph.Precision).Quo(pWeights[i]))
graph.Nodes[root].PRank = graph.Nodes[root].PRank.Add(graph.Precision.Sub(totalScore).Mul(graph.Precision).Quo(pWeights[i]))
}
}

// compute personalization weights based on degree
// this ensures source nodes will have the same weight
// we also update start scores here
func (graph Graph) initPersonalizationNodes() []sdk.Uint {
pVector := graph.params.personalization
pVector := graph.Params.Personalization
pWeights := make([]sdk.Uint, len(pVector))

pWeightsSum := sdk.ZeroUint()
scoreSum := sdk.ZeroUint()
for i, key := range pVector {
var d sdk.Uint
// root node score and weight should not be 0
if d = graph.Precision; graph.nodes[key].degree.GT(sdk.ZeroUint()) {
d = graph.nodes[key].degree
if d = graph.Precision; graph.Nodes[key].degree.GT(sdk.ZeroUint()) {
d = graph.Nodes[key].degree
}
pWeights[i] = d
pWeightsSum = pWeightsSum.Add(d)
scoreSum = scoreSum.Add(graph.nodes[key].PRank)
scoreSum = scoreSum.Add(graph.Nodes[key].PRank)
}

// normalize personalization weights
for i, key := range pVector {
pWeights[i] = pWeights[i].Mul(graph.Precision).Quo(pWeightsSum)
graph.nodes[key].PRank = scoreSum.Mul(pWeights[i]).Quo(graph.Precision)
graph.Nodes[key].PRank = scoreSum.Mul(pWeights[i]).Quo(graph.Precision)
}

return pWeights
Expand Down
12 changes: 6 additions & 6 deletions detrep/processResults.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@ import (

func (graph Graph) processResults(callback func(id string, pRank sdk.Uint, nRank sdk.Uint)) {
graph.mergeNegatives()
for key, node := range graph.nodes {
for key, node := range graph.Nodes {
callback(key, node.PRank, node.NRank)
}
}

func (graph Graph) mergeNegatives() {
for key, node := range graph.negNodes {
for key, node := range graph.NegNodes {
// create the positive node if it doesn't exist
if _, ok := graph.nodes[node.ID]; ok == false {
graph.nodes[node.ID] = &Node{
if _, ok := graph.Nodes[node.ID]; ok == false {
graph.Nodes[node.ID] = &Node{
ID: key,
PRank: sdk.ZeroUint(),
degree: sdk.ZeroUint(),
nodeType: Positive,
}
}
// write the neg rank value to the positive node
graph.nodes[node.ID].NRank = node.PRank
graph.Nodes[node.ID].NRank = node.PRank
// we don't want negative nodes around after they are merged
delete(graph.nodes, key)
delete(graph.Nodes, key)
}
}
Loading

0 comments on commit 33e8029

Please sign in to comment.