Exemple #1
0
    def apply_inverse(self, V, mu=None, least_squares=False):
        from pymor.operators.constructions import FixedParameterOperator
        assembled_op = self.assemble(mu)
        if assembled_op != self and not isinstance(assembled_op, FixedParameterOperator):
            return assembled_op.apply_inverse(V, least_squares=least_squares)
        elif self.linear:
            options = self.solver_options.get('inverse') if self.solver_options else None
            return genericsolvers.apply_inverse(assembled_op, V, options=options, least_squares=least_squares)
        else:
            from pymor.algorithms.newton import newton
            from pymor.core.exceptions import NewtonError

            options = self.solver_options.get('inverse') if self.solver_options else None
            if options:
                if isinstance(options, str):
                    assert options == 'newton'
                    options = {}
                else:
                    assert options['type'] == 'newton'
                    options = options.copy()
                    options.pop('type')
            else:
                options = {}
            options['least_squares'] = least_squares

            R = V.empty(reserve=len(V))
            for i in range(len(V)):
                try:
                    R.append(newton(self, V[i], mu=mu, **options)[0])
                except NewtonError as e:
                    raise InversionError(e)
            return R
Exemple #2
0
    def apply_inverse(self, V, ind=None, mu=None, least_squares=False):
        assert V in self.range
        assert V.check_ind(ind)
        assert not self.functional and not self.vector

        if V.dim == 0:
            if self.source.dim == 0 and least_squares:
                return ListVectorArray([NumpyVector(np.zeros(0), copy=False) for _ in range(V.len_ind(ind))],
                                       subtype=self.source.subtype)
            else:
                raise InversionError

        options = (self.solver_options.get('inverse') if self.solver_options else
                   'least_squares' if least_squares else
                   None)

        if options and not least_squares:
            solver_type = options if isinstance(options, str) else options['type']
            if solver_type.startswith('least_squares'):
                self.logger.warn('Least squares solver selected but "least_squares == False"')

        if ind is None:
            vectors = V._list
        elif isinstance(ind, Number):
            vectors = [V._list[ind]]
        else:
            vectors = (V._list[i] for i in ind)

        try:
            return ListVectorArray([NumpyVector(_apply_inverse(self._matrix, v._array.reshape((1, -1)),
                                                               options=options).ravel(),
                                                copy=False)
                                    for v in vectors],
                                   subtype=self.source.subtype)
        except InversionError as e:
            if least_squares and options:
                solver_type = options if isinstance(options, str) else options['type']
                if not solver_type.startswith('least_squares'):
                    msg = str(e) \
                        + '\nNote: linear solver was selected for solving least squares problem (maybe not invertible?)'
                    raise InversionError(msg)
            raise e
Exemple #3
0
    def apply_inverse(self, V, ind=None, mu=None, least_squares=False):
        assert V in self.range
        assert V.check_ind(ind)

        if V.dim == 0:
            if self.source.dim == 0 or least_squares:
                return NumpyVectorArray(
                    np.zeros((V.len_ind(ind), self.source.dim)))
            else:
                raise InversionError

        options = (self.solver_options.get('inverse') if self.solver_options
                   else 'least_squares' if least_squares else None)

        if options and not least_squares:
            solver_type = options if isinstance(options,
                                                str) else options['type']
            if solver_type.startswith('least_squares'):
                self.logger.warn(
                    'Least squares solver selected but "least_squares == False"'
                )

        V = V.data if ind is None else \
            V.data[ind] if hasattr(ind, '__len__') else V.data[ind:ind + 1]

        try:
            return NumpyVectorArray(_apply_inverse(self._matrix,
                                                   V,
                                                   options=options),
                                    copy=False)
        except InversionError as e:
            if least_squares and options:
                solver_type = options if isinstance(options,
                                                    str) else options['type']
                if not solver_type.startswith('least_squares'):
                    msg = str(e) \
                        + '\nNote: linear solver was selected for solving least squares problem (maybe not invertible?)'
                    raise InversionError(msg)
            raise e
Exemple #4
0
    def apply_inverse(self,
                      V,
                      mu=None,
                      initial_guess=None,
                      least_squares=False):
        """Apply the inverse operator.

        Parameters
        ----------
        V
            |VectorArray| of vectors to which the inverse operator is applied.
        mu
            The |parameter values| for which to evaluate the inverse operator.
        initial_guess
            |VectorArray| with the same length as `V` containing initial guesses
            for the solution.  Some implementations of `apply_inverse` may
            ignore this parameter.  If `None` a solver-dependent default is used.
        least_squares
            If `True`, solve the least squares problem::

                u = argmin ||op(u) - v||_2.

            Since for an invertible operator the least squares solution agrees
            with the result of the application of the inverse operator,
            setting this option should, in general, have no effect on the result
            for those operators. However, note that when no appropriate
            |solver_options| are set for the operator, most implementations
            will choose a least squares solver by default which may be
            undesirable.

        Returns
        -------
        |VectorArray| of the inverse operator evaluations.

        Raises
        ------
        InversionError
            The operator could not be inverted.
        """
        assert V in self.range
        assert initial_guess is None or initial_guess in self.source and len(
            initial_guess) == len(V)
        from pymor.operators.constructions import FixedParameterOperator
        assembled_op = self.assemble(mu)
        if assembled_op != self and not isinstance(assembled_op,
                                                   FixedParameterOperator):
            return assembled_op.apply_inverse(V,
                                              initial_guess=initial_guess,
                                              least_squares=least_squares)
        elif self.linear:
            options = self.solver_options.get(
                'inverse') if self.solver_options else None
            return genericsolvers.apply_inverse(assembled_op,
                                                V,
                                                initial_guess=initial_guess,
                                                options=options,
                                                least_squares=least_squares)
        else:
            from pymor.algorithms.newton import newton
            from pymor.core.exceptions import NewtonError

            options = self.solver_options.get(
                'inverse') if self.solver_options else None
            if options:
                if isinstance(options, str):
                    assert options == 'newton'
                    options = {}
                else:
                    assert options['type'] == 'newton'
                    options = options.copy()
                    options.pop('type')
            else:
                options = {}
            options['least_squares'] = least_squares

            with self.logger.block(
                    'Solving nonlinear problem using newton algorithm ...'):
                R = V.empty(reserve=len(V))
                for i in range(len(V)):
                    try:
                        R.append(
                            newton(self,
                                   V[i],
                                   initial_guess=initial_guess[i]
                                   if initial_guess is not None else None,
                                   mu=mu,
                                   **options)[0])
                    except NewtonError as e:
                        raise InversionError(e)
            return R
