Example #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
Example #2
0
 def apply_inverse(self, U, ind=None, mu=None, options=None):
     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(U, ind=ind, options=options)
     else:
         return genericsolvers.apply_inverse(assembled_op, U.copy(ind), options=options)
Example #3
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
Example #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
Example #5
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 = 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)

    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':
        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, 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 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
Example #6
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
Example #7
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
Example #8
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 = 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(V), matrix.shape[1]), dtype=np.promote_types(matrix.dtype, V.dtype))

    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":
        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, 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:
            if scipy.version.version >= "0.14":
                if hasattr(matrix, "factorization"):
                    R = matrix.factorization.solve(V.T).T
                elif options["keep_factorization"]:
                    matrix.factorization = splu(matrix, permc_spec=options["permc_spec"])
                    R = matrix.factorization.solve(V.T).T
                else:
                    R = spsolve(matrix, V.T, permc_spec=options["permc_spec"]).T
            else:
                if hasattr(matrix, "factorization"):
                    for i, VV in enumerate(V):
                        R[i] = matrix.factorization.solve(VV)
                elif options["keep_factorization"]:
                    matrix.factorization = splu(matrix, permc_spec=options["permc_spec"])
                    for i, VV in enumerate(V):
                        R[i] = matrix.factorization.solve(VV)
                elif len(V) > 1:
                    factorization = splu(matrix, permc_spec=options["permc_spec"])
                    for i, VV in enumerate(V):
                        R[i] = factorization.solve(VV)
                else:
                    R = spsolve(matrix, 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
Example #9
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