Exemplo n.º 1
0
    def compute_model(self, Markovs, num_states, mc=None, mo=None):
        """Computes the A, B, and C LTI ROM matrices.

        Args:
            ``Markovs``: Array of Markov params w/indices [time, output, input] 
                ``Markovs[i]`` is the Markov parameter C A**i B.
                
            ``num_states``: Number of states to be found for the model.
            
            
        Kwargs:
            ``mc``: Number of Markov parameters for controllable dimension.
            		
            ``mo``: Number of Markov parameters for observable dimension.
            		Default is mc and mo equal and maximal for a balanced model.
        
        Assembles the Hankel matrices from self.Markovs and takes SVD.
        
        Default values of ``mc`` and ``mo`` are equal and maximal
        for a balanced model.            
        
        Tip: For discrete time systems the impulse is applied over a time
        interval dt and so has a time-integral 1*dt rather than 1. 
        This means the reduced B matrix is "off" by a factor of dt. 
        You can account for this by multiplying B by dt.
        """
        #SVD is ``L_sing_vecs*N.mat(N.diag(sing_vals))*\
        #    R_sing_vecs.H = Hankel_mat``
        self._set_Markovs(Markovs)       
        self.mc = mc
        self.mo = mo

        self._assemble_Hankel()
        self.L_sing_vecs, self.sing_vals, self.R_sing_vecs = \
            util.svd(self.Hankel_mat) 

        # Truncate matrices
        Ur = N.mat(self.L_sing_vecs[:, :num_states])
        Er = N.squeeze(self.sing_vals[:num_states])
        Vr = N.mat(self.R_sing_vecs[:, :num_states])
        
        self.A = N.mat(N.diag(Er**-.5)) * Ur.H * self.Hankel_mat2 * Vr * \
            N.mat(N.diag(Er**-.5))
        self.B = (N.mat(N.diag(Er**.5)) * (Vr.H)[:, :self.num_inputs]) 
        # *dt above is removed, users must do this themselves.
        # It is explained in the docs.
        
        self.C = Ur[:self.num_Markovs] * N.mat(N.diag(Er**.5))
        
        if (N.abs(N.linalg.eigvals(self.A)) >= 1.).any() and self.verbosity:
            print 'Warning: Unstable eigenvalues of reduced A matrix'
            print 'eig vals are', N.linalg.eigvals(self.A)
        return self.A, self.B, self.C
Exemplo n.º 2
0
    def compute_model(self, Markovs, num_states, mc=None, mo=None):
        """Computes the A, B, and C LTI ROM matrices.

        Args:
            ``Markovs``: Array of Markov params w/indices [time, output, input] 
                ``Markovs[i]`` is the Markov parameter C A**i B.
                
            ``num_states``: Number of states to be found for the model.
            
            
        Kwargs:
            ``mc``: Number of Markov parameters for controllable dimension.
            		
            ``mo``: Number of Markov parameters for observable dimension.
            		Default is mc and mo equal and maximal for a balanced model.
        
        Assembles the Hankel matrices from self.Markovs and takes SVD.
        
        Default values of ``mc`` and ``mo`` are equal and maximal
        for a balanced model.            
        
        Tip: For discrete time systems the impulse is applied over a time
        interval dt and so has a time-integral 1*dt rather than 1. 
        This means the reduced B matrix is "off" by a factor of dt. 
        You can account for this by multiplying B by dt.
        """
        #SVD is ``L_sing_vecs*N.mat(N.diag(sing_vals))*\
        #    R_sing_vecs.H = Hankel_mat``
        self._set_Markovs(Markovs)
        self.mc = mc
        self.mo = mo

        self._assemble_Hankel()
        self.L_sing_vecs, self.sing_vals, self.R_sing_vecs = \
            util.svd(self.Hankel_mat)

        # Truncate matrices
        Ur = N.mat(self.L_sing_vecs[:, :num_states])
        Er = N.squeeze(self.sing_vals[:num_states])
        Vr = N.mat(self.R_sing_vecs[:, :num_states])

        self.A = N.mat(N.diag(Er**-.5)) * Ur.H * self.Hankel_mat2 * Vr * \
            N.mat(N.diag(Er**-.5))
        self.B = (N.mat(N.diag(Er**.5)) * (Vr.H)[:, :self.num_inputs])
        # *dt above is removed, users must do this themselves.
        # It is explained in the docs.

        self.C = Ur[:self.num_Markovs] * N.mat(N.diag(Er**.5))

        if (N.abs(N.linalg.eigvals(self.A)) >= 1.).any() and self.verbosity:
            print 'Warning: Unstable eigenvalues of reduced A matrix'
            print 'eig vals are', N.linalg.eigvals(self.A)
        return self.A, self.B, self.C