Exemple #5
0
    def apply_inverse(self,
                      V,
                      mu=None,
                      least_squares=False,
                      check_finite=True,
                      default_sparse_solver_backend='scipy'):
        """Apply the inverse operator.

        Parameters
        ----------
        V
            |VectorArray| of vectors to which the inverse operator is applied.
        mu
            The |Parameter| for which to evaluate the inverse operator.
        least_squares
            If `True`, solve the least squares problem::

                u = argmin ||op(u) - v||_2.

            Since for an invertible operator the least squares solution agrees
            with the result of the application of the inverse operator,
            setting this option should, in general, have no effect on the result
            for those operators. However, note that when no appropriate
            |solver_options| are set for the operator, most implementations
            will choose a least squares solver by default which may be
            undesirable.
        check_finite
            Test if solution only contains finite values.
        default_sparse_solver_backend
            Default sparse solver backend to use (scipy, pyamg, generic).

        Returns
        -------
        |VectorArray| of the inverse operator evaluations.

        Raises
        ------
        InversionError
            The operator could not be inverted.
        """
        assert V in self.range

        if V.dim == 0:
            if self.source.dim == 0 or least_squares:
                return self.source.make_array(
                    np.zeros((len(V), self.source.dim)))
            else:
                raise InversionError

        options = self.solver_options.get(
            'inverse') if self.solver_options else None
        assert self.sparse or not options

        if self.sparse:
            if options:
                solver = options if isinstance(options,
                                               str) else options['type']
                backend = solver.split('_')[0]
            else:
                backend = default_sparse_solver_backend

            if backend == 'scipy':
                from pymor.bindings.scipy import apply_inverse as apply_inverse_impl
            elif backend == 'pyamg':
                if not config.HAVE_PYAMG:
                    raise RuntimeError('PyAMG support not enabled.')
                from pymor.bindings.pyamg import apply_inverse as apply_inverse_impl
            elif backend == 'generic':
                logger = getLogger('pymor.bindings.scipy.scipy_apply_inverse')
                logger.warning(
                    'You have selected a (potentially slow) generic solver for a NumPy matrix operator!'
                )
                from pymor.algorithms.genericsolvers import apply_inverse as apply_inverse_impl
            else:
                raise NotImplementedError

            return apply_inverse_impl(self,
                                      V,
                                      options=options,
                                      least_squares=least_squares,
                                      check_finite=check_finite)

        else:
            if least_squares:
                try:
                    R, _, _, _ = np.linalg.lstsq(self.matrix, V.to_numpy().T)
                except np.linalg.LinAlgError as e:
                    raise InversionError(f'{str(type(e))}: {str(e)}')
                R = R.T
            else:
                try:
                    R = np.linalg.solve(self.matrix, V.to_numpy().T).T
                except np.linalg.LinAlgError as e:
                    raise InversionError(f'{str(type(e))}: {str(e)}')

            if check_finite:
                if not np.isfinite(np.sum(R)):
                    raise InversionError('Result contains non-finite values')

            return self.source.make_array(R)
Exemple #6
0
 def apply_inverse(self, V, mu=None, least_squares=False):
     if not least_squares:
         raise InversionError('ConstantOperator is not invertible.')
     return self.source.zeros(len(V))
Exemple #7
0
def apply_inverse(op,
                  V,
                  initial_guess=None,
                  options=None,
                  least_squares=False,
                  check_finite=True,
                  default_solver='generic_lgmres',
                  default_least_squares_solver='generic_least_squares_lsmr'):
    """Solve linear equation system.

    Applies the inverse of `op` to the vectors in `V` using a generic iterative solver.

    Parameters
    ----------
    op
        The linear, non-parametric |Operator| to invert.
    V
        |VectorArray| of right-hand sides for the equation system.
    initial_guess
        |VectorArray| with the same length as `V` containing initial guesses
        for the solution.  Some implementations of `apply_inverse` may
        ignore this parameter.  If `None` a solver-dependent default is used.
    options
        The |solver_options| to use (see :func:`solver_options`).
    least_squares
        If `True`, return least squares solution.
    check_finite
        Test if solution only contains finite values.
    default_solver
        Default solver to use (generic_lgmres, generic_least_squares_lsmr, generic_least_squares_lsqr).
    default_least_squares_solver
        Default solver to use for least squares problems (generic_least_squares_lsmr,
        generic_least_squares_lsqr).

    Returns
    -------
    |VectorArray| of the solution vectors.
    """

    assert V in op.range
    assert initial_guess is None or initial_guess in op.source and len(
        initial_guess) == len(V)
    options = _parse_options(options, solver_options(), default_solver,
                             default_least_squares_solver, least_squares)

    R = op.source.empty(reserve=len(V))

    if options['type'] == 'generic_lgmres':
        for i in range(len(V)):
            r, info = lgmres(
                op,
                V[i],
                initial_guess[i] if initial_guess is not None else None,
                tol=options['tol'],
                maxiter=options['maxiter'],
                inner_m=options['inner_m'],
                outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    f'lgmres failed to converge after {info} iterations')
            assert info == 0
            R.append(r)
    elif options['type'] == 'generic_least_squares_lsmr':
        for i in range(len(V)):
            r, info, itn, _, _, _, _, _ = lsmr(op,
                                               V[i],
                                               damp=options['damp'],
                                               atol=options['atol'],
                                               btol=options['btol'],
                                               conlim=options['conlim'],
                                               maxiter=options['maxiter'],
                                               show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    f'lsmr failed to converge after {itn} iterations')
            getLogger('pymor.algorithms.genericsolvers.lsmr').info(
                f'Converged after {itn} iterations')
            R.append(r)
    elif options['type'] == 'generic_least_squares_lsqr':
        for i in range(len(V)):
            r, info, itn, _, _, _, _, _, _ = lsqr(op,
                                                  V[i],
                                                  damp=options['damp'],
                                                  atol=options['atol'],
                                                  btol=options['btol'],
                                                  conlim=options['conlim'],
                                                  iter_lim=options['iter_lim'],
                                                  show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    f'lsmr failed to converge after {itn} iterations')
            getLogger('pymor.algorithms.genericsolvers.lsqr').info(
                f'Converged after {itn} iterations')
            R.append(r)
    else:
        raise ValueError('Unknown solver type')

    if check_finite:
        if not np.isfinite(np.all(R.norm())):
            raise InversionError('Result contains non-finite values')

    return R
