Esempio n. 1
0
def pod(A, modes=None, product=None, tol=None, symmetrize=None, orthonormalize=False,
        check=None, check_tol=None):

    assert isinstance(A, VectorArrayInterface)
    assert len(A) > 0
    assert modes is None or modes <= len(A)
    assert product is None or isinstance(product, OperatorInterface)

    tol = defaults.pod_tol if tol is None else tol
    symmetrize = defaults.pod_symmetrize if symmetrize is None else symmetrize
    orthonormalize = defaults.pod_orthonormalize if orthonormalize is None else orthonormalize
    check = defaults.pod_check if check is None else check
    check_tol = defaults.pod_check_tol if check_tol is None else check_tol


    B = A.gramian() if product is None else product.apply2(A, A, pairwise=False)

    if symmetrize:     # according to rbmatlab this is necessary due to rounding
        B = B + B.T
        B *= 0.5

    eigvals = None if modes is None else (len(B) - modes, len(B) - 1)

    EVALS, EVECS = eigh(B, overwrite_a=True, turbo=True, eigvals=eigvals)
    EVALS = EVALS[::-1]
    EVECS = EVECS.T[::-1, :] # is this a view?

    last_above_tol = np.where(EVALS >= tol)[0][-1]
    EVALS = EVALS[:last_above_tol + 1]
    EVECS = EVECS[:last_above_tol + 1]

    if len(EVALS) == 0:
        return type(A).empty(A.dim)

    POD = A.lincomb(EVECS / np.sqrt(EVALS[:, np.newaxis]))

    if orthonormalize:
        POD = gram_schmidt(POD, product=product)

    if check:
        if not product and not float_cmp_all(POD.dot(POD, pairwise=False), np.eye(len(POD)), check_tol):
            err = np.max(np.abs(POD.dot(POD, pairwise=False) - np.eye(len(POD))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))
        elif product and not float_cmp_all(product.apply2(POD, POD, pairwise=False), np.eye(len(POD)), check_tol):
            err = np.max(np.abs(product.apply2(POD, POD, pairwise=False) - np.eye(len(POD))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))

    return POD
Esempio n. 2
0
 def allclose(self, mu):
     assert isinstance(mu, Parameter)
     if set(self.keys()) != set(mu.keys()):
         return False
     if not all(float_cmp_all(v, mu[k]) for k, v in self.iteritems()):
         return False
     else:
         return True
Esempio n. 3
0
    def allclose(self, mu):
        '''Compare to |Parameters| using :meth:`~pymor.tools.floatcmp.float_cmp_all`.

        Parameters
        ----------
        mu
            The |Parameter| with which to compare.

        Returns
        -------
        `True` if both |Parameters| have the same |ParameterType| and all parameter
        components are almost equal, else `False`.
        '''
        assert isinstance(mu, Parameter)
        if self.viewkeys() != mu.viewkeys():
            return False
        elif not all(float_cmp_all(v, mu[k]) for k, v in self.iteritems()):
            return False
        else:
            return True
Esempio n. 4
0
def numpy_trivial_basis_extension(basis, U):
    '''Trivially extend basis by just adding the new vector.

    We check that the new vector is not already contained in the basis, but we do
    not check for linear independence.

    Parameters
    ----------
    basis
        The basis to extend.
    U
        The new basis vector.

    Returns
    -------
    The new basis.

    Raises
    ------
    ExtensionError
        Is raised if U is already contained in basis.
    '''
    assert isinstance(U, NumpyVectorArray)
    if basis is None:
        return U
    assert isinstance(basis, NumpyVectorArray)
    basis = basis.data
    U = U.data

    if not all(not float_cmp_all(B, U) for B in basis):
        raise ExtensionError

    new_basis = np.empty((basis.shape[0] + 1, basis.shape[1]))
    new_basis[:-1, :] = basis
    new_basis[-1, :] = U

    return NumpyVectorArray(new_basis)
Esempio n. 5
0
 def almost_equal(self, other, rtol=None, atol=None):
     assert self.dim == other.dim
     return float_cmp_all(self._array, other._array, rtol=rtol, atol=atol)