Exemplo n.º 3
0
 def _helper_compute_DMD_from_data(self, vecs, adv_vecs,
     inner_product):
     correlation_mat = inner_product(vecs, vecs)
     W, Sigma, dummy = util.svd(correlation_mat) # dummy = W.
     U = vecs.dot(W).dot(N.diag(Sigma**-0.5))
     ritz_vals, eig_vecs = N.linalg.eig(inner_product(
         U, adv_vecs).dot(W).dot(N.diag(Sigma**-0.5)))
     eig_vecs = N.mat(eig_vecs)
     ritz_vecs = U.dot(eig_vecs)
     scaling = N.linalg.lstsq(ritz_vecs, vecs[:, 0])[0]
     scaling = N.mat(N.diag(N.array(scaling).squeeze()))
     ritz_vecs = ritz_vecs.dot(scaling)
     build_coeffs = W.dot(N.diag(Sigma**-0.5)).dot(eig_vecs).dot(scaling)
     mode_norms = N.diag(inner_product(ritz_vecs, ritz_vecs)).real
     return ritz_vals, ritz_vecs, build_coeffs, mode_norms
Exemplo n.º 4
0
 def test_svd(self):
     num_internals_list = [10, 50]
     num_rows_list = [3, 5, 40]
     num_cols_list = [1, 9, 70]
     for num_rows in num_rows_list:
         for num_cols in num_cols_list:
             for num_internals in num_internals_list:
                 left_mat = N.mat(N.random.random((num_rows, num_internals)))
                 right_mat = N.mat(N.random.random((num_internals, num_cols)))
                 full_mat = left_mat*right_mat
                 L_sing_vecs, sing_vals, R_sing_vecs = util.svd(full_mat)
                 
                 U, E, V_comp_conj = N.linalg.svd(full_mat, full_matrices=0)
                 V = N.mat(V_comp_conj).H
                 if num_internals < num_rows or num_internals < num_cols:
                     U = U[:,:num_internals]
                     V = V[:,:num_internals]
                     E = E[:num_internals]
       
                 N.testing.assert_allclose(L_sing_vecs, U)
                 N.testing.assert_allclose(sing_vals, E)
                 N.testing.assert_allclose(R_sing_vecs, V)
Exemplo n.º 5
0
    def _helper_compute_DMD_from_data(self, vec_array, adv_vec_array,
        inner_product):
        # Create lists of vecs, advanced vecs for inner product function
        vecs = [vec_array[:, i] for i in range(vec_array.shape[1])]
        adv_vecs = [adv_vec_array[:, i] for i in range(adv_vec_array.shape[1])]

        # Compute DMD
        correlation_mat = inner_product(vecs, vecs)
        W, Sigma, dummy = util.svd(correlation_mat) # dummy = W.
        U = vec_array.dot(W).dot(N.diag(Sigma**-0.5))
        U_list = [U[:,i] for i in range(U.shape[1])]
        ritz_vals, eig_vecs = N.linalg.eig(inner_product(
            U_list, adv_vecs).dot(W).dot(N.diag(Sigma**-0.5)))
        eig_vecs = N.mat(eig_vecs)
        ritz_vecs = U.dot(eig_vecs)
        scaling = N.linalg.lstsq(ritz_vecs, vec_array[:, 0])[0]
        scaling = N.mat(N.diag(N.array(scaling).squeeze()))
        ritz_vecs = ritz_vecs.dot(scaling)
        build_coeffs = W.dot(N.diag(Sigma**-0.5)).dot(eig_vecs).dot(scaling)
        ritz_vecs_list = [N.array(ritz_vecs[:,i]).squeeze() 
            for i in range(ritz_vecs.shape[1])]
        mode_norms = N.diag(inner_product(ritz_vecs_list, ritz_vecs_list)).real

        return ritz_vals, ritz_vecs, build_coeffs, mode_norms
