def compute_user_pagerank(network, user, directed_networks, weighted):
    if len(network.es) == 0:
        return 1

    options = igraph.ARPACKOptions()
    options.mxiter = 100000

    # Igraph 0.7 has a bad bug! It does not give pagerank of a specific user. It
    # only returns pagerank of first user in the graph, no matter what the
    # vertices argument is. So we get a list of all pageranks, then query in the
    # list with the index of node with name user
    user_index = network.vs.find(user).index

    if weighted:
        # if weights are requested, we should use the original weights rather than their inverse
        # (in contrast to closeness). In case of pagerank, large edge weights are good for the
        # incoming node. They give it more credit.
        weights = network.es['weight']
    else:
        weights = None

    user_pagerank = network.pagerank(directed=directed_networks,
                                     damping=0.85,
                                     weights=weights,
                                     implementation="prpack",
                                     arpack_options=options)[user_index]
    if user_pagerank > 1:
        sys.stderr.write('Error: Pagerank of user %s is greater than one: %f' %
                         (user, user_pagerank))

    # Igraph returns tiny non-zero values for page ranks that should be really zero.
    # manually zero them out.
    if user_pagerank < pow(10, -15):
        user_pagerank = 0
    return utils.round_to_sigfigs(user_pagerank, NUM_SIGFIGS)
def compute_user_pagerank(network, user, directed_networks, weighted):
  if len(network.es) == 0:
    return 1

  options = igraph.ARPACKOptions()
  options.mxiter = 100000

  # Igraph 0.7 has a bad bug! It does not give pagerank of a specific user. It
  # only returns pagerank of first user in the graph, no matter what the
  # vertices argument is. So we get a list of all pageranks, then query in the
  # list with the index of node with name user
  user_index = network.vs.find(user).index

  if weighted:
    # if weights are requested, we should use the original weights rather than their inverse
    # (in contrast to closeness). In case of pagerank, large edge weights are good for the
    # incoming node. They give it more credit.
    weights = network.es['weight']
  else:
    weights = None

  user_pagerank = network.pagerank(directed=directed_networks,
                                   damping=0.85, weights=weights,
                                   implementation = "prpack",
                                   arpack_options = options)[user_index]
  if user_pagerank > 1:
    sys.stderr.write('Error: Pagerank of user %s is greater than one: %f' %
                     (user, user_pagerank))

  # Igraph returns tiny non-zero values for page ranks that should be really zero.
  # manually zero them out.
  if user_pagerank < pow(10, -15):
    user_pagerank = 0
  return utils.round_to_sigfigs(user_pagerank, NUM_SIGFIGS)
def compute_network_weighted_average_path_length(network, directed_networks):
    if len(network.es) == 0:
        return 0

    # weights are requested, we should use the inverse of weights, since higher weights
    # means more interactions between two users
    weights = [1.0 / w for w in network.es['weight']]
    if directed_networks:
        shortest_paths = network.shortest_paths(source=network.vs,
                                                target=network.vs,
                                                weights=weights,
                                                mode=igraph.OUT)
    else:
        shortest_paths = network.shortest_paths(source=network.vs,
                                                target=network.vs,
                                                weights=weights,
                                                mode=igraph.ALL)

    # iterate over all shortest paths and add them up
    # assign the largest path length to unconnected nodes
    max_path_length = -1
    for i in shortest_paths:
        for path_length in i:
            if path_length != float('Inf') and path_length > max_path_length:
                max_path_length = path_length

    num_pairs = 0
    sum_path_lengths = 0
    for i in shortest_paths:
        for path_length in i:
            sum_path_lengths += min(path_length, max_path_length)
            num_pairs += 1
    average_path_length = sum_path_lengths / num_pairs
    return utils.round_to_sigfigs(average_path_length, NUM_SIGFIGS)
def compute_network_weighted_average_path_length(network, directed_networks):
  if len(network.es) == 0:
    return 0
  
  # weights are requested, we should use the inverse of weights, since higher weights
  # means more interactions between two users
  weights = [1.0/w for w in network.es['weight']]
  if directed_networks:
    shortest_paths = network.shortest_paths(source = network.vs, target = network.vs,
                                            weights = weights, mode = igraph.OUT)
  else:
    shortest_paths = network.shortest_paths(source = network.vs, target = network.vs,
                                            weights = weights, mode = igraph.ALL)
  
  # iterate over all shortest paths and add them up
  # assign the largest path length to unconnected nodes
  max_path_length = -1
  for i in shortest_paths: 
    for path_length in i:
      if path_length != float('Inf') and path_length > max_path_length:
        max_path_length = path_length
  
  num_pairs = 0
  sum_path_lengths = 0
  for i in shortest_paths: 
    for path_length in i:
      sum_path_lengths += min(path_length, max_path_length)
      num_pairs += 1
  average_path_length = sum_path_lengths / num_pairs
  return utils.round_to_sigfigs(average_path_length, NUM_SIGFIGS)