Esempio n. 6
0
def gram_schmidt(A, product=None, tol=1e-14, offset=0, find_duplicates=True,
                 reiterate=True, reiteration_threshold=1e-1, check=True, check_tol=1e-3,
                 copy=False):
    """Orthonormalize a |VectorArray| using the Gram-Schmidt algorithm.

    Parameters
    ----------
    A
        The |VectorArray| which is to be orthonormalized.
    product
        The scalar product w.r.t. which to orthonormalize, given as a linear
        |Operator|. If `None` the Euclidean product is used.
    tol
        Tolerance to determine a linear dependent row.
    offset
        Assume that the first `offset` vectors are already orthogonal and start the
        algorithm at the `offset + 1`-th vector.
    find_duplicates
        If `True`, eliminate duplicate vectors before the main loop.
    reiterate
        If `True`, orthonormalize again if the norm of the orthogonalized vector is
        much smaller than the norm of the original vector.
    reiteration_threshold
        If `reiterate` is `True`, re-orthonormalize if the ratio between the norms of
        the orthogonalized vector and the original vector is smaller than this value.
    check
        If `True`, check if the resulting VectorArray is really orthonormal.
    check_tol
        Tolerance for the check.
    copy
        If `True`, create a copy of `A` instead of working directly on `A`.


    Returns
    -------
    The orthonormalized |VectorArray|.
    """

    logger = getLogger('pymor.la.gram_schmidt.gram_schmidt')

    if copy:
        A = A.copy()

    # find duplicate vectors since in some circumstances these cannot be detected in the main loop
    # (is this really needed or is in this cases the tolerance poorly chosen anyhow)
    if find_duplicates:
        i = 0
        while i < len(A):
            duplicates = A.almost_equal(A, ind=i, o_ind=np.arange(max(offset, i + 1), len(A)))
            if np.any(duplicates):
                A.remove(np.where(duplicates)[0])
                logger.info("Removing duplicate vectors")
            i += 1

    # main loop
    remove = []
    norm = None
    for i in xrange(offset, len(A)):
        # first calculate norm
        if product is None:
            oldnorm = A.l2_norm(ind=i)[0]
        else:
            oldnorm = np.sqrt(product.apply2(A, A, V_ind=i, U_ind=i, pairwise=True))[0]

        if float_cmp_all(oldnorm, 0):
            logger.info("Removing null vector {}".format(i))
            remove.append(i)
            continue

        if i == 0:
            A.scal(1/oldnorm, ind=0)

        else:
            first_iteration = True

            # If reiterate is True, reiterate as long as the norm of the vector changes
            # strongly during orthonormalization (due to Andreas Buhr).
            while first_iteration or reiterate and norm < reiteration_threshold:
                # this loop assumes that oldnorm is the norm of the ith vector when entering

                if first_iteration:
                    first_iteration = False
                else:
                    logger.info('Orthonormalizing vector {} again'.format(i))

                # orthogonalize to all vectors left
                for j in xrange(i):
                    if j in remove:
                        continue
                    if product is None:
                        p = A.dot(A, ind=i, o_ind=j, pairwise=True)[0]
                    else:
                        p = product.apply2(A, A, V_ind=i, U_ind=j, pairwise=True)[0]
                    A.axpy(-p, A, ind=i, x_ind=j)

                # calculate new norm
                if product is None:
                    norm = A.l2_norm(ind=i)[0]
                else:
                    norm = np.sqrt(product.apply2(A, A, V_ind=i, U_ind=i, pairwise=True))[0]

                # remove vector if it got too small:
                if norm / oldnorm < tol:
                    logger.info("Removing linear dependent vector {}".format(i))
                    remove.append(i)
                    break

                A.scal(1 / norm, ind=i)
                oldnorm = 1.

    if remove:
        A.remove(remove)

    if check:
        if not product and not float_cmp_all(A.dot(A, pairwise=False), np.eye(len(A)),
                                             atol=check_tol, rtol=0.):
            err = np.max(np.abs(A.dot(A, pairwise=False) - np.eye(len(A))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))
        elif product and not float_cmp_all(product.apply2(A, A, pairwise=False), np.eye(len(A)),
                                           atol=check_tol, rtol=0.):
            err = np.max(np.abs(product.apply2(A, A, pairwise=False) - np.eye(len(A))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))

    return A