Exemplo n.º 6
0
def compute_DMD_matrices_direct_method(vecs,
                                       mode_indices,
                                       adv_vecs=None,
                                       inner_product_weights=None,
                                       return_all=False):
    """Dynamic Mode Decomposition/Koopman Mode Decomposition with data in a
    matrix, using a direct method.

    Args:
        ``vecs``: Matrix with vectors as columns.
    
        ``mode_indices``: List of mode numbers, ``range(10)`` or ``[3, 0, 5]``.
    
    Kwargs:
        ``adv_vecs``: Matrix with ``vecs`` advanced in time as columns.
            If not provided, then it is assumed that the vectors are a 
            sequential time-series. Thus ``vecs`` becomes ``vecs[:-1]`` and
            ``adv_vecs`` becomes ``vecs[1:]``.
            
        ``inner_product_weights``: 1D or Matrix of inner product weights.
            It corresponds to :math:`W` in inner product :math:`v_1^* W v_2`.

        ``return_all``: Return more objects, see below. Default is false.

    Returns:
        ``modes``: Matrix with requested modes as columns.

        ``ritz_vals``: 1D array of Ritz values.
        
        ``mode_norms``: 1D array of mode norms.

        If ``return_all`` is true, also returns:

        ``build_coeffs``: Matrix of build coefficients for modes.
        
    This method does not square the matrix of vectors as in the method of
    snapshots (:py:func:`compute_DMD_matrices_snaps_method`). It's slightly 
    more accurate, but slower when the number of elements in a vector is 
    more than the number of vectors (more rows than columns in ``vecs``).
    """
    if _parallel.is_distributed():
        raise RuntimeError('Cannot run in parallel.')
    vec_space = VectorSpaceMatrices(weights=inner_product_weights)
    vecs = util.make_mat(vecs)
    if adv_vecs is not None:
        adv_vecs = util.make_mat(adv_vecs)

    if inner_product_weights is None:
        vecs_weighted = vecs
        if adv_vecs is not None:
            adv_vecs_weighted = adv_vecs
    elif inner_product_weights.ndim == 1:
        sqrt_weights = N.mat(N.diag(inner_product_weights**0.5))
        vecs_weighted = sqrt_weights * vecs
        if adv_vecs is not None:
            adv_vecs_weighted = sqrt_weights * adv_vecs
    elif inner_product_weights.ndim == 2:
        if inner_product_weights.shape[0] > 500:
            print 'Warning: Cholesky decomposition could be time consuming.'
        sqrt_weights = N.mat(N.linalg.cholesky(inner_product_weights)).H
        vecs_weighted = sqrt_weights * vecs
        if adv_vecs is not None:
            adv_vecs_weighted = sqrt_weights * adv_vecs

    # Compute low-order linear map for sequential snapshot set.  This takes
    # advantage of the fact that for a sequential dataset, the unadvanced
    # and advanced vectors overlap.
    if adv_vecs is None:
        U, sing_vals, correlation_mat_evecs = util.svd(vecs_weighted[:, :-1])
        correlation_mat_evals = sing_vals**2
        correlation_mat = correlation_mat_evecs * \
            N.mat(N.diag(correlation_mat_evals)) * correlation_mat_evecs.H
        last_col = U.H * vecs_weighted[:, -1]
        correlation_mat_evals_sqrt = N.mat(N.diag(sing_vals**-1.0))
        correlation_mat = correlation_mat_evecs * \
            N.mat(N.diag(correlation_mat_evals)) * correlation_mat_evecs.H

        low_order_linear_map = N.mat(N.concatenate(
            (correlation_mat_evals_sqrt * correlation_mat_evecs.H * \
            correlation_mat[:, 1:], last_col), axis=1)) * \
            correlation_mat_evecs * correlation_mat_evals_sqrt
    else:
        if vecs.shape != adv_vecs.shape:
            raise ValueError(('vecs and adv_vecs are not the same shape.'))
        U, sing_vals, correlation_mat_evecs = util.svd(vecs_weighted)
        correlation_mat_evals_sqrt = N.mat(N.diag(sing_vals**-1.0))
        low_order_linear_map = U.H * adv_vecs_weighted * \
            correlation_mat_evecs * correlation_mat_evals_sqrt
        correlation_mat_evals = sing_vals**2
        correlation_mat = correlation_mat_evecs * \
            N.mat(N.diag(correlation_mat_evals)) * correlation_mat_evecs.H

    # Compute eigendecomposition of low-order linear map.
    ritz_vals, low_order_evecs = N.linalg.eig(low_order_linear_map)
    build_coeffs = correlation_mat_evecs *\
        correlation_mat_evals_sqrt * low_order_evecs *\
        N.diag(N.array(N.array(N.linalg.inv(
        low_order_evecs.H * low_order_evecs) * low_order_evecs.H *\
        correlation_mat_evals_sqrt * correlation_mat_evecs.H *
        correlation_mat[:, 0]).squeeze(), ndmin=1))
    mode_norms = N.diag(build_coeffs.H * correlation_mat * build_coeffs).real
    if (mode_norms < 0).any():
        print(
            'Warning: mode norms has negative values. This is often happens '
            'when the rank of the vector matrix is much less than the number '
            'of columns. Try using fewer vectors (fewer columns).')
    # For sequential data, the user will provide a vecs
    # whose length is one larger than the number of columns of the
    # build_coeffs matrix.
    if vecs.shape[1] - build_coeffs.shape[0] == 1:
        modes = vec_space.lin_combine(vecs[:, :-1],
                                      build_coeffs,
                                      coeff_mat_col_indices=mode_indices)
    # For a non-sequential dataset, user provides vecs
    # whose length is equal to the number of columns of build_coeffs
    elif vecs.shape[1] == build_coeffs.shape[0]:
        modes = vec_space.lin_combine(vecs,
                                      build_coeffs,
                                      coeff_mat_col_indices=mode_indices)
    # Raise an error if number of handles isn't one of the two cases above.
    else:
        raise ValueError(('Number of cols in vecs does not match '
                          'number of rows in build_coeffs matrix.'))

    if return_all:
        return modes, ritz_vals, mode_norms, build_coeffs
    else:
        return modes, ritz_vals, mode_norms
