big restyle, last fixes before final release

This commit is contained in:
Luca Lombardo
2022-12-23 17:16:33 +01:00
parent 248045eb54
commit 5808251a09
17 changed files with 1412 additions and 566168 deletions
+507
View File
@@ -0,0 +1,507 @@
#!/usr/bin/env python3
# Importing the libraries
import os
import wget
import gzip
import time
import scipy as sp
import numpy as np
import pandas as pd
import networkx as nx
from os.path import exists
from scipy.sparse import *
import plotly.graph_objs as go
from typing import Literal
def load_data(dataset: Literal["Stanford", "NotreDame", "BerkStan"]) -> nx.Graph:
"""Load the dataset and return a graph.
Parameters
----------
dataset : Literal["Stanford", "BerkStan"]
The dataset to load.
Returns
-------
nx.Graph
The graph of the dataset.
data/web-Stanford.txt
"""
# check if there is a data folder
if not exists(os.path.join(os.getcwd(), "data")):
os.mkdir(os.path.join(os.getcwd(), "data"))
if dataset not in ["Stanford", "NotreDame", "BerkStan"]:
raise ValueError("Invalid dataset. Please choose a valid dataset.")
# Download the dataset
if not exists(f"../data/Web-{dataset}.txt.gz"):
print(f"\nDownloading the dataset {dataset}...")
wget.download(f"http://snap.stanford.edu/data/web-{dataset}.txt.gz", out=f"data/Web-{dataset}.txt.gz")
else:
print(f"\nThe dataset {dataset} is already downloaded.")
# unzip the dataset
if not exists(f"../data/Web-{dataset}.txt"):
print(f"\nUnzipping the dataset {dataset}...")
with gzip.open(f"../data/Web-{dataset}.txt.gz", "rb") as f_in:
with open(f"../data/Web-{dataset}.txt", "wb") as f_out:
f_out.write(f_in.read())
# create the graph
print(f"\nCreating the graph of the dataset {dataset}...\n")
G_dataset = nx.read_edgelist(f"../data/Web-{dataset}.txt", create_using=nx.DiGraph(), nodetype=int)
print(f"\tNumber of nodes: {G_dataset.number_of_nodes()}")
print(f"\tNumber of edges: {G_dataset.number_of_edges()}")
return G_dataset
def google_matrix(G, alpha=0.85, personalization=None, nodelist=None, weight="weight", dangling=None) -> np.matrix:
"""Returns the Google matrix of the graph.
Parameters
----------
G : graph
A NetworkX graph. Undirected graphs will be converted to a directed
graph with two directed edges for each undirected edge.
alpha : float
The damping factor.
personalization: dict, optional
The "personalization vector" consisting of a dictionary with a
key some subset of graph nodes and personalization value each of those.
At least one personalization value must be non-zero.
If not specfiied, a nodes personalization value will be zero.
By default, a uniform distribution is used.
nodelist : list, optional
The rows and columns are ordered according to the nodes in nodelist.
If nodelist is None, then the ordering is produced by G.nodes().
weight : key, optional
Edge data key to use as weight. If None weights are set to 1.
dangling: dict, optional
The outedges to be assigned to any "dangling" nodes, i.e., nodes without
any outedges. The dict key is the node the outedge points to and the dict
value is the weight of that outedge. By default, dangling nodes are given
outedges according to the personalization vector (uniform if not
specified) This must be selected to result in an irreducible transition
matrix (see notes below). It may be common to have the dangling dict to
be the same as the personalization dict.
Returns
-------
A : NumPy matrix
Google matrix of the graph
Notes
-----
The matrix returned represents the transition matrix that describes the
Markov chain used in PageRank. For PageRank to converge to a unique
solution (i.e., a unique stationary distribution in a Markov chain), the
transition matrix must be irreducible. In other words, it must be that
there exists a path between every pair of nodes in the graph, or else there
is the potential of "rank sinks."
"""
if nodelist is None:
nodelist = list(G)
A = nx.to_numpy_array(G, nodelist=nodelist, weight=weight)
N = len(G)
if N == 0:
# TODO: Remove np.asmatrix wrapper in version 3.0
return np.asmatrix(A)
# Personalization vector
if personalization is None:
p = np.repeat(1.0 / N, N)
else:
p = np.array([personalization.get(n, 0) for n in nodelist], dtype=float)
if p.sum() == 0:
raise ZeroDivisionError
p /= p.sum()
# Dangling nodes
if dangling is None:
dangling_weights = p
else:
# Convert the dangling dictionary into an array in nodelist order
dangling_weights = np.array([dangling.get(n, 0) for n in nodelist], dtype=float)
dangling_weights /= dangling_weights.sum()
dangling_nodes = np.where(A.sum(axis=1) == 0)[0]
# Assign dangling_weights to any dangling nodes (nodes with no out links)
A[dangling_nodes] = dangling_weights
A /= A.sum(axis=1)[:, np.newaxis] # Normalize rows to sum to 1
return np.asmatrix(alpha * A + (1 - alpha) * p)
def google_matrix_sparse(G, alpha=0.85, personalization=None, nodelist=None, weight="weight", dangling=None) -> np.matrix:
"""Returns the Google matrix of the graph.
Parameters
----------
G : graph
A NetworkX graph. Undirected graphs will be converted to a directed
graph with two directed edges for each undirected edge.
alpha : float
The damping factor.
personalization: dict, optional
The "personalization vector" consisting of a dictionary with a
key some subset of graph nodes and personalization value each of those.
At least one personalization value must be non-zero.
If not specfiied, a nodes personalization value will be zero.
By default, a uniform distribution is used.
nodelist : list, optional
The rows and columns are ordered according to the nodes in nodelist.
If nodelist is None, then the ordering is produced by G.nodes().
weight : key, optional
Edge data key to use as weight. If None weights are set to 1.
dangling: dict, optional
The outedges to be assigned to any "dangling" nodes, i.e., nodes without
any outedges. The dict key is the node the outedge points to and the dict
value is the weight of that outedge. By default, dangling nodes are given
outedges according to the personalization vector (uniform if not
specified) This must be selected to result in an irreducible transition
matrix (see notes below). It may be common to have the dangling dict to
be the same as the personalization dict.
Returns
-------
A : NumPy matrix
Google matrix of the graph
Notes
-----
The matrix returned represents the transition matrix that describes the
Markov chain used in PageRank. For PageRank to converge to a unique
solution (i.e., a unique stationary distribution in a Markov chain), the
transition matrix must be irreducible. In other words, it must be that
there exists a path between every pair of nodes in the graph, or else there
is the potential of "rank sinks."
"""
if nodelist is None:
nodelist = list(G)
A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, format="lil", dtype=int)
N = len(G)
if N == 0:
return np.asmatrix(A)
# Personalization vector
if personalization is None:
p = np.repeat(1.0 / N, N)
p = sp.sparse.lil_array(p)
else:
p = np.array([personalization.get(n, 0) for n in nodelist], dtype=float)
if p.sum() == 0:
raise ZeroDivisionError
p /= p.sum()
# Dangling nodes
if dangling is None:
dangling_weights = np.ones(N, dtype=int)
dangling_weights = sp.sparse.lil_array(dangling_weights, dtype=int)
else:
# Convert the dangling dictionary into an array in nodelist order
dangling_weights = np.array([dangling.get(n, 0) for n in nodelist], dtype=float)
dangling_weights /= dangling_weights.sum()
# # Assign dangling_weights to any dangling nodes (nodes with no out links).
# for i in range(N):
# if A[[i],:].sum(axis=1) == 0:
# A[[i],:] = dangling_weights
# replace rows with all zeros with dangling_weights
A[[A.sum(axis=1)==0],:] = dangling_weights
# Normalize rows
row_sums = A.sum(axis=1) # row sums
r_inv = np.power(row_sums.astype(float), -1).flatten() # inverse of row sums
r_inv[np.isinf(r_inv)] = 0.0 # replace inf with 0
R = sp.sparse.diags(r_inv) # create diagonal matrix
A = R.dot(A) # normalize rows
return A, p
def pagerank_numpy(G, alpha=0.85, personalization=None, weight="weight", dangling=None):
"""Returns the PageRank of the nodes in the graph.
PageRank computes a ranking of the nodes in the graph G based on
the structure of the incoming links. It was originally designed as
an algorithm to rank web pages.
Parameters
----------
G : graph
A NetworkX graph. Undirected graphs will be converted to a directed
graph with two directed edges for each undirected edge.
alpha : float, optional
Damping parameter for PageRank, default=0.85.
personalization: dict, optional
The "personalization vector" consisting of a dictionary with a
key some subset of graph nodes and personalization value each of those.
At least one personalization value must be non-zero.
If not specfiied, a nodes personalization value will be zero.
By default, a uniform distribution is used.
weight : key, optional
Edge data key to use as weight. If None weights are set to 1.
dangling: dict, optional
The outedges to be assigned to any "dangling" nodes, i.e., nodes without
any outedges. The dict key is the node the outedge points to and the dict
value is the weight of that outedge. By default, dangling nodes are given
outedges according to the personalization vector (uniform if not
specified) This must be selected to result in an irreducible transition
matrix (see notes under google_matrix). It may be common to have the
dangling dict to be the same as the personalization dict.
Returns
-------
pagerank : dictionary
Dictionary of nodes with PageRank as value.
Notes
-----
The eigenvector calculation uses NumPy's interface to the LAPACK
eigenvalue solvers. This will be the fastest and most accurate
for small graphs.
"""
if len(G) == 0:
return {}
M = google_matrix(
G, alpha, personalization=personalization, weight=weight, dangling=dangling
)
# use numpy LAPACK solver
eigenvalues, eigenvectors = np.linalg.eig(M.T)
ind = np.argmax(eigenvalues)
# eigenvector of largest eigenvalue is at ind, normalized
largest = np.array(eigenvectors[:, ind]).flatten().real
norm = largest.sum()
return dict(zip(G, map(float, largest / norm)))
def pagerank(G, alpha=0.85, personalization=None, max_iter=10000, tol=1.0e-9, nstart=None, weight="weight", dangling=None,):
"""
Returns the PageRank of the nodes in the graph.
PageRank computes a ranking of the nodes in the graph G based on
the structure of the incoming links. It was originally designed as
an algorithm to rank web pages.
Parameters
----------
G : graph
A NetworkX graph. Undirected graphs will be converted to a directed
graph with two directed edges for each undirected edge.
alpha : float, optional
Damping parameter for PageRank, default=0.85.
personalization: dict, optional
The "personalization vector" consisting of a dictionary with a
key some subset of graph nodes and personalization value each of those.
At least one personalization value must be non-zero.
If not specfiied, a nodes personalization value will 1/N where N is the
number of nodes in G.
max_iter : integer, optional
Maximum number of iterations in power method eigenvalue solver.
tol : float, optional
Error tolerance used to check convergence in power method solver.
nstart : dictionary, optional
Starting value of PageRank iteration for each node.
weight : key, optional
Edge data key to use as weight. If None weights are set to 1.
dangling: dict, optional
The outedges to be assigned to any "dangling" nodes, i.e., nodes without
any outedges. The dict key is the node the outedge points to and the dict
value is the weight of that outedge. By default, dangling nodes are given
outedges according to the personalization vector (uniform if not
specified) This must be selected to result in an irreducible transition
matrix (see notes under google_matrix). It may be common to have the
dangling dict to be the same as the personalization dict.
Returns
-------
pagerank : dictionary
Dictionary of nodes with PageRank as value
Notes
-----
The eigenvector calculation uses power iteration with a SciPy
sparse matrix representation.
Raises
------
PowerIterationFailedConvergence
If the algorithm fails to converge to the specified tolerance
within the specified number of iterations of the power iteration
method.
"""
N = len(G)
if N == 0:
return {}
nodelist = list(G)
A = nx.to_scipy_sparse_array(G, nodelist=nodelist, weight=weight, dtype=float)
S = A.sum(axis=1) # S[i] is the sum of the weights of edges going out of node i
S[S != 0] = 1.0 / S[S != 0] # S[i] is now the sum of the weights of edges going into node i
Q = sp.sparse.csr_array(sp.sparse.spdiags(S.T, 0, *A.shape)) # Q is the matrix of edge weights going into each node
A = Q @ A # A is now the "stochastic matrix"
# initial vector
if nstart is None: # if no initial vector is specified, start with a uniform vector
x = np.repeat(1.0 / N, N) # x is the vector of PageRank values
else: # if an initial vector is specified, normalize it
x = np.array([nstart.get(n, 0) for n in nodelist], dtype=float) # x is the vector of PageRank values
x /= x.sum() # normalize x
# Personalization vector
if personalization is None: # if no personalization vector is specified, use a uniform vector
p = np.repeat(1.0 / N, N) # p is the personalization vector
else: # if a personalization vector is specified, normalize it
p = np.array([personalization.get(n, 0) for n in nodelist], dtype=float) # p is the personalization vector
if p.sum() == 0: # if the personalization vector is all zeros, use a uniform vector
raise ZeroDivisionError
p /= p.sum() # normalize p
# Dangling nodes
if dangling is None: # if no dangling nodes are specified, use a uniform vector
dangling_weights = p # dangling_weights is the vector of dangling node weights
else:
# Convert the dangling dictionary into an array in nodelist order
dangling_weights = np.array([dangling.get(n, 0) for n in nodelist], dtype=float) # dangling_weights is the vector of dangling node weights
dangling_weights /= dangling_weights.sum() # normalize dangling_weights
is_dangling = np.where(S == 0)[0] # is_dangling is the list of dangling nodes
# power iteration: make up to max_iter iterations
iter = 1
for _ in range(max_iter):
iter += 1
xlast = x # xlast is the previous vector of PageRank values
x = alpha * (x @ A + sum(x[is_dangling]) * dangling_weights) + (1 - alpha) * p # x is the current vector of PageRank values
# check convergence, l1 norm
err = np.absolute(x - xlast).sum() # err is the error between the current and previous vectors of PageRank values
print("Iteration: ", _, "\r", end="")
if err < N * tol: # if the error is small enough, stop iterating
return dict(zip(nodelist, map(float, x))), iter, tol # return the current vector of PageRank values'
# other wise, return a Null dictionary, the number of iterations, and the tolerance
# this is a failure to convergeS
return {}, iter, tol
def shifted_pow_pagerank(G, alphas=[0.85, 0.9, 0.95, 0.99], max_iter=10000, tol=1.0e-9):
"""
Compute the PageRank of each node in the graph G.
Parameters
----------
G : graph
A NetworkX graph. Undirected graphs will be converted to a directed graph.
alphas : list, optional
A list of alpha values to use in the shifted power method. The default is [0.85, 0.9, 0.95, 0.99].
max_iter : integer, optional
Maximum number of iterations in power method eigenvalue solver.
tol : float, optional
Error tolerance used to check convergence in power method solver.
Returns
-------
pagerank : dictionary
Dictionary of nodes with PageRank as value
mv : integer
The number of matrix-vector multiplications used in the power method
Notes
-----
The eigenvector calculation uses power iteration with a SciPy sparse matrix representation. The shifted power method is described as Algorithm 1 in the paper located in the sources folders.
"""
N = len(G)
if N == 0:
raise nx.NetworkXException("Empty graph.")
A, v = google_matrix_sparse(G)
# initialize a sparse matrix of dimension N x len(alphas). The cols of this matrix are the page rank vectors for each alpha. Each col is v
x = sp.sparse.lil_matrix((N, len(alphas)))
for i in range(len(alphas)):
# reshape v to a column vector
v = v.reshape((N,1))
x[:, [i]] = v
# make v sparse
v = sp.sparse.lil_array(v)
mu = A @ v - v # column vector
r = sp.sparse.lil_matrix((N, len(alphas))) # residual matrix
res = np.ones(len(alphas)) # residual norm vector
for i in range(len(alphas)):
r[:,[i]] = alphas[i] * mu # residual vector for the i-th alpha
# r_i = r[:,[i]].toarray()
res[i] = sp.sparse.linalg.norm(r[:,[i]]) # residual norm for the i-th alpha
if res[i] >= tol:
x[:, [i]] = r[:,[i]] + v # update the i-th column of x
mv = 1 # number of matrix-vector multiplications
for _ in range(max_iter): # starting of the while loop in the paper
# print("Iteration: ", _)
err = np.max(res)
# print("Error: ", err)
if err < tol:
print("Convergence reached with, ", mv, " matrix-vector multiplications")
return x, mv, alphas, tol
print("Iteration: ", _, "\r", end="")
mu = A @ mu
mv += 1
for i in range(len(alphas)):
if res[i] >= tol:
r[:,[i]] = np.power(alphas[i], mv+1) * mu
# r_i = r[:,[i]].toarray()
res[i] = sp.sparse.linalg.norm(r[:,[i]])
if res[i] >= tol:
x[:, [i]] = r[:,[i]] + v
raise nx.PowerIterationFailedConvergence(max_iter) # if the error is not small enough, raise an error
-257
View File
@@ -1,257 +0,0 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import networkx as nx\n",
"import time\n",
"import math\n",
"import pandas as pd\n",
"import scipy as sp\n",
"import plotly.express as px\n",
"import plotly.graph_objs as go\n",
"from scipy.sparse import *\n",
"from scipy import linalg\n",
"from scipy.sparse.linalg import norm\n",
"from scipy.optimize import least_squares"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Arnoldi \n",
"\n",
"This is a copy of the algorithm defined and tested in the notebook `algo2_testing`. It's an implementation of the Algorithm 2 from the paper. It's needed in this notebook since this function is called by the `algo4` function. It's implemented to return exactly what's needed in the `algo4` function.\n",
"\n",
"Everything will be reorganized in the main.py file once everything is working."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def Arnoldi(A,v0,m):\n",
" v = v0\n",
" beta = norm(v)\n",
" v = v/beta\n",
" H = sp.sparse.lil_matrix((m+1,m)) \n",
" V = sp.sparse.lil_matrix((A.shape[0],m+1))\n",
" V[:,0] = v # each column of V is a vector v\n",
"\n",
" for j in range(m):\n",
" w = A @ v \n",
" for i in range(j):\n",
" tmp = v.T @ w # tmp is a 1x1 matrix, so it's O(1) in memory\n",
" H[i,j] = tmp[0,0] \n",
" w = w - H[i,j]*v \n",
" \n",
" H[j+1,j] = norm(w)\n",
"\n",
" if H[j+1,j] == 0:\n",
" # print(\"Arnoldi breakdown\")\n",
" m = j\n",
" v = 0\n",
" break\n",
" else:\n",
" if j < m-1:\n",
" v = w/H[j+1,j]\n",
" V[:,j+1] = v\n",
"\n",
" return V, H, v, beta, j "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Algorithm 4 testing\n",
"\n",
"This algorithm is based on the \"Algorithm 4\" of the paper, the pseudocode provided by the authors is the following \n",
"\n",
"![](https://i.imgur.com/H92fru7.png)\n",
"\n",
"Line 14 is particularly tricky to understand, not working for now. Need to figure out how to solve that linear system. My idea was to do something like that\n",
"\n",
"![](https://i.imgur.com/uBCDYUa.jpeg)\n",
"\n",
"And use the `sp.sparse.linalg.spsolve` function to solve the linear system as $Ax=0$ where $A$ is $[\\bar H_m^i ~ | ~ z]$ but it returns an array of zeros. So the idea it's wrong"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def Algo4(Pt, v, m, a: list, tau, maxit: int, x):\n",
" \n",
" # I'm using a non declared variable n here , it's declared in the next cell when I call this function. This will be fixed later in the main.py file\n",
"\n",
" iter = 1\n",
" mv = 0\n",
" I = sp.sparse.eye(n, n, format='lil')\n",
" r = sp.sparse.lil_matrix((n,1))\n",
" res = np.zeros(len(a)) \n",
"\n",
" # I'm defining 3 canonical vectors of different sizes. It's probably stupid, will be fixed once the algorithm actually works\n",
"\n",
" H_e1 = np.zeros((m+1,1)) # canonical basis vector of size H.shape[0]\n",
" H_e1[0] = 1\n",
"\n",
" V_e1 = np.zeros((n,1)) # canonical basis vector of size V.shape[0]\n",
" V_e1[0] = 1\n",
"\n",
" s_e1 = np.zeros((len(a),1)) # canonical basis vector of size s.shape[0]\n",
" s_e1[0] = 1\n",
"\n",
" def find_k(res): # function to find the index of the largest element in res\n",
" k = 0\n",
" for i in range(len(a)):\n",
" if res[i] == max(res):\n",
" k = i\n",
" break\n",
" return k\n",
"\n",
" def compute_gamma(res, a, k): # function to compute gamma\n",
" gamma = np.zeros(len(a))\n",
" for i in range(len(a)):\n",
" if i != k:\n",
" gamma[i] = (res[i]*a[k])/(res[k]*a[i])\n",
" else:\n",
" gamma[i] = 0\n",
" return gamma\n",
"\n",
" # compute the residual vector\n",
" for i in range(len(a)):\n",
" r = ((1-a[i])/a[i])*v - ((1/a[i])*I - Pt) @ x\n",
" res[i] = a[i]*norm(r)\n",
"\n",
" while max(res) >= tau and iter <= maxit:\n",
" k = find_k(res)\n",
" gamma = compute_gamma(res, a, k)\n",
" V, H, v, beta, j = Arnoldi((1/a[k])*I - Pt, r, m)\n",
"\n",
" mv = mv + j\n",
"\n",
" # compute y as the minimizer of || beta*e1 - Hy ||_2 using the least squares method\n",
" y = sp.sparse.linalg.lsqr(H, beta*H_e1)[0]\n",
"\n",
" # reshape y to be a column vector\n",
" y = y.reshape(y.shape[0],1)\n",
"\n",
" # update x \n",
" x += V[:,0:y.shape[0]] @ y\n",
"\n",
" # compute the residual vector\n",
" res[k] = a[k]*np.linalg.norm(beta*V_e1 - V[:,0:y.shape[0]] @ y)\n",
" \n",
" # for i in range(len(a)) but not k\n",
" for i in range(len(a)):\n",
" if i != k and res[i] >= tau:\n",
" # Compute H as described in the paper\n",
" H = H + ((1-a[i])/a[i] - (1-a[k])/a[k])*sp.sparse.eye(H.shape[0], H.shape[1], format='lil') \n",
"\n",
" z = beta*H_e1 - H @ y # define z as in the paper (page 9)\n",
" A_tmp = sp.sparse.hstack([H, z]) # stack H and z, as in the paper, to solve the linear system (?)\n",
" A_tmp = A_tmp.tocsc() # Convert A to CSC format for sparse solver\n",
"\n",
" # What should I put here? What does it mean in the paper the line 14 of the pseudocode?\n",
" result = sp.sparse.linalg.spsolve(A_tmp, np.zeros(A_tmp.shape[0])) # if I solve this, I get a vector of zeros.\n",
" print(result)\n",
" \n",
" # I don't know if the code below is correct since I don't get how to solve the linear system above, so I'm unsure about what y and gamma should be. For now it's commented out.\n",
"\n",
" # # update x\n",
" # x += V[:,0:y.shape[0]] @ y\n",
" # # update the residual vector\n",
" # res[i] = (a[i]/a[k])*gamma[k]*res[k] \n",
"\n",
" iter = iter + 1\n",
"\n",
" return x, iter, mv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Basic test case with random numbers to test the algorithm."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"n = 100\n",
"m = 110\n",
"maxit = 100\n",
"tau = 1e-6\n",
"a = [0.85, 0.9, 0.95, 0.99]\n",
"\n",
"x = sp.sparse.lil_matrix((n,1))\n",
"x[0,0] = 1\n",
"\n",
"# generate a random graph\n",
"G = nx.gnp_random_graph(n, 0.1, seed=1, directed=True)\n",
"\n",
"P = sp.sparse.lil_matrix((n,n))\n",
"for i in G.nodes():\n",
" for j in G[i]: #G[i] is the list of nodes connected to i, it's neighbors\n",
" P[i-1,j-1] = 1/len(G[i])\n",
"\n",
"# generate a probability vector, with all the entries as 1/n\n",
"v = sp.sparse.lil_matrix((n,1))\n",
"for i in range(n):\n",
" v[i] = 1/n\n",
"\n",
"# dangling nodes vector\n",
"d = sp.sparse.lil_matrix((n,1))\n",
"for i in range(n):\n",
" if P[i].sum() == 0:\n",
" d[i] = 1\n",
"\n",
"# compute the transition matrix\n",
"Pt = P + v @ (d.T)\n",
"\n",
"# run the algorithm\n",
"Algo4(Pt, v, m, a, tau, maxit, x)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.6 64-bit",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+82
View File
@@ -0,0 +1,82 @@
#! /usr/bin/python
from algo import *
import warnings
import argparse
warnings.filterwarnings("ignore")
# df = pd.DataFrame(columns=["method" "alpha", "cpu_time", "mv", "tol"])
def run_standard_pagerank(G, alphas):
print("\nStarting the standard pagerank algorithm...\n")
iter_dict = dict.fromkeys(alphas, 0)
list_of_pageranks = []
start1 = time.time()
for alpha in alphas:
x, iter, tol = pagerank(G, alpha, tol=1e-6)
iter_dict[alpha] = iter
list_of_pageranks.append(x)
end1 = time.time()
total_iter = sum(iter_dict.values())
cpu_time = round(end1 - start1,1)
mv = total_iter
print("\nSTANDARD PAGERANK ALGORITHM\n")
print("\tCPU time (s):", cpu_time)
print("\tMatrix-vector multiplications:", mv)
print("\tAlpha:", alphas)
print("\tTolerance:", tol)
for i in range(len(list_of_pageranks)):
if not list_of_pageranks[i]:
print("The algorithm did not converge for alpha =", alphas[i])
# df.loc[len(df)] = ["Power Method", alphas, cpu_time, mv, tol]
# df.to_csv(args.dataset + "_results.tsv", sep="\t", index=False)
# print("\nThe results are saved in the file:", args.dataset + "_results.tsv")
def run_shifted_powe(G, alphas):
print("\nStarting the shifted pagerank algorithm...\n")
start2 = time.time()
x, mv, alphas, tol = shifted_pow_pagerank(G, alphas, tol=1e-6)
end2 = time.time()
cpu_time = round(end2 - start2,1)
print("\nSHIFTED PAGERANK ALGORITHM\n")
print("\tCPU time (s):", cpu_time)
print("\tMatrix-vector multiplications:", mv)
print("\tAlphas:", alphas)
print("\tTolerance:", tol)
# df.loc[len(df)] = ["Shifted Power Method", alphas, cpu_time, mv, tol]
# df.to_csv(args.dataset + "_results.tsv", sep="\t", index=False)
# print("\nThe results are saved in the file:", args.dataset + "_results.tsv")
# main function
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--dataset", type=str, default="Stanford", help="Choose the dataset to work with. The options are: Stanford, NotreDame, BerkStan")
parser.add_argument("--algo", type=str, default="both", help="Choose the algorithm to use. The options are: pagerank, shifted_pagerank, both")
args = parser.parse_args()
G = load_data(args.dataset)
alphas = [0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99]
if args.algo == "pagerank":
run_standard_pagerank(G, alphas)
elif args.algo == "shifted_pagerank":
run_shifted_powe(G, alphas)
elif args.algo == "both":
run_standard_pagerank(G, alphas)
run_shifted_powe(G, alphas)