def compute_user_closeness_centrality(network, user, mode, weighted):
    if weighted:
        # if weights are requested, we should use the inverse of weights, since higher weights
        # means more interactions between two users
        weights = [1.0 / w for w in network.es['weight']]
    else:
        weights = None
    user_closeness_centrality = network.closeness(vertices=user,
                                                  mode=mode,
                                                  weights=weights)
    return utils.round_to_sigfigs(user_closeness_centrality, NUM_SIGFIGS)
def compute_user_closeness_centrality(network, user, mode, weighted):
  if weighted:
    # if weights are requested, we should use the inverse of weights, since higher weights
    # means more interactions between two users
    weights = [1.0/w for w in network.es['weight']]
  else:
    weights = None
  user_closeness_centrality = network.closeness(vertices = user,
                                                mode = mode,
                                                weights = weights)
  return utils.round_to_sigfigs(user_closeness_centrality, NUM_SIGFIGS)
def compute_laplacian_centrality(network, vertex):
    # find mapping from vertex to sum of edge weights
    vertex_weights = collections.defaultdict(float)
    for edge in network.es:
        source = edge["source"]
        target = edge["target"]
        weight = edge["weight"]
        vertex_weights[source] += weight
        vertex_weights[target] += weight
    neighbors = network.neighbors(vertex, mode="all")
    result = (vertex_weights[vertex]**2 + vertex_weights[vertex] +
              2 * sum(vertex_weights[i] for i in neighbors))
    return utils.round_to_sigfigs(result, NUM_SIGFIGS)
def compute_laplacian_centrality(network, vertex):
  # find mapping from vertex to sum of edge weights
  vertex_weights = collections.defaultdict(float)
  for edge in network.es:
    source = edge["source"]
    target = edge["target"]
    weight = edge["weight"]
    vertex_weights[source] += weight
    vertex_weights[target] += weight
  neighbors = network.neighbors(vertex, mode="all")
  result = (vertex_weights[vertex]**2 + vertex_weights[vertex] +
            2 * sum(vertex_weights[i] for i in neighbors) )
  return utils.round_to_sigfigs(result, NUM_SIGFIGS)
def compute_network_weighted_density(network, directed_networks):
  if len(network.es) == 0:
    return 0

  num_vertices = len(network.vs)
  num_max_edges = num_vertices * (num_vertices - 1)
  sum_edge_weights = 0
  for edge_weight in network.es['weight']:
    sum_edge_weights += edge_weight

  if directed_networks:
    density = (sum_edge_weights / num_max_edges)
  else:
    density = (2.0 * sum_edge_weights / num_max_edges)
  return utils.round_to_sigfigs(density, NUM_SIGFIGS)
def compute_network_weighted_density(network, directed_networks):
    if len(network.es) == 0:
        return 0

    num_vertices = len(network.vs)
    num_max_edges = num_vertices * (num_vertices - 1)
    sum_edge_weights = 0
    for edge_weight in network.es['weight']:
        sum_edge_weights += edge_weight

    if directed_networks:
        density = (sum_edge_weights / num_max_edges)
    else:
        density = (2.0 * sum_edge_weights / num_max_edges)
    return utils.round_to_sigfigs(density, NUM_SIGFIGS)
def compute_user_betweenness_centrality(network, user, directed_networks, weighted):
  if len(network.es) == 0:
    return 1

  if weighted:
    # if weights are requested, we should use the inverse of weights, since higher weights
    # means more interactions between two users so the path length should be smaller
    weights = [1.0/w for w in network.es['weight']]
  else:
    weights = None

  user_betweenness = network.betweenness(vertices = user,
                                         directed = directed_networks,
                                         weights = weights,
                                         nobigint = False)
  return utils.round_to_sigfigs(user_betweenness, NUM_SIGFIGS)
def compute_user_satoshi_pagerank(network, user, weighted):
    if len(network.es) == 0:
        return 1
    if 'satoshi' not in network.vs["name"]:
        return 0

    # increase the pagerank computation max number of iterations. sometimes arpack does not
    # converge.
    options = igraph.ARPACKOptions()
    options.mxiter = 100000

    # Igraph 0.7 has a bad bug! It does not give pagerank of a specific user. It
    # only returns pagerank of first user in the graph, no matter what the
    # vertices argument is. So we get a list of all pageranks, then query in the
    # list with the index of node with name user
    user_index = network.vs.find(user).index

    if weighted:
        # if weights are requested, we should use the original weights rather than their inverse
        # (in contrast to closeness). In case of pagerank, large edge weights are good for the
        # incoming node. They give it more credit.
        weights = network.es['weight']
    else:
        weights = None

    # Vertices should be the active user whose page rank we want to compute. directed
    # should be true so that directed paths are considered. damping is the probability
    # that we reset the random walk on Satoshi at each step. reset_vertices should only
    # contain Satoshi, since we are interested in flow from Satoshi. weights should be
    # the 'weight' attribute so that weights are used in page rank computation.
    user_satoshi_pagerank = network.personalized_pagerank(
        directed=True,
        damping=0.85,
        reset_vertices='satoshi',
        weights=weights,
        implementation="prpack",
        arpack_options=options)[user_index]
    if user_satoshi_pagerank > 1:
        sys.stderr.write(
            'Error: Satoshi pagerank of user %s is greater than one: %f' %
            (user, user_satoshi_pagerank))

    # Igraph returns tiny non-zero values for page ranks that should be really zero.
    # manually zero them out.
    if user_satoshi_pagerank < pow(10, -15):
        user_satoshi_pagerank = 0
    return utils.round_to_sigfigs(user_satoshi_pagerank, NUM_SIGFIGS)