Exemplo n.º 7
0
def compute_POD_matrices_direct_method(vecs,
                                       mode_indices,
                                       inner_product_weights=None,
                                       return_all=False):
    """Computes POD modes with data in a matrix using the direct method.
    
    Args:
        ``vecs``: Matrix of vectors stacked as columns.
        
        ``mode_indices``: List of mode indices to compute.
            Examples are ``range(10)`` or ``[3, 0, 6, 8]``.

    Kwargs:
        ``inner_product_weights``: 1D array or matrix of inner product weights.
            It corresponds to :math:`W` in inner product :math:`v_1^* W v_2`.
        
        ``return_all``: Return more objects, see below. Default is false.
    
    Returns:
        ``modes``: Matrix with requested modes as columns.
        
        ``eigen_vals``: 1D array of eigenvalues. 
            These are the eigenvalues of the correlation matrix (:math:`X^* W X`), 
            and are also the squares of the singular values of :math:`X`. 
        
        If ``return_all`` is true, also returns:
               
        ``eigen_vecs``: Matrix of eigenvectors.
            These are the eigenvectors of correlation matrix (:math:`X^* W X`),
            and are also the right singular vectors of :math:`X`.
                
    The algorithm is
    
    1. SVD :math:`U E V^* = W^{1/2} X`
    2. Modes are :math:`W^{-1/2} U`
    
    where :math:`X`, :math:`W`, :math:`E`, :math:`V`, correspond to 
    ``vecs``, ``inner_product_weights``, ``eigen_vals**0.5``, 
    and ``eigen_vecs``, respectively.
       
    Since this method does not square the vectors and singular values,
    it is more accurate than taking the eigen decomposition of :math:`X^* W X`,
    as in the method of snapshots (:py:func:`compute_POD_arrays_direct_method`). 
    However, this method is slower when :math:`X` has more rows than columns, 
    i.e. there are fewer vectors than elements in each vector.
    """
    if _parallel.is_distributed():
        raise RuntimeError('Cannot run in parallel.')
    vecs = util.make_mat(vecs)
    if inner_product_weights is None:
        modes, sing_vals, eigen_vecs = util.svd(vecs)
        modes = modes[:, mode_indices]

    elif inner_product_weights.ndim == 1:
        sqrt_weights = inner_product_weights**0.5
        vecs_weighted = N.mat(N.diag(sqrt_weights)) * vecs
        modes_weighted, sing_vals, eigen_vecs = util.svd(vecs_weighted)
        modes = N.mat(N.diag(sqrt_weights**
                             -1.0)) * modes_weighted[:, mode_indices]

    elif inner_product_weights.ndim == 2:
        if inner_product_weights.shape[0] > 500:
            print 'Warning: Cholesky decomposition could be time consuming.'
        sqrt_weights = N.linalg.cholesky(inner_product_weights).H
        vecs_weighted = sqrt_weights * vecs
        modes_weighted, sing_vals, eigen_vecs = util.svd(vecs_weighted)
        modes = N.linalg.solve(sqrt_weights, modes_weighted[:, mode_indices])
        #inv_sqrt_weights = N.linalg.inv(sqrt_weights)
        #modes = inv_sqrt_weights.dot(modes_weighted[:, mode_indices])

    eigen_vals = sing_vals**2

    if return_all:
        return modes, eigen_vals, eigen_vecs
    else:
        return modes, eigen_vals