Exemple #8
0
def apply_inverse(op,
                  V,
                  initial_guess=None,
                  options=None,
                  least_squares=False,
                  check_finite=True,
                  default_solver='scipy_spsolve',
                  default_least_squares_solver='scipy_least_squares_lsmr'):
    """Solve linear equation system.

    Applies the inverse of `op` to the vectors in `V` using SciPy.

    Parameters
    ----------
    op
        The linear, non-parametric |Operator| to invert.
    V
        |VectorArray| of right-hand sides for the equation system.
    initial_guess
        |VectorArray| with the same length as `V` containing initial guesses
        for the solution.  Some implementations of `apply_inverse` may
        ignore this parameter.  If `None` a solver-dependent default is used.
    options
        The |solver_options| to use (see :func:`solver_options`).
    least_squares
        If `True`, return least squares solution.
    check_finite
        Test if solution only contains finite values.
    default_solver
        Default solver to use (scipy_spsolve, scipy_bicgstab, scipy_bicgstab_spilu,
        scipy_lgmres, scipy_least_squares_lsmr, scipy_least_squares_lsqr).
    default_least_squares_solver
        Default solver to use for least squares problems (scipy_least_squares_lsmr,
        scipy_least_squares_lsqr).

    Returns
    -------
    |VectorArray| of the solution vectors.
    """

    assert V in op.range
    assert initial_guess is None or initial_guess in op.source and len(
        initial_guess) == len(V)

    if isinstance(op, NumpyMatrixOperator):
        matrix = op.matrix
    else:
        from pymor.algorithms.to_matrix import to_matrix
        matrix = to_matrix(op)

    options = _parse_options(options, solver_options(), default_solver,
                             default_least_squares_solver, least_squares)

    V = V.to_numpy()
    initial_guess = initial_guess.to_numpy(
    ) if initial_guess is not None else None
    promoted_type = np.promote_types(matrix.dtype, V.dtype)
    R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type)

    if options['type'] == 'scipy_bicgstab':
        for i, VV in enumerate(V):
            R[i], info = bicgstab(
                matrix,
                VV,
                initial_guess[i] if initial_guess is not None else None,
                tol=options['tol'],
                maxiter=options['maxiter'])
            if info != 0:
                if info > 0:
                    raise InversionError(
                        f'bicgstab failed to converge after {info} iterations')
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'scipy_bicgstab_spilu':
        ilu = spilu(matrix,
                    drop_tol=options['spilu_drop_tol'],
                    fill_factor=options['spilu_fill_factor'],
                    drop_rule=options['spilu_drop_rule'],
                    permc_spec=options['spilu_permc_spec'])
        precond = LinearOperator(matrix.shape, ilu.solve)
        for i, VV in enumerate(V):
            R[i], info = bicgstab(
                matrix,
                VV,
                initial_guess[i] if initial_guess is not None else None,
                tol=options['tol'],
                maxiter=options['maxiter'],
                M=precond)
            if info != 0:
                if info > 0:
                    raise InversionError(
                        f'bicgstab failed to converge after {info} iterations')
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'scipy_spsolve':
        try:
            # maybe remove unusable factorization:
            if hasattr(matrix, 'factorization'):
                fdtype = matrix.factorizationdtype
                if not np.can_cast(V.dtype, fdtype, casting='safe'):
                    del matrix.factorization

            if hasattr(matrix, 'factorization'):
                # we may use a complex factorization of a real matrix to
                # apply it to a real vector. In that case, we downcast
                # the result here, removing the imaginary part,
                # which should be zero.
                R = matrix.factorization.solve(V.T).T.astype(promoted_type,
                                                             copy=False)
            elif options['keep_factorization']:
                # the matrix is always converted to the promoted type.
                # if matrix.dtype == promoted_type, this is a no_op
                matrix.factorization = splu(matrix_astype_nocopy(
                    matrix.tocsc(), promoted_type),
                                            permc_spec=options['permc_spec'])
                matrix.factorizationdtype = promoted_type
                R = matrix.factorization.solve(V.T).T
            else:
                # the matrix is always converted to the promoted type.
                # if matrix.dtype == promoted_type, this is a no_op
                R = spsolve(matrix_astype_nocopy(matrix, promoted_type),
                            V.T,
                            permc_spec=options['permc_spec']).T
        except RuntimeError as e:
            raise InversionError(e)
    elif options['type'] == 'scipy_lgmres':
        for i, VV in enumerate(V):
            R[i], info = lgmres(
                matrix,
                VV,
                initial_guess[i] if initial_guess is not None else None,
                tol=options['tol'],
                atol=options['tol'],
                maxiter=options['maxiter'],
                inner_m=options['inner_m'],
                outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    f'lgmres failed to converge after {info} iterations')
            assert info == 0
    elif options['type'] == 'scipy_least_squares_lsmr':
        from scipy.sparse.linalg import lsmr
        for i, VV in enumerate(V):
            R[i], info, itn, _, _, _, _, _ = lsmr(
                matrix,
                VV,
                damp=options['damp'],
                atol=options['atol'],
                btol=options['btol'],
                conlim=options['conlim'],
                maxiter=options['maxiter'],
                show=options['show'],
                x0=initial_guess[i] if initial_guess is not None else None)
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    f'lsmr failed to converge after {itn} iterations')
    elif options['type'] == 'scipy_least_squares_lsqr':
        for i, VV in enumerate(V):
            R[i], info, itn, _, _, _, _, _, _, _ = lsqr(
                matrix,
                VV,
                damp=options['damp'],
                atol=options['atol'],
                btol=options['btol'],
                conlim=options['conlim'],
                iter_lim=options['iter_lim'],
                show=options['show'],
                x0=initial_guess[i] if initial_guess is not None else None)
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    f'lsmr failed to converge after {itn} iterations')
    else:
        raise ValueError('Unknown solver type')

    if check_finite:
        if not np.isfinite(np.sum(R)):
            raise InversionError('Result contains non-finite values')

    return op.source.from_numpy(R)
