def ltsa(geom, n_components, eigen_solver='<STR_LIT>',<EOL>random_state=None, solver_kwds=None):
if geom.X is None:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>(N, d_in) = geom.X.shape<EOL>if n_components > d_in:<EOL><INDENT>raise ValueError("<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>if geom.adjacency_matrix is None:<EOL><INDENT>geom.compute_adjacency_matrix()<EOL><DEDENT>(rows, cols) = geom.adjacency_matrix.nonzero()<EOL>eigen_solver, solver_kwds = check_eigen_solver(eigen_solver, solver_kwds,<EOL>size=geom.adjacency_matrix.shape[<NUM_LIT:0>],<EOL>nvec=n_components + <NUM_LIT:1>)<EOL>if eigen_solver != '<STR_LIT>':<EOL><INDENT>M = sparse.csr_matrix((N, N))<EOL><DEDENT>else:<EOL><INDENT>M = np.zeros((N, N))<EOL><DEDENT>for i in range(N):<EOL><INDENT>neighbors_i = cols[rows == i]<EOL>n_neighbors_i = len(neighbors_i)<EOL>use_svd = (n_neighbors_i > d_in)<EOL>Xi = geom.X[neighbors_i]<EOL>Xi -= Xi.mean(<NUM_LIT:0>)<EOL>if use_svd:<EOL><INDENT>v = svd(Xi, full_matrices=True)[<NUM_LIT:0>]<EOL><DEDENT>else:<EOL><INDENT>Ci = np.dot(Xi, Xi.T)<EOL>v = eigh(Ci)[<NUM_LIT:1>][:, ::-<NUM_LIT:1>]<EOL><DEDENT>Gi = np.zeros((n_neighbors_i, n_components + <NUM_LIT:1>))<EOL>Gi[:, <NUM_LIT:1>:] = v[:, :n_components]<EOL>Gi[:, <NUM_LIT:0>] = <NUM_LIT:1.> / np.sqrt(n_neighbors_i)<EOL>GiGiT = np.dot(Gi, Gi.T)<EOL>nbrs_x, nbrs_y = np.meshgrid(neighbors_i, neighbors_i)<EOL>with warnings.catch_warnings():<EOL><INDENT>warnings.simplefilter("<STR_LIT:ignore>")<EOL>M[nbrs_x, nbrs_y] -= GiGiT<EOL>M[neighbors_i, neighbors_i] += <NUM_LIT:1><EOL><DEDENT><DEDENT>return null_space(M, n_components, k_skip=<NUM_LIT:1>, eigen_solver=eigen_solver,<EOL>random_state=random_state,solver_kwds=solver_kwds)<EOL>
Perform a Local Tangent Space Alignment analysis on the data.

Parameters
----------
geom : a Geometry object from megaman.geometry.geometry
n_components : integer
    number of coordinates for the manifold.
eigen_solver : {'auto', 'dense', 'arpack', 'lobpcg', or 'amg'}
    'auto' :
        algorithm will attempt to choose the best method for input data
    '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' :
        AMG requires pyamg to be installed. It can be faster on very large,
        sparse problems, but may also lead to instabilities.
random_state : numpy.RandomState or int, optional
    The generator or seed used to determine the starting vector for arpack
    iterations.  Defaults to numpy.random.
solver_kwds : any additional keyword arguments to pass to the selected eigen_solver

Returns
-------
embedding : array-like, shape [n_samples, n_components]
    Embedding vectors.
squared_error : float
    Reconstruction error for the embedding vectors. Equivalent to
    ``norm(Y - W Y, 'fro')**2``, where W are the reconstruction weights.

References
----------
* Zhang, Z. & Zha, H. Principal manifolds and nonlinear
  dimensionality reduction via tangent space alignment.
  Journal of Shanghai Univ.  8:406 (2004)