Exemplo n.º 8
0
def compute_BPOD_matrices(direct_vecs, adjoint_vecs, 
    direct_mode_indices, adjoint_mode_indices, inner_product_weights=None, 
    return_all=False):
    """Computes BPOD modes with data in a matrix.
        
    Args:
        ``direct_vecs``: Matrix with direct vecs as columns (:math:`X`).
    
        ``adjoint_vecs``: Matrix with adjoint vecs as columns (:math:`Y`).

        ``direct_mode_indices``: List of direct mode indices to compute. 
          Examples are ``range(10)`` or ``[3, 0, 6, 8]``. 

        ``adjoint_mode_indices``: List of adjoint mode indices to compute. 
          Examples are ``range(10)`` or ``[3, 0, 6, 8]``. 

    Kwargs:
        ``inner_product_weights``: 1D array or matrix of inner product weights.
            It corresponds to :math:`W` in inner product :math:`v_1^* W v_2`.
        
        ``return_all``: Return more objects, see below. Default is false.
        
    Returns:
        ``direct_modes``: Matrix with direct modes as columns.
        
        ``adjoint_modes``: Matrix with adjoint modes as columns.

        ``sing_vals``: 1D array of singular values of Hankel mat (:math:`E`).
        
        If ``return_all`` is true, then also returns:
        
        ``L_sing_vecs``: Matrix of left singular vectors of Hankel mat 
        (:math:`U`).
    
        ``R_sing_vecs``: Matrix of right singular vectors of Hankel mat
        (:math:`V`).

        ``Hankel_mat``: Hankel matrix (:math:`Y^* W X`).
        
    See also :py:class:`BPODHandles`.
    """
    if _parallel.is_distributed():
        raise RuntimeError('Cannot run in parallel.')
    vec_space = VectorSpaceMatrices(weights=inner_product_weights)
    direct_vecs = util.make_mat(direct_vecs)
    adjoint_vecs = util.make_mat(adjoint_vecs)
    
    #Hankel_mat = vec_space.compute_inner_product_mat(adjoint_vecs, 
    #    direct_vecs)
    first_adjoint_all_direct = vec_space.compute_inner_product_mat(adjoint_vecs[:,0], 
        direct_vecs)
    all_adjoint_last_direct = vec_space.compute_inner_product_mat(adjoint_vecs, 
        direct_vecs[:,-1])
    Hankel_mat = util.Hankel(first_adjoint_all_direct, all_adjoint_last_direct)
    L_sing_vecs, sing_vals, R_sing_vecs = util.svd(Hankel_mat)
    #print 'diff in Hankels',Hankel_mat - Hankel_mat2
    #Hankel_mat = Hankel_mat2
    sing_vals_sqrt_mat = N.mat(N.diag(sing_vals**-0.5))
    direct_build_coeff_mat = R_sing_vecs * sing_vals_sqrt_mat
    direct_mode_array = vec_space.lin_combine(direct_vecs, 
        direct_build_coeff_mat, coeff_mat_col_indices=direct_mode_indices)
     
    adjoint_build_coeff_mat = L_sing_vecs * sing_vals_sqrt_mat
    adjoint_mode_array = vec_space.lin_combine(adjoint_vecs,
        adjoint_build_coeff_mat, coeff_mat_col_indices=adjoint_mode_indices)
    
    if return_all:
        return direct_mode_array, adjoint_mode_array, sing_vals, L_sing_vecs, \
            R_sing_vecs, Hankel_mat
    else:
        return direct_mode_array, adjoint_mode_array, sing_vals