Esempio n. 7
0
def pod(A, modes=None, product=None, tol=None, symmetrize=None, orthonormalize=None,
        check=None, check_tol=None):
    '''Proper orthogonal decomposition of `A`.

    If the |VectorArray| `A` is viewed as a map ::

        A: R^(len(A)) ---> R^(dim(A))

    then the return value of this method is simply the array of left-singular
    vectors of the singular value decomposition of `A`, where the scalar product
    on R^(dim(A) is given by `product` and the scalar product on R^(len(A)) is
    the Euclidean product.

    Parameters
    ----------
    A
        The |VectorArray| for which the POD is to be computed.
    modes
        If not `None` only the first `modes` POD modes (singular vectors) are
        returned.
    products
        Scalar product given as a linear |Operator| w.r.t. compute the POD.
    tol
        Squared singular values smaller than this value are ignored. If `None`,
        the `pod_tol` |default| value is used.
    symmetrize
        If `True` symmetrize the gramian again before proceeding. If `None`,
        the `pod_symmetrize` |default| value is chosen.
    orthonormalize
        If `True`, orthonormalize the computed POD modes again using
        :func:`la.gram_schmidt.gram_schmidt`. If `None`, the `pod_orthonormalize`
        |default| value is used.
    check
        If `True`, check the computed POD modes for orthonormality. If `None`,
        the `pod_check` |default| value is used.
    check_tol
        Tolerance for the orthonormality check. If `None`, the `pod_check_tol`
        |default| value is used.

    Returns
    -------
    |VectorArray| of POD modes.

    '''

    assert isinstance(A, VectorArrayInterface)
    assert len(A) > 0
    assert modes is None or modes <= len(A)
    assert product is None or isinstance(product, OperatorInterface)

    tol = defaults.pod_tol if tol is None else tol
    symmetrize = defaults.pod_symmetrize if symmetrize is None else symmetrize
    orthonormalize = defaults.pod_orthonormalize if orthonormalize is None else orthonormalize
    check = defaults.pod_check if check is None else check
    check_tol = defaults.pod_check_tol if check_tol is None else check_tol

    B = A.gramian() if product is None else product.apply2(A, A, pairwise=False)

    if symmetrize:     # according to rbmatlab this is necessary due to rounding
        B = B + B.T
        B *= 0.5

    eigvals = None if modes is None else (len(B) - modes, len(B) - 1)

    EVALS, EVECS = eigh(B, overwrite_a=True, turbo=True, eigvals=eigvals)
    EVALS = EVALS[::-1]
    EVECS = EVECS.T[::-1, :]  # is this a view? yes it is!

    above_tol = np.where(EVALS >= tol)[0]
    if len(above_tol) == 0:
        return type(A).empty(A.dim)
    last_above_tol = above_tol[-1]

    EVALS = EVALS[:last_above_tol + 1]
    EVECS = EVECS[:last_above_tol + 1]

    POD = A.lincomb(EVECS / np.sqrt(EVALS[:, np.newaxis]))

    if orthonormalize:
        POD = gram_schmidt(POD, product=product, copy=False)

    if check:
        if not product and not float_cmp_all(POD.dot(POD, pairwise=False), np.eye(len(POD)), check_tol):
            err = np.max(np.abs(POD.dot(POD, pairwise=False) - np.eye(len(POD))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))
        elif product and not float_cmp_all(product.apply2(POD, POD, pairwise=False), np.eye(len(POD)), check_tol):
            err = np.max(np.abs(product.apply2(POD, POD, pairwise=False) - np.eye(len(POD))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))

    return POD