def compute_user_betweenness_centrality(network, user, directed_networks,
                                        weighted):
    if len(network.es) == 0:
        return 1

    if weighted:
        # if weights are requested, we should use the inverse of weights, since higher weights
        # means more interactions between two users so the path length should be smaller
        weights = [1.0 / w for w in network.es['weight']]
    else:
        weights = None

    user_betweenness = network.betweenness(vertices=user,
                                           directed=directed_networks,
                                           weights=weights,
                                           nobigint=False)
    return utils.round_to_sigfigs(user_betweenness, NUM_SIGFIGS)
def compute_user_satoshi_pagerank(network, user, weighted):
  if len(network.es) == 0:
    return 1
  if 'satoshi' not in network.vs["name"]:
    return 0

  # increase the pagerank computation max number of iterations. sometimes arpack does not
  # converge.
  options = igraph.ARPACKOptions()
  options.mxiter = 100000

  # Igraph 0.7 has a bad bug! It does not give pagerank of a specific user. It
  # only returns pagerank of first user in the graph, no matter what the
  # vertices argument is. So we get a list of all pageranks, then query in the
  # list with the index of node with name user
  user_index = network.vs.find(user).index

  if weighted:
    # if weights are requested, we should use the original weights rather than their inverse
    # (in contrast to closeness). In case of pagerank, large edge weights are good for the
    # incoming node. They give it more credit.
    weights = network.es['weight']
  else:
    weights = None

  # Vertices should be the active user whose page rank we want to compute. directed
  # should be true so that directed paths are considered. damping is the probability
  # that we reset the random walk on Satoshi at each step. reset_vertices should only
  # contain Satoshi, since we are interested in flow from Satoshi. weights should be
  # the 'weight' attribute so that weights are used in page rank computation.
  user_satoshi_pagerank = network.personalized_pagerank(
      directed=True, damping=0.85, reset_vertices='satoshi', weights=weights,
      implementation = "prpack", arpack_options = options)[user_index]
  if user_satoshi_pagerank > 1:
    sys.stderr.write('Error: Satoshi pagerank of user %s is greater than one: %f' %
                     (user, user_satoshi_pagerank))

  # Igraph returns tiny non-zero values for page ranks that should be really zero.
  # manually zero them out.
  if user_satoshi_pagerank < pow(10, -15):
    user_satoshi_pagerank = 0
  return utils.round_to_sigfigs(user_satoshi_pagerank, NUM_SIGFIGS)
def compute_user_clustering_coefficient(network, user):
    user_clustering_coefficient = network.transitivity_local_undirected(
        vertices=user, mode="zero")
    return utils.round_to_sigfigs(user_clustering_coefficient, NUM_SIGFIGS)
def compute_network_average_degree(network):
    average_degree = network.degree_distribution().mean
    return utils.round_to_sigfigs(average_degree, NUM_SIGFIGS)
def compute_network_clustering_coefficient(network):
    clustering_coefficient = network.transitivity_undirected(mode="zero")
    return utils.round_to_sigfigs(clustering_coefficient, NUM_SIGFIGS)
def compute_network_average_path_length(network, directed_networks):
  average_path_length = network.average_path_length(directed = directed_networks,
                                                    unconn = True)
  return utils.round_to_sigfigs(average_path_length, NUM_SIGFIGS)
def compute_network_unweighted_average_path_length(network, directed_networks):
    average_path_length = network.average_path_length(
        directed=directed_networks, unconn=False)
    return utils.round_to_sigfigs(average_path_length, NUM_SIGFIGS)
def compute_network_unweighted_density(network):
    return utils.round_to_sigfigs(network.density(loops=True), NUM_SIGFIGS)
def compute_network_unweighted_density(network):
  return utils.round_to_sigfigs(network.density(loops=True), NUM_SIGFIGS)
def compute_network_clustering_coefficient(network):
  clustering_coefficient = network.transitivity_undirected(mode="zero")
  return utils.round_to_sigfigs(clustering_coefficient, NUM_SIGFIGS)
def compute_network_average_degree(network):
  average_degree = network.degree_distribution().mean
  return utils.round_to_sigfigs(average_degree, NUM_SIGFIGS)
def compute_user_clustering_coefficient(network, user):
  user_clustering_coefficient = network.transitivity_local_undirected(
      vertices = user, mode = "zero")
  return utils.round_to_sigfigs(user_clustering_coefficient, NUM_SIGFIGS)