Exemplo n.º 9
0
def compute_BPOD_matrices(direct_vecs,
                          adjoint_vecs,
                          direct_mode_indices,
                          adjoint_mode_indices,
                          inner_product_weights=None,
                          return_all=False):
    """Computes BPOD modes with data in a matrix.
        
    Args:
        ``direct_vecs``: Matrix with direct vecs as columns (:math:`X`).
    
        ``adjoint_vecs``: Matrix with adjoint vecs as columns (:math:`Y`).

        ``direct_mode_indices``: List of direct mode indices to compute. 
          Examples are ``range(10)`` or ``[3, 0, 6, 8]``. 

        ``adjoint_mode_indices``: List of adjoint mode indices to compute. 
          Examples are ``range(10)`` or ``[3, 0, 6, 8]``. 

    Kwargs:
        ``inner_product_weights``: 1D array or matrix of inner product weights.
            It corresponds to :math:`W` in inner product :math:`v_1^* W v_2`.
        
        ``return_all``: Return more objects, see below. Default is false.
        
    Returns:
        ``direct_modes``: Matrix with direct modes as columns.
        
        ``adjoint_modes``: Matrix with adjoint modes as columns.

        ``sing_vals``: 1D array of singular values of Hankel mat (:math:`E`).
        
        If ``return_all`` is true, then also returns:
        
        ``L_sing_vecs``: Matrix of left singular vectors of Hankel mat 
        (:math:`U`).
    
        ``R_sing_vecs``: Matrix of right singular vectors of Hankel mat
        (:math:`V`).

        ``Hankel_mat``: Hankel matrix (:math:`Y^* W X`).
        
    See also :py:class:`BPODHandles`.
    """
    if _parallel.is_distributed():
        raise RuntimeError('Cannot run in parallel.')
    vec_space = VectorSpaceMatrices(weights=inner_product_weights)
    direct_vecs = util.make_mat(direct_vecs)
    adjoint_vecs = util.make_mat(adjoint_vecs)

    #Hankel_mat = vec_space.compute_inner_product_mat(adjoint_vecs,
    #    direct_vecs)
    first_adjoint_all_direct = vec_space.compute_inner_product_mat(
        adjoint_vecs[:, 0], direct_vecs)
    all_adjoint_last_direct = vec_space.compute_inner_product_mat(
        adjoint_vecs, direct_vecs[:, -1])
    Hankel_mat = util.Hankel(first_adjoint_all_direct, all_adjoint_last_direct)
    L_sing_vecs, sing_vals, R_sing_vecs = util.svd(Hankel_mat)
    #print 'diff in Hankels',Hankel_mat - Hankel_mat2
    #Hankel_mat = Hankel_mat2
    sing_vals_sqrt_mat = N.mat(N.diag(sing_vals**-0.5))
    direct_build_coeff_mat = R_sing_vecs * sing_vals_sqrt_mat
    direct_mode_array = vec_space.lin_combine(
        direct_vecs,
        direct_build_coeff_mat,
        coeff_mat_col_indices=direct_mode_indices)

    adjoint_build_coeff_mat = L_sing_vecs * sing_vals_sqrt_mat
    adjoint_mode_array = vec_space.lin_combine(
        adjoint_vecs,
        adjoint_build_coeff_mat,
        coeff_mat_col_indices=adjoint_mode_indices)

    if return_all:
        return direct_mode_array, adjoint_mode_array, sing_vals, L_sing_vecs, \
            R_sing_vecs, Hankel_mat
    else:
        return direct_mode_array, adjoint_mode_array, sing_vals
