I'm trying out Mathematica for the first time and using TunkRank as my algorithm of choice. Here is what I came up with:
Following = {{2, 3, 4}, {0, 4}, {1, 3}, {1, 4}, {0, 2}}
Followers = {{1, 4}, {2, 3}, {0, 4}, {0, 2}, {0, 1, 3}}
p = 0.05
Influence[x_] := Influence[x] =
Sum[1 + (p * Influence[Followers[[x, i]]])/(1 +
Length[Following[[x]]]), {i, 0, Length[Followers[[x]]]}]
If you run this in Mathematica you will see that it doesn't operate on just the follower nodes . Instead, the recursion is infinite. What am I doing wrong?
For a start, you might want to consider making p
a parameter with a default value (see documentation). Something like Influence[x_,p_?Positive:0.05]:= (* definition *)
.
Second, you are setting the part specification i
to start at 0. In Mathematica, indices start at 1, not 0. You will end up getting the Head
of the object. In this case, Followers[[x,0]]
will return List
. You need to change this and increment your data by 1.
Following = {{3, 4, 5}, {1, 5}, {2, 4}, {2, 5}, {1, 3}};
Followers = {{2, 5}, {3, 4}, {1, 5}, {1, 3}, {1, 2, 4}};
Influence[x_, P_: 0.05] :=
Influence[x] =
Sum[1 + (P*Influence[Followers[[x, i]]])/(1 +
Length[Following[[x]]]), {i, Length[Followers[[x]]]}]
Third, you have some recursiveness in your data. Person 1 is followed by person 2, who is followed by 3 and 4, who are both followed by 1. So of course it is recursive.
follows = Join @@ Thread /@ Thread[Following -> Range@5]
{3 -> 1, 4 -> 1, 5 -> 1, 1 -> 2, 5 -> 2, 2 -> 3, 4 -> 3, 2 -> 4,
5 -> 4, 1 -> 5, 3 -> 5}
GraphPlot[follows, DirectedEdges -> True, VertexLabeling -> True]
You could consider an explicit FixedPoint
type of iteration, using Chop
or the SameTest
option to prevent recursion for ever with small changes. But I doubt even that will avoid the problem with a test data set as cyclical as yours.
EDIT
ok so I worked out the iterative solution. First you need to convert your followers data into an adjacency matrix.
(* Following = {{3, 4, 5}, {1, 5}, {2, 4}, {2, 5}, {1, 3}}; *)
Followers = {{2, 5}, {3, 4}, {1, 5}, {1, 3}, {1, 2, 4}};
adjmatrix = PadRight[SparseArray[List /@ # -> 1] & /@ Followers]
{{0, 1, 0, 0, 1}, {0, 0, 1, 1, 0}, {1, 0, 0, 0, 1}, {1, 0, 1, 0, 0}, {1, 1, 0, 1, 0}}
This gives the bit equivalent to the Length
statements in your version.
vec1 = Table[1, {5}] (* {1, 1, 1, 1, 1} *)
adjmatrix.vec1
vec1.adjmatrix
{2, 2, 2, 2, 3}
{3, 2, 2, 2, 2}
Convergence is fast.
NestList[1 + 0.02 * adjmatrix.#1/(1 + vec1.adjmatrix) &, {1, 1, 1, 1, 1}, 5]
{{1, 1, 1, 1, 1}, {1.01, 1.01333, 1.01333, 1.01333, 1.02}, {1.01017,
1.01351, 1.01353, 1.01349, 1.02024}, {1.01017, 1.01351, 1.01354,
1.01349, 1.02025}, {1.01017, 1.01351, 1.01354, 1.01349,
1.02025}, {1.01017, 1.01351, 1.01354, 1.01349, 1.02025}}
Given the adjacency matrix, you can have a function:
TunkRank[mat_?MatrixQ, p_?Positive] :=
With[{vec = Table[1, {Length[mat]}]},
FixedPoint[1 + p * mat.#1/(1 + vec.mat) &, vec]]
Hope that helps. I assume this is giving the right answers.