docs: def eigen_decomposition(G, n_components=8, eigen_solver='auto',
                          random_state=None,
                          drop_first=True, largest=True, solver_kwds=None):
      """
      Function to compute the eigendecomposition of a square matrix.
  
      Parameters
      ----------
      G : array_like or sparse matrix
          The square matrix for which to compute the eigen-decomposition.
      n_components : integer, optional
          The number of eigenvectors to return
      eigen_solver : {'auto', 'dense', 'arpack', 'lobpcg', or 'amg'}
          'auto' :
              attempt to choose the best method for input data (default)
          'dense' :
              use standard dense matrix operations for the eigenvalue decomposition.
              For this method, M must be an array or matrix type.
              This method should be avoided for large problems.
          'arpack' :
              use arnoldi iteration in shift-invert mode. For this method,
              M may be a dense matrix, sparse matrix, or general linear operator.
              Warning: ARPACK can be unstable for some problems.  It is best to
              try several random seeds in order to check results.
          'lobpcg' :
              Locally Optimal Block Preconditioned Conjugate Gradient Method.
              A preconditioned eigensolver for large symmetric positive definite
              (SPD) generalized eigenproblems.
          'amg' :
              Algebraic Multigrid solver (requires ``pyamg`` to be installed)
              It can be faster on very large, sparse problems, but may also lead
              to instabilities.
      random_state : int seed, RandomState instance, or None (default)
          A pseudo random number generator used for the initialization of the
          lobpcg eigen vectors decomposition when eigen_solver == 'amg'.
          By default, arpack is used.
      solver_kwds : any additional keyword arguments to pass to the selected eigen_solver
  
      Returns
      -------
      lambdas, diffusion_map : eigenvalues, eigenvectors
      """
      n_nodes = G.shape[0]
      if drop_first:
          n_components = n_components + 1
  
      eigen_solver, solver_kwds = check_eigen_solver(eigen_solver, solver_kwds,
                                                     size=n_nodes,
                                                     nvec=n_components)
      random_state = check_random_state(random_state)
  
      # Convert G to best type for eigendecomposition
      if sparse.issparse(G):
          if G.getformat() is not 'csr':
              G.tocsr()
      G = G.astype(np.float)
  
      # Check for symmetry
      is_symmetric = _is_symmetric(G)
  
      # Try Eigen Methods:
      if eigen_solver == 'arpack':
          # This matches the internal initial state used by ARPACK
          v0 = random_state.uniform(-1, 1, G.shape[0])
          if is_symmetric:
              if largest:
                  which = 'LM'
              else:
                  which = 'SM'
              lambdas, diffusion_map = eigsh(G, k=n_components, which=which,
                                             v0=v0,**(solver_kwds or {}))
          else:
              if largest:
                  which = 'LR'
              else:
                  which = 'SR'
              lambdas, diffusion_map = eigs(G, k=n_components, which=which,
                                            **(solver_kwds or {}))
          lambdas = np.real(lambdas)
          diffusion_map = np.real(diffusion_map)
      elif eigen_solver == 'amg':
          # separate amg & lobpcg keywords:
          if solver_kwds is not None:
              amg_kwds = {}
              lobpcg_kwds = solver_kwds.copy()
              for kwd in AMG_KWDS:
                  if kwd in solver_kwds.keys():
                      amg_kwds[kwd] = solver_kwds[kwd]
                      del lobpcg_kwds[kwd]
          else:
              amg_kwds = None
              lobpcg_kwds = None
          if not is_symmetric:
              raise ValueError("lobpcg requires symmetric matrices.")
          if not sparse.issparse(G):
              warnings.warn("AMG works better for sparse matrices")
          # Use AMG to get a preconditioner and speed up the eigenvalue problem.
          ml = smoothed_aggregation_solver(check_array(G, accept_sparse = ['csr']),**(amg_kwds or {}))
          M = ml.aspreconditioner()
          n_find = min(n_nodes, 5 + 2*n_components)
          X = random_state.rand(n_nodes, n_find)
          X[:, 0] = (G.diagonal()).ravel()
          lambdas, diffusion_map = lobpcg(G, X, M=M, largest=largest,**(lobpcg_kwds or {}))
          sort_order = np.argsort(lambdas)
          if largest:
              lambdas = lambdas[sort_order[::-1]]
              diffusion_map = diffusion_map[:, sort_order[::-1]]
          else:
              lambdas = lambdas[sort_order]
              diffusion_map = diffusion_map[:, sort_order]
          lambdas = lambdas[:n_components]
          diffusion_map = diffusion_map[:, :n_components]
      elif eigen_solver == "lobpcg":
          if not is_symmetric:
              raise ValueError("lobpcg requires symmetric matrices.")
          n_find = min(n_nodes, 5 + 2*n_components)
          X = random_state.rand(n_nodes, n_find)
          lambdas, diffusion_map = lobpcg(G, X, largest=largest,**(solver_kwds or {}))
          sort_order = np.argsort(lambdas)
          if largest:
              lambdas = lambdas[sort_order[::-1]]
              diffusion_map = diffusion_map[:, sort_order[::-1]]
          else:
              lambdas = lambdas[sort_order]
              diffusion_map = diffusion_map[:, sort_order]
          lambdas = lambdas[:n_components]
          diffusion_map = diffusion_map[:, :n_components]
      elif eigen_solver == 'dense':
          if sparse.isspmatrix(G):
              G = G.todense()
          if is_symmetric:
              lambdas, diffusion_map = eigh(G,**(solver_kwds or {}))
          else:
              lambdas, diffusion_map = eig(G,**(solver_kwds or {}))
              sort_index = np.argsort(lambdas)
              lambdas = lambdas[sort_index]
              diffusion_map[:,sort_index]
          if largest:# eigh always returns eigenvalues in ascending order
              lambdas = lambdas[::-1] # reverse order the e-values
              diffusion_map = diffusion_map[:, ::-1] # reverse order the vectors
          lambdas = lambdas[:n_components]
          diffusion_map = diffusion_map[:, :n_components]
      return (lambdas, diffusion_map)