Esempio n. 8
0
def numpy_gram_schmidt(A, product=None, tol=None, row_offset=0, find_row_duplicates=True, find_col_duplicates=False,
                       check=None, check_tol=None):
    '''Orthonormnalize a matrix using the Gram-Schmidt algorithm.

    Parameters
    ----------
    A
        The matrix which is to be orthonormalized.
    product
        The scalar product w.r.t. which to orthonormalize. Either a `DiscreteLinearOperator`
        or a square matrix.
    tol
        Tolerance to determine a linear dependent row.
    row_offset
        Assume that the first `row_offset` rows are already orthogonal and start the
        algorithm at the `row_offset + 1`-th row.
    find_row_duplicates
        If `True`, eliminate duplicate rows before the main loop.
    find_col_duplicates
        If `True`, eliminate duplicate columns before the main loop.
    check
        If `True`, check if the resulting matrix is really orthonormal. If `None`, use
        `defaults.gram_schmidt_check`.
    check_tol
        Tolerance for the check. If `None`, `defaults.gram_schmidt_check_tol` is used.


    Returns
    -------
    The orthonormalized matrix.
    '''

    A = A.copy()
    tol = defaults.gram_schmidt_tol if tol is None else tol
    check = defaults.gram_schmidt_tol if check is None else check
    check_tol = check_tol or defaults.gram_schmidt_check_tol

    # find duplicate rows since in some circumstances these cannot be detected in the main loop
    # (is this really needed or is in this cases the tolerance poorly chosen anyhow)
    if find_row_duplicates:
        for i in xrange(A.shape[0]):
            for j in xrange(max(row_offset, i + 1), A.shape[0]):
                if float_cmp_all(A[i], A[j]):
                    A[j] = 0

    # find duplicate columns
    if find_col_duplicates:
        for i in xrange(A.shape[1]):
            for j in xrange(i, A.shape[1]):
                if float_cmp_all(A[:, i], A[:, j]):
                    A[:, j] = 0

    # main loop
    for i in xrange(A.shape[0]):

        if i >= row_offset:
            if product is None:
                norm = np.sqrt(np.sum(A[i] ** 2))
            else:
                norm = np.sqrt(product.apply2(A[i], A[i]))

            if norm < tol:
                A[i] = 0
            else:
                A[i] = A[i] / norm

        j = max(row_offset, i + 1)
        if product is None:
            p = np.sum(A[j:] * A[i], axis=-1)
        else:
            p = product.apply2(A[j:], A[i], pairwise=False)

        A[j:] -= p[..., np.newaxis] * A[i]

    rows = np.logical_not(np.all(A == 0, axis=-1))
    A = A[rows]

    if check:
        if not float_cmp_all(A.dot(A.T), np.eye(A.shape[0]), check_tol):
            err = np.max(np.abs(A.dot(A.T) - np.eye(A.shape[0])))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))

    return A
Esempio n. 9
0
def gram_schmidt(A, product=None, tol=None, offset=0, find_duplicates=True,
                 check=None, check_tol=None):
    '''Orthonormnalize a matrix using the Gram-Schmidt algorithm.

    Parameters
    ----------
    A
        The VectorArray which is to be orthonormalized.
    product
        The scalar product w.r.t. which to orthonormalize.
    tol
        Tolerance to determine a linear dependent row.
    offset
        Assume that the first `offset` vectors are already orthogonal and start the
        algorithm at the `offset + 1`-th vector.
    find_duplicates
        If `True`, eliminate duplicate vectors before the main loop.
    check
        If `True`, check if the resulting VectorArray is really orthonormal. If `None`, use
        `defaults.gram_schmidt_check`.
    check_tol
        Tolerance for the check. If `None`, `defaults.gram_schmidt_check_tol` is used.


    Returns
    -------
    The orthonormalized matrix.
    '''

    tol = defaults.gram_schmidt_tol if tol is None else tol
    check = defaults.gram_schmidt_tol if check is None else check
    check_tol = check_tol or defaults.gram_schmidt_check_tol

    # find duplicate vectors since in some circumstances these cannot be detected in the main loop
    # (is this really needed or is in this cases the tolerance poorly chosen anyhow)
    if find_duplicates:
        for i in xrange(len(A)):
            duplicates = A.almost_equal(A, ind=[i], o_ind=xrange(max(offset, i + 1), len(A)))
            if np.any(duplicates):
                A.remove(np.where(duplicates))

    # main loop
    i = 0
    remove = []
    while i < len(A):

        if i >= offset:
            if product is None:
                norm = A.l2_norm(ind=[i])[0]
            else:
                norm = np.sqrt(product.apply2(A, A, ind=[i], o_ind=[i], pairwise=True))[0]

            if norm < tol:
                remove.append(i)
                continue
            else:
                A.iadd_mult(None, factor=1/norm, o_factor=0, ind=[i])

        for j in xrange(max(offset, i + 1), len(A)):
            if product is None:
                p = A.prod(A, ind=[j], o_ind=[i], pairwise=True)[0]
            else:
                p = product.apply2(A, A, V_ind=[j], U_ind=[i], pairwise=True)[0]
            A.iadd_mult(A, o_factor=-p, ind=[j], o_ind=[i])

        i += 1

    if remove:
        A.remove(remove)

    if check:
        if not float_cmp_all(A.prod(A, pairwise=False), np.eye(len(A)), check_tol):
            err = np.max(np.abs(A.prod(A, pairwise=False) - np.eye(len(A))))
            raise AccuracyError('result not orthogonal (max err={})'.format(err))

    return A