Exemplo n.º 10
0
def compute_DMD_matrices_direct_method(vecs, mode_indices, 
    adv_vecs=None, inner_product_weights=None, return_all=False):
    """Dynamic Mode Decomposition/Koopman Mode Decomposition with data in a
    matrix, using a direct method.

    Args:
        ``vecs``: Matrix with vectors as columns.
    
        ``mode_indices``: List of mode numbers, ``range(10)`` or ``[3, 0, 5]``.
    
    Kwargs:
        ``adv_vecs``: Matrix with ``vecs`` advanced in time as columns.
            If not provided, then it is assumed that the vectors are a 
            sequential time-series. Thus ``vecs`` becomes ``vecs[:-1]`` and
            ``adv_vecs`` becomes ``vecs[1:]``.
            
        ``inner_product_weights``: 1D or Matrix of inner product weights.
            It corresponds to :math:`W` in inner product :math:`v_1^* W v_2`.

        ``return_all``: Return more objects, see below. Default is false.

    Returns:
        ``modes``: Matrix with requested modes as columns.

        ``ritz_vals``: 1D array of Ritz values.
        
        ``mode_norms``: 1D array of mode norms.

        If ``return_all`` is true, also returns:

        ``build_coeffs``: Matrix of build coefficients for modes.
        
    This method does not square the matrix of vectors as in the method of
    snapshots (:py:func:`compute_DMD_matrices_snaps_method`). It's slightly 
    more accurate, but slower when the number of elements in a vector is 
    more than the number of vectors (more rows than columns in ``vecs``).
    """
    if _parallel.is_distributed():
        raise RuntimeError('Cannot run in parallel.')
    vec_space = VectorSpaceMatrices(weights=inner_product_weights)
    vecs = util.make_mat(vecs)
    if adv_vecs is not None:
        adv_vecs = util.make_mat(adv_vecs)
    
    if inner_product_weights is None:
        vecs_weighted = vecs
        if adv_vecs is not None:
            adv_vecs_weighted = adv_vecs
    elif inner_product_weights.ndim == 1:
        sqrt_weights = N.mat(N.diag(inner_product_weights**0.5))
        vecs_weighted = sqrt_weights * vecs
        if adv_vecs is not None:
            adv_vecs_weighted = sqrt_weights * adv_vecs
    elif inner_product_weights.ndim == 2:
        if inner_product_weights.shape[0] > 500:
            print 'Warning: Cholesky decomposition could be time consuming.'
        sqrt_weights = N.mat(N.linalg.cholesky(inner_product_weights)).H
        vecs_weighted = sqrt_weights * vecs
        if adv_vecs is not None:
            adv_vecs_weighted = sqrt_weights * adv_vecs
    
    # Compute low-order linear map for sequential snapshot set.  This takes
    # advantage of the fact that for a sequential dataset, the unadvanced
    # and advanced vectors overlap.
    if adv_vecs is None:        
        U, sing_vals, correlation_mat_evecs = util.svd(vecs_weighted[:,:-1])
        correlation_mat_evals = sing_vals**2
        correlation_mat = correlation_mat_evecs * \
            N.mat(N.diag(correlation_mat_evals)) * correlation_mat_evecs.H
        last_col = U.H * vecs_weighted[:,-1]
        correlation_mat_evals_sqrt = N.mat(N.diag(sing_vals**-1.0))
        correlation_mat = correlation_mat_evecs * \
            N.mat(N.diag(correlation_mat_evals)) * correlation_mat_evecs.H

        low_order_linear_map = N.mat(N.concatenate(
            (correlation_mat_evals_sqrt * correlation_mat_evecs.H * \
            correlation_mat[:, 1:], last_col), axis=1)) * \
            correlation_mat_evecs * correlation_mat_evals_sqrt
    else: 
        if vecs.shape != adv_vecs.shape:
            raise ValueError(('vecs and adv_vecs are not the same shape.'))
        U, sing_vals, correlation_mat_evecs = util.svd(vecs_weighted)
        correlation_mat_evals_sqrt = N.mat(N.diag(sing_vals**-1.0))
        low_order_linear_map = U.H * adv_vecs_weighted * \
            correlation_mat_evecs * correlation_mat_evals_sqrt   
        correlation_mat_evals = sing_vals**2
        correlation_mat = correlation_mat_evecs * \
            N.mat(N.diag(correlation_mat_evals)) * correlation_mat_evecs.H

    # Compute eigendecomposition of low-order linear map.
    ritz_vals, low_order_evecs = N.linalg.eig(low_order_linear_map)
    build_coeffs = correlation_mat_evecs *\
        correlation_mat_evals_sqrt * low_order_evecs *\
        N.diag(N.array(N.array(N.linalg.inv(
        low_order_evecs.H * low_order_evecs) * low_order_evecs.H *\
        correlation_mat_evals_sqrt * correlation_mat_evecs.H * 
        correlation_mat[:, 0]).squeeze(), ndmin=1))
    mode_norms = N.diag(build_coeffs.H * correlation_mat * build_coeffs).real
    if (mode_norms < 0).any():
        print ('Warning: mode norms has negative values. This is often happens '
            'when the rank of the vector matrix is much less than the number '
            'of columns. Try using fewer vectors (fewer columns).')
    # For sequential data, the user will provide a vecs 
    # whose length is one larger than the number of columns of the 
    # build_coeffs matrix. 
    if vecs.shape[1] - build_coeffs.shape[0] == 1:
        modes = vec_space.lin_combine(vecs[:, :-1], 
            build_coeffs, coeff_mat_col_indices=mode_indices)
    # For a non-sequential dataset, user provides vecs
    # whose length is equal to the number of columns of build_coeffs
    elif vecs.shape[1] == build_coeffs.shape[0]:
        modes = vec_space.lin_combine(vecs, 
            build_coeffs, coeff_mat_col_indices=mode_indices)
    # Raise an error if number of handles isn't one of the two cases above.
    else:
        raise ValueError(('Number of cols in vecs does not match '
            'number of rows in build_coeffs matrix.'))
    
    if return_all:
        return modes, ritz_vals, mode_norms, build_coeffs    
    else:
        return modes, ritz_vals, mode_norms