docs: def lazy_approximate_personalized_pagerank(s,
                                             r,
                                             w_i,
                                             a_i,
                                             out_degree,
                                             in_degree,
                                             seed_node,
                                             rho=0.2,
                                             epsilon=0.00001,
                                             laziness_factor=0.5):
      """
      Calculates the approximate personalized PageRank starting from a seed node with self-loops.
  
      Introduced in: Andersen, R., Chung, F., & Lang, K. (2006, October).
                     Local graph partitioning using pagerank vectors.
                     In Foundations of Computer Science, 2006. FOCS'06. 47th Annual IEEE Symposium on (pp. 475-486). IEEE.
      """
      # Initialize approximate PageRank and residual distributions
      # s = np.zeros(number_of_nodes, dtype=np.float64)
      # r = np.zeros(number_of_nodes, dtype=np.float64)
      r[seed_node] = 1.0
  
      # Initialize queue of nodes to be pushed
      pushable = deque()
      pushable.append(seed_node)
  
      # Do one push anyway
      push_node = pushable.popleft()
  
      pagerank_lazy_push(s,
                         r,
                         w_i[push_node],
                         a_i[push_node],
                         push_node,
                         rho,
                         laziness_factor)
      number_of_push_operations = 1
  
      i = np.where(np.divide(r[a_i[push_node]], in_degree[a_i[push_node]]) >= epsilon)[0]
      if i.size > 0:
          pushable.extend(a_i[push_node][i])
  
      while r[push_node]/in_degree[push_node] >= epsilon:
          pagerank_lazy_push(s,
                             r,
                             w_i[push_node],
                             a_i[push_node],
                             push_node,
                             rho,
                             laziness_factor)
          number_of_push_operations += 1
  
      # While there are nodes with large residual probabilities, push
      while len(pushable) > 0:
          push_node = pushable.popleft()
  
          if r[push_node]/in_degree[push_node] >= epsilon:
              pagerank_lazy_push(s,
                                 r,
                                 w_i[push_node],
                                 a_i[push_node],
                                 push_node,
                                 rho,
                                 laziness_factor)
              number_of_push_operations += 1
  
              i = np.where(np.divide(r[a_i[push_node]], in_degree[a_i[push_node]]) >= epsilon)[0]
              if i.size > 0:
                  pushable.extend(a_i[push_node][i])
  
          while r[push_node]/in_degree[push_node] >= epsilon:
              pagerank_lazy_push(s,
                                 r,
                                 w_i[push_node],
                                 a_i[push_node],
                                 push_node,
                                 rho,
                                 laziness_factor)
              number_of_push_operations += 1
  
      return number_of_push_operations