Exemple #9
0
def apply_inverse(matrix, U, options=None):
    """Solve linear equation system.

    Applies the inverse of `matrix` to the row vectors in `U`.

    See :func:`sparse_options` for documentation of all possible options for
    sparse matrices.

    This method is called by :meth:`pymor.core.NumpyMatrixOperator.apply_inverse`
    and usually should not be used directly.

    Parameters
    ----------
    matrix
        The |NumPy| matrix to invert.
    U
        2-dimensional |NumPy array| containing as row vectors
        the right-hand sides of the linear equation systems to
        solve.
    options
        |invert_options| to use. (See :func:`invert_options`.)

    Returns
    -------
    |NumPy array| of the solution vectors.
    """

    default_options = invert_options(matrix)

    if options is None:
        options = default_options.values()[0]
    elif isinstance(options, str):
        if options == 'least_squares':
            for k, v in default_options.iteritems():
                if k.startswith('least_squares'):
                    options = v
                    break
            assert not isinstance(options, str)
        else:
            options = default_options[options]
    else:
        assert 'type' in options and options['type'] in default_options \
            and options.viewkeys() <= default_options[options['type']].viewkeys()
        user_options = options
        options = default_options[user_options['type']]
        options.update(user_options)

    R = np.empty((len(U), matrix.shape[1]))

    if options['type'] == 'solve':
        for i, UU in enumerate(U):
            try:
                R[i] = np.linalg.solve(matrix, UU)
            except np.linalg.LinAlgError as e:
                raise InversionError('{}: {}'.format(str(type(e)), str(e)))
    elif options['type'] == 'least_squares_lstsq':
        for i, UU in enumerate(U):
            try:
                R[i], _, _, _ = np.linalg.lstsq(matrix,
                                                UU,
                                                rcond=options['rcond'])
            except np.linalg.LinAlgError as e:
                raise InversionError('{}: {}'.format(str(type(e)), str(e)))
    elif options['type'] == 'bicgstab':
        for i, UU in enumerate(U):
            R[i], info = bicgstab(matrix,
                                  UU,
                                  tol=options['tol'],
                                  maxiter=options['maxiter'])
            if info != 0:
                if info > 0:
                    raise InversionError(
                        'bicgstab failed to converge after {} iterations'.
                        format(info))
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'bicgstab_spilu':
        ilu = spilu(matrix,
                    drop_tol=options['spilu_drop_tol'],
                    fill_factor=options['spilu_fill_factor'],
                    drop_rule=options['spilu_drop_rule'],
                    permc_spec=options['spilu_permc_spec'])
        precond = LinearOperator(matrix.shape, ilu.solve)
        for i, UU in enumerate(U):
            R[i], info = bicgstab(matrix,
                                  UU,
                                  tol=options['tol'],
                                  maxiter=options['maxiter'],
                                  M=precond)
            if info != 0:
                if info > 0:
                    raise InversionError(
                        'bicgstab failed to converge after {} iterations'.
                        format(info))
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'spsolve':
        if scipy.version.version >= '0.14':
            if hasattr(matrix, 'factorization'):
                R = matrix.factorization.solve(U.T).T
            elif options['keep_factorization']:
                matrix.factorization = splu(matrix,
                                            permc_spec=options['permc_spec'])
                R = matrix.factorization.solve(U.T).T
            else:
                R = spsolve(matrix, U.T, permc_spec=options['permc_spec']).T
        else:
            if hasattr(matrix, 'factorization'):
                for i, UU in enumerate(U):
                    R[i] = matrix.factorization.solve(UU)
            elif options['keep_factorization']:
                matrix.factorization = splu(matrix,
                                            permc_spec=options['permc_spec'])
                for i, UU in enumerate(U):
                    R[i] = matrix.factorization.solve(UU)
            elif len(U) > 1:
                factorization = splu(matrix, permc_spec=options['permc_spec'])
                for i, UU in enumerate(U):
                    R[i] = factorization.solve(UU)
            else:
                R = spsolve(matrix, U.T,
                            permc_spec=options['permc_spec']).reshape((1, -1))
    elif options['type'] == 'lgmres':
        for i, UU in enumerate(U):
            R[i], info = lgmres(matrix,
                                UU.copy(i),
                                tol=options['tol'],
                                maxiter=options['maxiter'],
                                inner_m=options['inner_m'],
                                outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    'lgmres failed to converge after {} iterations'.format(
                        info))
            assert info == 0
    elif options['type'] == 'least_squares_lsmr':
        for i, UU in enumerate(U):
            R[i], info, itn, _, _, _, _, _ = lsmr(matrix,
                                                  UU.copy(i),
                                                  damp=options['damp'],
                                                  atol=options['atol'],
                                                  btol=options['btol'],
                                                  conlim=options['conlim'],
                                                  maxiter=options['maxiter'],
                                                  show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
    elif options['type'] == 'least_squares_lsqr':
        for i, UU in enumerate(U):
            R[i], info, itn, _, _, _, _, _, _, _ = lsqr(
                matrix,
                UU.copy(i),
                damp=options['damp'],
                atol=options['atol'],
                btol=options['btol'],
                conlim=options['conlim'],
                iter_lim=options['iter_lim'],
                show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
    elif options['type'] == 'pyamg':
        if len(U) > 0:
            U_iter = iter(enumerate(U))
            R[0], ml = pyamg.solve(matrix,
                                   next(U_iter)[1],
                                   tol=options['tol'],
                                   maxiter=options['maxiter'],
                                   return_solver=True)
            for i, UU in U_iter:
                R[i] = pyamg.solve(matrix,
                                   UU,
                                   tol=options['tol'],
                                   maxiter=options['maxiter'],
                                   existing_solver=ml)
    elif options['type'] == 'pyamg-rs':
        ml = pyamg.ruge_stuben_solver(matrix,
                                      strength=options['strength'],
                                      CF=options['CF'],
                                      presmoother=options['presmoother'],
                                      postsmoother=options['postsmoother'],
                                      max_levels=options['max_levels'],
                                      max_coarse=options['max_coarse'],
                                      coarse_solver=options['coarse_solver'])
        for i, UU in enumerate(U):
            R[i] = ml.solve(UU,
                            tol=options['tol'],
                            maxiter=options['maxiter'],
                            cycle=options['cycle'],
                            accel=options['accel'])
    elif options['type'] == 'pyamg-sa':
        ml = pyamg.smoothed_aggregation_solver(
            matrix,
            symmetry=options['symmetry'],
            strength=options['strength'],
            aggregate=options['aggregate'],
            smooth=options['smooth'],
            presmoother=options['presmoother'],
            postsmoother=options['postsmoother'],
            improve_candidates=options['improve_candidates'],
            max_levels=options['max_levels'],
            max_coarse=options['max_coarse'],
            diagonal_dominance=options['diagonal_dominance'])
        for i, UU in enumerate(U):
            R[i] = ml.solve(UU,
                            tol=options['tol'],
                            maxiter=options['maxiter'],
                            cycle=options['cycle'],
                            accel=options['accel'])
    elif options['type'].startswith('generic') or options['type'].startswith(
            'least_squares_generic'):
        logger = getLogger('pymor.la.numpysolvers.apply_inverse')
        logger.warn(
            'You have selected a (potentially slow) generic solver for a NumPy matrix operator!'
        )
        from pymor.operators.numpy import NumpyMatrixOperator
        from pymor.la.numpyvectorarray import NumpyVectorArray
        return genericsolvers.apply_inverse(NumpyMatrixOperator(matrix),
                                            NumpyVectorArray(U, copy=False),
                                            options=options).data
    else:
        raise ValueError('Unknown solver type')
    return R
Exemple #10
0
    def apply_inverse(self, V, mu=None, least_squares=False):
        """Apply the inverse operator.

        Parameters
        ----------
        V
            |VectorArray| of vectors to which the inverse operator is applied.
        mu
            The |Parameter| for which to evaluate the inverse operator.
        least_squares
            If `True`, solve the least squares problem::

                u = argmin ||op(u) - v||_2.

            Since for an invertible operator the least squares solution agrees
            with the result of the application of the inverse operator,
            setting this option should, in general, have no effect on the result
            for those operators. However, note that when no appropriate
            |solver_options| are set for the operator, most implementations
            will choose a least squares solver by default which may be
            undesirable.

        Returns
        -------
        |VectorArray| of the inverse operator evaluations.

        Raises
        ------
        InversionError
            The operator could not be inverted.
        """
        from pymor.operators.constructions import FixedParameterOperator
        assembled_op = self.assemble(mu)
        if assembled_op != self and not isinstance(assembled_op,
                                                   FixedParameterOperator):
            return assembled_op.apply_inverse(V, least_squares=least_squares)
        elif self.linear:
            options = self.solver_options.get(
                'inverse') if self.solver_options else None
            return genericsolvers.apply_inverse(assembled_op,
                                                V,
                                                options=options,
                                                least_squares=least_squares)
        else:
            from pymor.algorithms.newton import newton
            from pymor.core.exceptions import NewtonError

            options = self.solver_options.get(
                'inverse') if self.solver_options else None
            if options:
                if isinstance(options, str):
                    assert options == 'newton'
                    options = {}
                else:
                    assert options['type'] == 'newton'
                    options = options.copy()
                    options.pop('type')
            else:
                options = {}
            options['least_squares'] = least_squares

            R = V.empty(reserve=len(V))
            for i in range(len(V)):
                try:
                    R.append(newton(self, V[i], mu=mu, **options)[0])
                except NewtonError as e:
                    raise InversionError(e)
            return R
Exemple #11
0
    def apply_inverse(self, V, ind=None, mu=None, least_squares=False):
        from pymor.operators.constructions import FixedParameterOperator
        assembled_op = self.assemble(mu)
        if assembled_op != self and not isinstance(assembled_op,
                                                   FixedParameterOperator):
            return assembled_op.apply_inverse(V,
                                              ind=ind,
                                              least_squares=least_squares)
        elif self.linear:
            options = (self.solver_options.get('inverse')
                       if self.solver_options else
                       'least_squares' if least_squares else None)

            if options and not least_squares:
                solver_type = options if isinstance(options,
                                                    str) else options['type']
                if solver_type.startswith('least_squares'):
                    self.logger.warn(
                        'Least squares solver selected but "least_squares == False"'
                    )

            try:
                return genericsolvers.apply_inverse(assembled_op,
                                                    V.copy(ind),
                                                    options=options)
            except InversionError as e:
                if least_squares and options:
                    solver_type = options if isinstance(
                        options, str) else options['type']
                    if not solver_type.startswith('least_squares'):
                        msg = str(e) \
                            + '\nNote: linear solver was selected for solving least squares problem ' \
                            + '(maybe not invertible?)'
                        raise InversionError(msg)
                raise e
        else:
            from pymor.algorithms.newton import newton
            from pymor.core.exceptions import NewtonError
            assert V.check_ind(ind)

            options = self.solver_options
            if options:
                if isinstance(options, str):
                    assert options == 'newton'
                    options = {}
                else:
                    assert options['type'] == 'newton'
                    options = options.copy()
                    options.pop('type')
            else:
                options = {}
            options['least_squares'] = least_squares

            ind = (list(range(len(V))) if ind is None else
                   [ind] if isinstance(ind, Number) else ind)
            R = V.empty(reserve=len(ind))
            for i in ind:
                try:
                    R.append(newton(self, V.copy(i), **options)[0])
                except NewtonError as e:
                    raise InversionError(e)
            return R
Exemple #12
0
def apply_inverse(op,
                  V,
                  options=None,
                  least_squares=False,
                  check_finite=True,
                  default_solver='scipy_spsolve',
                  default_least_squares_solver='scipy_least_squares_lsmr'):
    """Solve linear equation system.

    Applies the inverse of `op` to the vectors in `rhs` using PyAMG.

    Parameters
    ----------
    op
        The linear, non-parametric |Operator| to invert.
    rhs
        |VectorArray| of right-hand sides for the equation system.
    options
        The |solver_options| to use (see :func:`solver_options`).
    check_finite
        Test if solution only containes finite values.
    default_solver
        Default solver to use (scipy_spsolve, scipy_bicgstab, scipy_bicgstab_spilu,
        scipy_lgmres, scipy_least_squares_lsmr, scipy_least_squares_lsqr).
    default_least_squares_solver
        Default solver to use for least squares problems (scipy_least_squares_lsmr,
        scipy_least_squares_lsqr).

    Returns
    -------
    |VectorArray| of the solution vectors.
    """

    assert V in op.range

    if isinstance(op, NumpyMatrixOperator):
        matrix = op._matrix
    else:
        from pymor.algorithms.to_matrix import to_matrix
        matrix = to_matrix(op)

    options = _parse_options(options, solver_options(), default_solver,
                             default_least_squares_solver, least_squares)

    V = V.data
    promoted_type = np.promote_types(matrix.dtype, V.dtype)
    R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type)

    if options['type'] == 'scipy_bicgstab':
        for i, VV in enumerate(V):
            R[i], info = bicgstab(matrix,
                                  VV,
                                  tol=options['tol'],
                                  maxiter=options['maxiter'])
            if info != 0:
                if info > 0:
                    raise InversionError(
                        'bicgstab failed to converge after {} iterations'.
                        format(info))
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'scipy_bicgstab_spilu':
        if Version(scipy.version.version) >= Version('0.19'):
            ilu = spilu(matrix,
                        drop_tol=options['spilu_drop_tol'],
                        fill_factor=options['spilu_fill_factor'],
                        drop_rule=options['spilu_drop_rule'],
                        permc_spec=options['spilu_permc_spec'])
        else:
            if options['spilu_drop_rule']:
                logger = getLogger('pymor.operators.numpy._apply_inverse')
                logger.error(
                    "ignoring drop_rule in ilu factorization due to old SciPy")
            ilu = spilu(matrix,
                        drop_tol=options['spilu_drop_tol'],
                        fill_factor=options['spilu_fill_factor'],
                        permc_spec=options['spilu_permc_spec'])
        precond = LinearOperator(matrix.shape, ilu.solve)
        for i, VV in enumerate(V):
            R[i], info = bicgstab(matrix,
                                  VV,
                                  tol=options['tol'],
                                  maxiter=options['maxiter'],
                                  M=precond)
            if info != 0:
                if info > 0:
                    raise InversionError(
                        'bicgstab failed to converge after {} iterations'.
                        format(info))
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'scipy_spsolve':
        try:
            # maybe remove unusable factorization:
            if hasattr(matrix, 'factorization'):
                fdtype = matrix.factorizationdtype
                if not np.can_cast(V.dtype, fdtype, casting='safe'):
                    del matrix.factorization

            if Version(scipy.version.version) >= Version('0.14'):
                if hasattr(matrix, 'factorization'):
                    # we may use a complex factorization of a real matrix to
                    # apply it to a real vector. In that case, we downcast
                    # the result here, removing the imaginary part,
                    # which should be zero.
                    R = matrix.factorization.solve(V.T).T.astype(promoted_type,
                                                                 copy=False)
                elif options['keep_factorization']:
                    # the matrix is always converted to the promoted type.
                    # if matrix.dtype == promoted_type, this is a no_op
                    matrix.factorization = splu(
                        matrix_astype_nocopy(matrix.tocsc(), promoted_type),
                        permc_spec=options['permc_spec'])
                    matrix.factorizationdtype = promoted_type
                    R = matrix.factorization.solve(V.T).T
                else:
                    # the matrix is always converted to the promoted type.
                    # if matrix.dtype == promoted_type, this is a no_op
                    R = spsolve(matrix_astype_nocopy(matrix, promoted_type),
                                V.T,
                                permc_spec=options['permc_spec']).T
            else:
                # see if-part for documentation
                if hasattr(matrix, 'factorization'):
                    for i, VV in enumerate(V):
                        R[i] = matrix.factorization.solve(VV).astype(
                            promoted_type, copy=False)
                elif options['keep_factorization']:
                    matrix.factorization = splu(
                        matrix_astype_nocopy(matrix.tocsc(), promoted_type),
                        permc_spec=options['permc_spec'])
                    matrix.factorizationdtype = promoted_type
                    for i, VV in enumerate(V):
                        R[i] = matrix.factorization.solve(VV)
                elif len(V) > 1:
                    factorization = splu(matrix_astype_nocopy(
                        matrix.tocsc(), promoted_type),
                                         permc_spec=options['permc_spec'])
                    for i, VV in enumerate(V):
                        R[i] = factorization.solve(VV)
                else:
                    R = spsolve(matrix_astype_nocopy(matrix, promoted_type),
                                V.T,
                                permc_spec=options['permc_spec']).reshape(
                                    (1, -1))
        except RuntimeError as e:
            raise InversionError(e)
    elif options['type'] == 'scipy_lgmres':
        for i, VV in enumerate(V):
            R[i], info = lgmres(matrix,
                                VV,
                                tol=options['tol'],
                                maxiter=options['maxiter'],
                                inner_m=options['inner_m'],
                                outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    'lgmres failed to converge after {} iterations'.format(
                        info))
            assert info == 0
    elif options['type'] == 'scipy_least_squares_lsmr':
        from scipy.sparse.linalg import lsmr
        for i, VV in enumerate(V):
            R[i], info, itn, _, _, _, _, _ = lsmr(matrix,
                                                  VV,
                                                  damp=options['damp'],
                                                  atol=options['atol'],
                                                  btol=options['btol'],
                                                  conlim=options['conlim'],
                                                  maxiter=options['maxiter'],
                                                  show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
    elif options['type'] == 'scipy_least_squares_lsqr':
        for i, VV in enumerate(V):
            R[i], info, itn, _, _, _, _, _, _, _ = lsqr(
                matrix,
                VV,
                damp=options['damp'],
                atol=options['atol'],
                btol=options['btol'],
                conlim=options['conlim'],
                iter_lim=options['iter_lim'],
                show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
    else:
        raise ValueError('Unknown solver type')

    if check_finite:
        if not np.isfinite(np.sum(R)):
            raise InversionError('Result contains non-finite values')

    return op.source.from_data(R)
Exemple #13
0
    def apply_inverse(op,
                      V,
                      options=None,
                      least_squares=False,
                      check_finite=True,
                      default_solver='pyamg_solve'):
        """Solve linear equation system.

        Applies the inverse of `op` to the vectors in `rhs` using PyAMG.

        Parameters
        ----------
        op
            The linear, non-parametric |Operator| to invert.
        rhs
            |VectorArray| of right-hand sides for the equation system.
        options
            The |solver_options| to use (see :func:`solver_options`).
        least_squares
            Must be `False`.
        check_finite
            Test if solution only contains finite values.
        default_solver
            Default solver to use (pyamg_solve, pyamg_rs, pyamg_sa).

        Returns
        -------
        |VectorArray| of the solution vectors.
        """

        assert V in op.range

        if least_squares:
            raise NotImplementedError

        if isinstance(op, NumpyMatrixOperator):
            matrix = op.matrix
        else:
            from pymor.algorithms.to_matrix import to_matrix
            matrix = to_matrix(op)

        options = _parse_options(options, solver_options(), default_solver,
                                 None, least_squares)

        V = V.to_numpy()
        promoted_type = np.promote_types(matrix.dtype, V.dtype)
        R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type)

        if options['type'] == 'pyamg_solve':
            if len(V) > 0:
                V_iter = iter(enumerate(V))
                R[0], ml = pyamg.solve(matrix,
                                       next(V_iter)[1],
                                       tol=options['tol'],
                                       maxiter=options['maxiter'],
                                       return_solver=True)
                for i, VV in V_iter:
                    R[i] = pyamg.solve(matrix,
                                       VV,
                                       tol=options['tol'],
                                       maxiter=options['maxiter'],
                                       existing_solver=ml)
        elif options['type'] == 'pyamg_rs':
            ml = pyamg.ruge_stuben_solver(
                matrix,
                strength=options['strength'],
                CF=options['CF'],
                presmoother=options['presmoother'],
                postsmoother=options['postsmoother'],
                max_levels=options['max_levels'],
                max_coarse=options['max_coarse'],
                coarse_solver=options['coarse_solver'])
            for i, VV in enumerate(V):
                R[i] = ml.solve(VV,
                                tol=options['tol'],
                                maxiter=options['maxiter'],
                                cycle=options['cycle'],
                                accel=options['accel'])
        elif options['type'] == 'pyamg_sa':
            ml = pyamg.smoothed_aggregation_solver(
                matrix,
                symmetry=options['symmetry'],
                strength=options['strength'],
                aggregate=options['aggregate'],
                smooth=options['smooth'],
                presmoother=options['presmoother'],
                postsmoother=options['postsmoother'],
                improve_candidates=options['improve_candidates'],
                max_levels=options['max_levels'],
                max_coarse=options['max_coarse'],
                diagonal_dominance=options['diagonal_dominance'])
            for i, VV in enumerate(V):
                R[i] = ml.solve(VV,
                                tol=options['tol'],
                                maxiter=options['maxiter'],
                                cycle=options['cycle'],
                                accel=options['accel'])
        else:
            raise ValueError('Unknown solver type')

        if check_finite:
            if not np.isfinite(np.sum(R)):
                raise InversionError('Result contains non-finite values')

        return op.source.from_numpy(R)
Exemple #14
0
def apply_inverse(op, rhs, options=None):
    """Solve linear equation system.

    Applies the inverse of `op` to the vectors in `rhs`.

    Parameters
    ----------
    op
        The linear, non-parametric |Operator| to invert.
    rhs
        |VectorArray| of right-hand sides for the equation system.
    options
        The solver options to use. (See :func:`options`.)

    Returns
    -------
    |VectorArray| of the solution vectors.
    """

    def_opts = globals()['options']()

    if options is None:
        options = next(iter(def_opts.values()))
    elif isinstance(options, str):
        if options == 'least_squares':
            for k, v in def_opts.items():
                if k.startswith('least_squares'):
                    options = v
                    break
            assert not isinstance(options, str)
        else:
            options = def_opts[options]
    else:
        assert 'type' in options and options['type'] in def_opts \
            and options.keys() <= def_opts[options['type']].keys()
        user_options = options
        options = def_opts[user_options['type']]
        options.update(user_options)

    R = op.source.empty(reserve=len(rhs))

    if options['type'] == 'generic_lgmres':
        for i in range(len(rhs)):
            r, info = lgmres(op,
                             rhs.copy(i),
                             tol=options['tol'],
                             maxiter=options['maxiter'],
                             inner_m=options['inner_m'],
                             outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    'lgmres failed to converge after {} iterations'.format(
                        info))
            assert info == 0
            R.append(r)
    elif options['type'] == 'least_squares_generic_lsmr':
        for i in range(len(rhs)):
            r, info, itn, _, _, _, _, _ = lsmr(op,
                                               rhs.copy(i),
                                               damp=options['damp'],
                                               atol=options['atol'],
                                               btol=options['btol'],
                                               conlim=options['conlim'],
                                               maxiter=options['maxiter'],
                                               show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
            getLogger('pymor.algorithms.genericsolvers.lsmr').info(
                'Converged after {} iterations'.format(itn))
            R.append(r)
    elif options['type'] == 'least_squares_generic_lsqr':
        for i in range(len(rhs)):
            r, info, itn, _, _, _, _, _, _ = lsqr(op,
                                                  rhs.copy(i),
                                                  damp=options['damp'],
                                                  atol=options['atol'],
                                                  btol=options['btol'],
                                                  conlim=options['conlim'],
                                                  iter_lim=options['iter_lim'],
                                                  show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
            getLogger('pymor.algorithms.genericsolvers.lsqr').info(
                'Converged after {} iterations'.format(itn))
            R.append(r)
    else:
        raise ValueError('Unknown solver type')

    return R
Exemple #15
0
def _apply_inverse(matrix, V, options=None):
    """Solve linear equation system.

    Applies the inverse of `matrix` to the row vectors in `V`.

    See :func:`dense_options` for documentation of all possible options for
    sparse matrices.

    See :func:`sparse_options` for documentation of all possible options for
    sparse matrices.

    This method is called by :meth:`pymor.core.NumpyMatrixOperator.apply_inverse`
    and usually should not be used directly.

    Parameters
    ----------
    matrix
        The |NumPy| matrix to invert.
    V
        2-dimensional |NumPy array| containing as row vectors
        the right-hand sides of the linear equation systems to
        solve.
    options
        The solver options to use. (See :func:`_options`.)

    Returns
    -------
    |NumPy array| of the solution vectors.
    """

    default_options = _options(matrix)

    if options is None:
        options = next(iter(default_options.values()))
    elif isinstance(options, str):
        if options == 'least_squares':
            for k, v in default_options.items():
                if k.startswith('least_squares'):
                    options = v
                    break
            assert not isinstance(options, str)
        else:
            options = default_options[options]
    else:
        assert 'type' in options and options['type'] in default_options \
            and options.keys() <= default_options[options['type']].keys()
        user_options = options
        options = default_options[user_options['type']]
        options.update(user_options)

    promoted_type = np.promote_types(matrix.dtype, V.dtype)
    R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type)

    if options['type'] == 'solve':
        for i, VV in enumerate(V):
            try:
                R[i] = np.linalg.solve(matrix, VV)
            except np.linalg.LinAlgError as e:
                raise InversionError('{}: {}'.format(str(type(e)), str(e)))
    elif options['type'] == 'least_squares_lstsq':
        for i, VV in enumerate(V):
            try:
                R[i], _, _, _ = np.linalg.lstsq(matrix,
                                                VV,
                                                rcond=options['rcond'])
            except np.linalg.LinAlgError as e:
                raise InversionError('{}: {}'.format(str(type(e)), str(e)))
    elif options['type'] == 'bicgstab':
        for i, VV in enumerate(V):
            R[i], info = bicgstab(matrix,
                                  VV,
                                  tol=options['tol'],
                                  maxiter=options['maxiter'])
            if info != 0:
                if info > 0:
                    raise InversionError(
                        'bicgstab failed to converge after {} iterations'.
                        format(info))
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'bicgstab_spilu':
        # workaround for https://github.com/pymor/pymor/issues/171
        try:
            ilu = spilu(matrix,
                        drop_tol=options['spilu_drop_tol'],
                        fill_factor=options['spilu_fill_factor'],
                        drop_rule=options['spilu_drop_rule'],
                        permc_spec=options['spilu_permc_spec'])
        except TypeError as t:
            logger = getLogger('pymor.operators.numpy._apply_inverse')
            logger.error("ignoring drop_rule in ilu factorization")
            ilu = spilu(matrix,
                        drop_tol=options['spilu_drop_tol'],
                        fill_factor=options['spilu_fill_factor'],
                        permc_spec=options['spilu_permc_spec'])
        precond = LinearOperator(matrix.shape, ilu.solve)
        for i, VV in enumerate(V):
            R[i], info = bicgstab(matrix,
                                  VV,
                                  tol=options['tol'],
                                  maxiter=options['maxiter'],
                                  M=precond)
            if info != 0:
                if info > 0:
                    raise InversionError(
                        'bicgstab failed to converge after {} iterations'.
                        format(info))
                else:
                    raise InversionError(
                        'bicgstab failed with error code {} (illegal input or breakdown)'
                        .format(info))
    elif options['type'] == 'spsolve':
        try:
            # maybe remove unusable factorization:
            if hasattr(matrix, 'factorization'):
                fdtype = matrix.factorizationdtype
                if not np.can_cast(V.dtype, fdtype, casting='safe'):
                    del matrix.factorization

            if list(map(int, scipy.version.version.split('.'))) >= [0, 14, 0]:
                if hasattr(matrix, 'factorization'):
                    # we may use a complex factorization of a real matrix to
                    # apply it to a real vector. In that case, we downcast
                    # the result here, removing the imaginary part,
                    # which should be zero.
                    R = matrix.factorization.solve(V.T).T.astype(promoted_type,
                                                                 copy=False)
                elif options['keep_factorization']:
                    # the matrix is always converted to the promoted type.
                    # if matrix.dtype == promoted_type, this is a no_op
                    matrix.factorization = splu(
                        matrix_astype_nocopy(matrix, promoted_type),
                        permc_spec=options['permc_spec'])
                    matrix.factorizationdtype = promoted_type
                    R = matrix.factorization.solve(V.T).T
                else:
                    # the matrix is always converted to the promoted type.
                    # if matrix.dtype == promoted_type, this is a no_op
                    R = spsolve(matrix_astype_nocopy(matrix, promoted_type),
                                V.T,
                                permc_spec=options['permc_spec']).T
            else:
                # see if-part for documentation
                if hasattr(matrix, 'factorization'):
                    for i, VV in enumerate(V):
                        R[i] = matrix.factorization.solve(VV).astype(
                            promoted_type, copy=False)
                elif options['keep_factorization']:
                    matrix.factorization = splu(
                        matrix_astype_nocopy(matrix, promoted_type),
                        permc_spec=options['permc_spec'])
                    matrix.factorizationdtype = promoted_type
                    for i, VV in enumerate(V):
                        R[i] = matrix.factorization.solve(VV)
                elif len(V) > 1:
                    factorization = splu(matrix_astype_nocopy(
                        matrix, promoted_type),
                                         permc_spec=options['permc_spec'])
                    for i, VV in enumerate(V):
                        R[i] = factorization.solve(VV)
                else:
                    R = spsolve(matrix_astype_nocopy(matrix, promoted_type),
                                V.T,
                                permc_spec=options['permc_spec']).reshape(
                                    (1, -1))
        except RuntimeError as e:
            raise InversionError(e)
    elif options['type'] == 'lgmres':
        for i, VV in enumerate(V):
            R[i], info = lgmres(matrix,
                                VV.copy(i),
                                tol=options['tol'],
                                maxiter=options['maxiter'],
                                inner_m=options['inner_m'],
                                outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    'lgmres failed to converge after {} iterations'.format(
                        info))
            assert info == 0
    elif options['type'] == 'least_squares_lsmr':
        for i, VV in enumerate(V):
            R[i], info, itn, _, _, _, _, _ = lsmr(matrix,
                                                  VV.copy(i),
                                                  damp=options['damp'],
                                                  atol=options['atol'],
                                                  btol=options['btol'],
                                                  conlim=options['conlim'],
                                                  maxiter=options['maxiter'],
                                                  show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
    elif options['type'] == 'least_squares_lsqr':
        for i, VV in enumerate(V):
            R[i], info, itn, _, _, _, _, _, _, _ = lsqr(
                matrix,
                VV.copy(i),
                damp=options['damp'],
                atol=options['atol'],
                btol=options['btol'],
                conlim=options['conlim'],
                iter_lim=options['iter_lim'],
                show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
    elif options['type'] == 'pyamg':
        if len(V) > 0:
            V_iter = iter(enumerate(V))
            R[0], ml = pyamg.solve(matrix,
                                   next(V_iter)[1],
                                   tol=options['tol'],
                                   maxiter=options['maxiter'],
                                   return_solver=True)
            for i, VV in V_iter:
                R[i] = pyamg.solve(matrix,
                                   VV,
                                   tol=options['tol'],
                                   maxiter=options['maxiter'],
                                   existing_solver=ml)
    elif options['type'] == 'pyamg-rs':
        ml = pyamg.ruge_stuben_solver(matrix,
                                      strength=options['strength'],
                                      CF=options['CF'],
                                      presmoother=options['presmoother'],
                                      postsmoother=options['postsmoother'],
                                      max_levels=options['max_levels'],
                                      max_coarse=options['max_coarse'],
                                      coarse_solver=options['coarse_solver'])
        for i, VV in enumerate(V):
            R[i] = ml.solve(VV,
                            tol=options['tol'],
                            maxiter=options['maxiter'],
                            cycle=options['cycle'],
                            accel=options['accel'])
    elif options['type'] == 'pyamg-sa':
        ml = pyamg.smoothed_aggregation_solver(
            matrix,
            symmetry=options['symmetry'],
            strength=options['strength'],
            aggregate=options['aggregate'],
            smooth=options['smooth'],
            presmoother=options['presmoother'],
            postsmoother=options['postsmoother'],
            improve_candidates=options['improve_candidates'],
            max_levels=options['max_levels'],
            max_coarse=options['max_coarse'],
            diagonal_dominance=options['diagonal_dominance'])
        for i, VV in enumerate(V):
            R[i] = ml.solve(VV,
                            tol=options['tol'],
                            maxiter=options['maxiter'],
                            cycle=options['cycle'],
                            accel=options['accel'])
    elif options['type'].startswith('generic') or options['type'].startswith(
            'least_squares_generic'):
        logger = getLogger('pymor.operators.numpy._apply_inverse')
        logger.warn(
            'You have selected a (potentially slow) generic solver for a NumPy matrix operator!'
        )
        from pymor.operators.numpy import NumpyMatrixOperator
        from pymor.vectorarrays.numpy import NumpyVectorArray
        return genericsolvers.apply_inverse(NumpyMatrixOperator(matrix),
                                            NumpyVectorArray(V, copy=False),
                                            options=options).data
    else:
        raise ValueError('Unknown solver type')
    return R
Exemple #16
0
def apply_inverse(op,
                  rhs,
                  options=None,
                  least_squares=False,
                  check_finite=True,
                  default_solver='generic_lgmres',
                  default_least_squares_solver='generic_least_squares_lsmr'):
    """Solve linear equation system.

    Applies the inverse of `op` to the vectors in `rhs` using a generic iterative solver.

    Parameters
    ----------
    op
        The linear, non-parametric |Operator| to invert.
    rhs
        |VectorArray| of right-hand sides for the equation system.
    options
        The |solver_options| to use (see :func:`solver_options`).
    check_finite
        Test if solution only containes finite values.
    default_solver
        Default solver to use (generic_lgmres, generic_least_squares_lsmr, generic_least_squares_lsqr).
    default_least_squares_solver
        Default solver to use for least squares problems (generic_least_squares_lsmr,
        generic_least_squares_lsqr).

    Returns
    -------
    |VectorArray| of the solution vectors.
    """

    options = _parse_options(options, solver_options(), default_solver,
                             default_least_squares_solver, least_squares)

    R = op.source.empty(reserve=len(rhs))

    if options['type'] == 'generic_lgmres':
        for i in range(len(rhs)):
            r, info = lgmres(op,
                             rhs[i],
                             tol=options['tol'],
                             maxiter=options['maxiter'],
                             inner_m=options['inner_m'],
                             outer_k=options['outer_k'])
            if info > 0:
                raise InversionError(
                    'lgmres failed to converge after {} iterations'.format(
                        info))
            assert info == 0
            R.append(r)
    elif options['type'] == 'generic_least_squares_lsmr':
        for i in range(len(rhs)):
            r, info, itn, _, _, _, _, _ = lsmr(op,
                                               rhs[i],
                                               damp=options['damp'],
                                               atol=options['atol'],
                                               btol=options['btol'],
                                               conlim=options['conlim'],
                                               maxiter=options['maxiter'],
                                               show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
            getLogger('pymor.algorithms.genericsolvers.lsmr').info(
                'Converged after {} iterations'.format(itn))
            R.append(r)
    elif options['type'] == 'generic_least_squares_lsqr':
        for i in range(len(rhs)):
            r, info, itn, _, _, _, _, _, _ = lsqr(op,
                                                  rhs[i],
                                                  damp=options['damp'],
                                                  atol=options['atol'],
                                                  btol=options['btol'],
                                                  conlim=options['conlim'],
                                                  iter_lim=options['iter_lim'],
                                                  show=options['show'])
            assert 0 <= info <= 7
            if info == 7:
                raise InversionError(
                    'lsmr failed to converge after {} iterations'.format(itn))
            getLogger('pymor.algorithms.genericsolvers.lsqr').info(
                'Converged after {} iterations'.format(itn))
            R.append(r)
    else:
        raise ValueError('Unknown solver type')

    if check_finite:
        if not np.isfinite(np.all(R.l2_norm())):
            raise InversionError('Result contains non-finite values')

    return R