Beispiel #1
0
def solve_ricc_lrcf(A, E, B, C, R=None, trans=False, options=None):
    """Compute an approximate low-rank solution of a Riccati equation.

    See :func:`pymor.algorithms.riccati.solve_ricc_lrcf` for a general
    description.

    This function uses `scipy.linalg.solve_continuous_are`, which
    is a dense solver.
    Therefore, we assume all |Operators| and |VectorArrays| can be
    converted to |NumPy arrays| using
    :func:`~pymor.algorithms.to_matrix.to_matrix` and
    :func:`~pymor.vectorarrays.interface.VectorArray.to_numpy`.

    Parameters
    ----------
    A
        The non-parametric |Operator| A.
    E
        The non-parametric |Operator| E or `None`.
    B
        The operator B as a |VectorArray| from `A.source`.
    C
        The operator C as a |VectorArray| from `A.source`.
    R
        The operator R as a 2D |NumPy array| or `None`.
    trans
        Whether the first |Operator| in the Riccati equation is
        transposed.
    options
        The solver options to use (see :func:`ricc_lrcf_solver_options`).

    Returns
    -------
    Z
        Low-rank Cholesky factor of the Riccati equation solution,
        |VectorArray| from `A.source`.
    """

    _solve_ricc_check_args(A, E, B, C, R, trans)
    options = _parse_options(options, ricc_lrcf_solver_options(), 'scipy',
                             None, False)
    if options['type'] != 'scipy':
        raise ValueError(
            f"Unexpected Riccati equation solver ({options['type']}).")

    A_source = A.source
    A = to_matrix(A, format='dense')
    E = to_matrix(E, format='dense') if E else None
    B = B.to_numpy().T
    C = C.to_numpy()
    if R is None:
        R = np.eye(C.shape[0] if not trans else B.shape[1])
    if not trans:
        if E is not None:
            E = E.T
        X = solve_continuous_are(A.T, C.T, B.dot(B.T), R, E)
    else:
        X = solve_continuous_are(A, B, C.T.dot(C), R, E)

    return A_source.from_numpy(_chol(X).T)
Beispiel #2
0
    def solve_pos_ricc_lrcf(A, E, B, C, R=None, trans=False, options=None):
        """Compute an approximate low-rank solution of a positive Riccati equation.

        See :func:`pymor.algorithms.riccati.solve_pos_ricc_lrcf` for a
        general description.

        This function uses `pymess.dense_nm_gmpcare`.

        Parameters
        ----------
        A
            The non-parametric |Operator| A.
        E
            The non-parametric |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        C
            The operator C as a |VectorArray| from `A.source`.
        R
            The operator R as a 2D |NumPy array| or `None`.
        trans
            Whether the first |Operator| in the Riccati equation is
            transposed.
        options
            The solver options to use (see
            :func:`pos_ricc_lrcf_solver_options`).

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Riccati equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_ricc_check_args(A, E, B, C, R, trans)
        options = _parse_options(options, pos_ricc_lrcf_solver_options(),
                                 'pymess_dense_nm_gmpcare', None, False)

        if options['type'] == 'pymess_dense_nm_gmpcare':
            X = _call_pymess_dense_nm_gmpare(A,
                                             E,
                                             B,
                                             C,
                                             R,
                                             trans=trans,
                                             options=options['opts'],
                                             plus=True,
                                             method_name='solve_pos_ricc_lrcf')
            Z = _chol(X)
        else:
            raise ValueError(
                f'Unexpected positive Riccati equation solver ({options["type"]}).'
            )

        return A.source.from_numpy(Z.T)
Beispiel #3
0
    def solve_lyap_lrcf(A, E, B, trans=False, options=None):
        """Compute an approximate low-rank solution of a Lyapunov equation.

        See :func:`pymor.algorithms.lyapunov.solve_lyap_lrcf` for a
        general description.

        This function uses `slycot.sb03md` (if `E is None`) and
        `slycot.sg03ad` (if `E is not None`), which are dense solvers
        based on the Bartels-Stewart algorithm.
        Therefore, we assume A and E can be converted to |NumPy arrays|
        using :func:`~pymor.algorithms.to_matrix.to_matrix` and that
        `B.to_numpy` is implemented.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        trans
            Whether the first |Operator| in the Lyapunov equation is
            transposed.
        options
            The solver options to use (see
            :func:`lyap_lrcf_solver_options`).

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Lyapunov equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_lyap_lrcf_check_args(A, E, B, trans)
        options = _parse_options(options, lyap_lrcf_solver_options(),
                                 'slycot_bartels-stewart', None, False)

        if options['type'] == 'slycot_bartels-stewart':
            X = solve_lyap_dense(to_matrix(A, format='dense'),
                                 to_matrix(E, format='dense') if E else None,
                                 B.to_numpy().T if not trans else B.to_numpy(),
                                 trans=trans,
                                 options=options)
            Z = _chol(X)
        else:
            raise ValueError(
                f"Unexpected Lyapunov equation solver ({options['type']}).")

        return A.source.from_numpy(Z.T)
Beispiel #4
0
def solve_lyap_lrcf(A, E, B, trans=False, options=None):
    """Compute an approximate low-rank solution of a Lyapunov equation.

    See :func:`pymor.algorithms.lyapunov.solve_lyap_lrcf` for a general
    description.

    This function uses `scipy.linalg.solve_continuous_lyapunov`, which
    is a dense solver for Lyapunov equations with E=I.
    Therefore, we assume A and E can be converted to |NumPy arrays|
    using :func:`~pymor.algorithms.to_matrix.to_matrix` and that
    `B.to_numpy` is implemented.

    .. note::
        If E is not `None`, the problem will be reduced to a standard
        continuous-time algebraic Lyapunov equation by inverting E.

    Parameters
    ----------
    A
        The non-parametric |Operator| A.
    E
        The non-parametric |Operator| E or `None`.
    B
        The operator B as a |VectorArray| from `A.source`.
    trans
        Whether the first |Operator| in the Lyapunov equation is
        transposed.
    options
        The solver options to use (see
        :func:`lyap_lrcf_solver_options`).

    Returns
    -------
    Z
        Low-rank Cholesky factor of the Lyapunov equation solution,
        |VectorArray| from `A.source`.
    """

    _solve_lyap_lrcf_check_args(A, E, B, trans)
    options = _parse_options(options, lyap_lrcf_solver_options(), 'scipy',
                             None, False)

    X = solve_lyap_dense(to_matrix(A, format='dense'),
                         to_matrix(E, format='dense') if E else None,
                         B.to_numpy().T if not trans else B.to_numpy(),
                         trans=trans,
                         options=options)
    return A.source.from_numpy(_chol(X).T)
Beispiel #5
0
    def solve_lyap_lrcf(A, E, B, trans=False, options=None):
        """Compute an approximate low-rank solution of a Lyapunov equation.

        See :func:`pymor.algorithms.lyapunov.solve_lyap_lrcf` for a
        general description.

        This function uses `slycot.sb03md` (if `E is None`) and
        `slycot.sg03ad` (if `E is not None`), which are dense solvers
        based on the Bartels-Stewart algorithm.
        Therefore, we assume A and E can be converted to |NumPy arrays|
        using :func:`~pymor.algorithms.to_matrix.to_matrix` and that
        `B.to_numpy` is implemented.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        trans
            Whether the first |Operator| in the Lyapunov equation is
            transposed.
        options
            The solver options to use (see
            :func:`lyap_lrcf_solver_options`).

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Lyapunov equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_lyap_lrcf_check_args(A, E, B, trans)
        options = _parse_options(options, lyap_lrcf_solver_options(), 'slycot_bartels-stewart', None, False)

        if options['type'] == 'slycot_bartels-stewart':
            X = solve_lyap_dense(to_matrix(A, format='dense'),
                                 to_matrix(E, format='dense') if E else None,
                                 B.to_numpy().T if not trans else B.to_numpy(),
                                 trans=trans, options=options)
            Z = _chol(X)
        else:
            raise ValueError(f"Unexpected Lyapunov equation solver ({options['type']}).")

        return A.source.from_numpy(Z.T)
Beispiel #6
0
    def solve_pos_ricc_lrcf(A, E, B, C, R=None, S=None, trans=False, options=None):
        """Compute an approximate low-rank solution of a positive Riccati equation.

        See :func:`pymor.algorithms.riccati.solve_pos_ricc_lrcf` for a
        general description.

        This function uses `pymess.dense_nm_gmpcare`.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        C
            The operator C as a |VectorArray| from `A.source`.
        R
            The operator R as a 2D |NumPy array| or `None`.
        S
            The operator S as a |VectorArray| from `A.source` or `None`.
        trans
            Whether the first |Operator| in the Riccati equation is
            transposed.
        options
            The solver options to use (see
            :func:`pos_ricc_lrcf_solver_options`).

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Riccati equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_ricc_check_args(A, E, B, C, R, S, trans)
        options = _parse_options(options, pos_ricc_lrcf_solver_options(), 'pymess_dense_nm_gmpcare', None, False)

        if options['type'] == 'pymess_dense_nm_gmpcare':
            X = _call_pymess_dense_nm_gmpare(A, E, B, C, R, S, trans=trans, options=options['opts'], plus=True)
            Z = _chol(X)
        else:
            raise ValueError(f'Unexpected positive Riccati equation solver ({options["type"]}).')

        return A.source.from_numpy(Z.T)
Beispiel #7
0
def solve_lyap_lrcf(A, E, B, trans=False, options=None):
    """Compute an approximate low-rank solution of a Lyapunov equation.

    See :func:`pymor.algorithms.lyapunov.solve_lyap_lrcf` for a general
    description.

    This function uses `scipy.linalg.solve_continuous_lyapunov`, which
    is a dense solver for Lyapunov equations with E=I.
    Therefore, we assume A and E can be converted to |NumPy arrays|
    using :func:`~pymor.algorithms.to_matrix.to_matrix` and that
    `B.to_numpy` is implemented.

    .. note::
        If E is not `None`, the problem will be reduced to a standard
        continuous-time algebraic Lyapunov equation by inverting E.

    Parameters
    ----------
    A
        The |Operator| A.
    E
        The |Operator| E or `None`.
    B
        The operator B as a |VectorArray| from `A.source`.
    trans
        Whether the first |Operator| in the Lyapunov equation is
        transposed.
    options
        The solver options to use (see
        :func:`lyap_lrcf_solver_options`).

    Returns
    -------
    Z
        Low-rank Cholesky factor of the Lyapunov equation solution,
        |VectorArray| from `A.source`.
    """

    _solve_lyap_lrcf_check_args(A, E, B, trans)
    options = _parse_options(options, lyap_lrcf_solver_options(), 'scipy', None, False)

    X = solve_lyap_dense(to_matrix(A, format='dense'),
                         to_matrix(E, format='dense') if E else None,
                         B.to_numpy().T if not trans else B.to_numpy(),
                         trans=trans, options=options)
    return A.source.from_numpy(_chol(X).T)
Beispiel #8
0
    def solve_ricc_lrcf(A,
                        E,
                        B,
                        C,
                        R=None,
                        S=None,
                        trans=False,
                        options=None,
                        default_solver=None):
        """Compute an approximate low-rank solution of a Riccati equation.

        See :func:`pymor.algorithms.riccati.solve_ricc_lrcf` for a
        general description.

        This function uses `pymess.dense_nm_gmpcare` and `pymess.lrnm`.
        For both methods,
        :meth:`~pymor.vectorarrays.interface.VectorArray.to_numpy`
        and
        :meth:`~pymor.vectorarrays.interface.VectorSpace.from_numpy`
        need to be implemented for `A.source`.
        Additionally, since `dense_nm_gmpcare` is a dense solver, it
        expects :func:`~pymor.algorithms.to_matrix.to_matrix` to work
        for A and E.

        If the solver is not specified using the options or
        default_solver arguments, `dense_nm_gmpcare` is used for small
        problems (smaller than defined with
        :func:`~pymor.algorithms.lyapunov.mat_eqn_sparse_min_size`) and
        `lrnm` for large problems.

        Parameters
        ----------
        A
            The non-parametric |Operator| A.
        E
            The non-parametric |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        C
            The operator C as a |VectorArray| from `A.source`.
        R
            The operator R as a 2D |NumPy array| or `None`.
        S
            The operator S as a |VectorArray| from `A.source` or `None`.
        trans
            Whether the first |Operator| in the Riccati equation is
            transposed.
        options
            The solver options to use (see
            :func:`ricc_lrcf_solver_options`).
        default_solver
            Default solver to use (pymess_lrnm,
            pymess_dense_nm_gmpcare).
            If `None`, chose solver depending on dimension `A`.

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Riccati equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_ricc_check_args(A, E, B, C, R, S, trans)
        if default_solver is None:
            default_solver = 'pymess_lrnm' if A.source.dim >= mat_eqn_sparse_min_size(
            ) else 'pymess_dense_nm_gmpcare'
        options = _parse_options(options, ricc_lrcf_solver_options(),
                                 default_solver, None, False)

        if options['type'] == 'pymess_dense_nm_gmpcare':
            X = _call_pymess_dense_nm_gmpare(A,
                                             E,
                                             B,
                                             C,
                                             R,
                                             S,
                                             trans=trans,
                                             options=options['opts'],
                                             plus=False,
                                             method_name='solve_ricc_lrcf')
            Z = _chol(X)
        elif options['type'] == 'pymess_lrnm':
            if S is not None:
                raise NotImplementedError
            if R is not None:
                import scipy.linalg as spla
                Rc = spla.cholesky(R)  # R = Rc^T * Rc
                Rci = spla.solve_triangular(Rc, np.eye(
                    Rc.shape[0]))  # R^{-1} = Rci * Rci^T
                if not trans:
                    C = C.lincomb(Rci.T)  # C <- Rci^T * C = (C^T * Rci)^T
                else:
                    B = B.lincomb(Rci.T)  # B <- B * Rci
            opts = options['opts']
            opts.type = pymess.MESS_OP_NONE if not trans else pymess.MESS_OP_TRANSPOSE
            eqn = RiccatiEquation(opts, A, E, B, C)
            Z, status = pymess.lrnm(eqn, opts)
            relres = status.res2_norm / status.res2_0
            if relres > opts.adi.res2_tol:
                logger = getLogger('pymor.bindings.pymess.solve_ricc_lrcf')
                logger.warning(
                    f'Desired relative residual tolerance was not achieved '
                    f'({relres:e} > {opts.adi.res2_tol:e}).')
        else:
            raise ValueError(
                f'Unexpected Riccati equation solver ({options["type"]}).')

        return A.source.from_numpy(Z.T)
Beispiel #9
0
    def solve_lyap_lrcf(A,
                        E,
                        B,
                        trans=False,
                        options=None,
                        default_solver=None):
        """Compute an approximate low-rank solution of a Lyapunov equation.

        See :func:`pymor.algorithms.lyapunov.solve_lyap_lrcf` for a
        general description.

        This function uses `pymess.glyap` and `pymess.lradi`.
        For both methods,
        :meth:`~pymor.vectorarrays.interface.VectorArray.to_numpy`
        and
        :meth:`~pymor.vectorarrays.interface.VectorSpace.from_numpy`
        need to be implemented for `A.source`.
        Additionally, since `glyap` is a dense solver, it expects
        :func:`~pymor.algorithms.to_matrix.to_matrix` to work for A and
        E.

        If the solver is not specified using the options or
        default_solver arguments, `glyap` is used for small problems
        (smaller than defined with
        :func:`~pymor.algorithms.lyapunov.mat_eqn_sparse_min_size`) and
        `lradi` for large problems.

        Parameters
        ----------
        A
            The non-parametric |Operator| A.
        E
            The non-parametric |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        trans
            Whether the first |Operator| in the Lyapunov equation is
            transposed.
        options
            The solver options to use (see
            :func:`lyap_lrcf_solver_options`).
        default_solver
            Default solver to use (pymess_lradi, pymess_glyap).
            If `None`, choose solver depending on the dimension of A.

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Lyapunov equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_lyap_lrcf_check_args(A, E, B, trans)
        if default_solver is None:
            default_solver = 'pymess_lradi' if A.source.dim >= mat_eqn_sparse_min_size(
            ) else 'pymess_glyap'
        options = _parse_options(options, lyap_lrcf_solver_options(),
                                 default_solver, None, False)

        if options['type'] == 'pymess_glyap':
            X = solve_lyap_dense(to_matrix(A, format='dense'),
                                 to_matrix(E, format='dense') if E else None,
                                 B.to_numpy().T if not trans else B.to_numpy(),
                                 trans=trans,
                                 options=options)
            Z = _chol(X)
        elif options['type'] == 'pymess_lradi':
            opts = options['opts']
            opts.type = pymess.MESS_OP_NONE if not trans else pymess.MESS_OP_TRANSPOSE
            eqn = LyapunovEquation(opts, A, E, B)
            Z, status = pymess.lradi(eqn, opts)
            relres = status.res2_norm / status.res2_0
            if relres > opts.adi.res2_tol:
                logger = getLogger('pymor.bindings.pymess.solve_lyap_lrcf')
                logger.warning(
                    f'Desired relative residual tolerance was not achieved '
                    f'({relres:e} > {opts.adi.res2_tol:e}).')
        else:
            raise ValueError(
                f'Unexpected Lyapunov equation solver ({options["type"]}).')

        return A.source.from_numpy(Z.T)
Beispiel #10
0
    def solve_ricc_lrcf(A, E, B, C, R=None, S=None, trans=False, options=None):
        """Compute an approximate low-rank solution of a Riccati equation.

        See :func:`pymor.algorithms.riccati.solve_ricc_lrcf` for a
        general description.

        This function uses `slycot.sb02md` (if E and S are `None`),
        `slycot.sb02od` (if E is `None` and S is not `None`) and
        `slycot.sg03ad` (if E is not `None`), which are dense solvers.
        Therefore, we assume all |Operators| and |VectorArrays| can be
        converted to |NumPy arrays| using
        :func:`~pymor.algorithms.to_matrix.to_matrix` and
        :func:`~pymor.vectorarrays.interfaces.VectorArrayInterface.to_numpy`.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        C
            The operator C as a |VectorArray| from `A.source`.
        R
            The operator R as a 2D |NumPy array| or `None`.
        S
            The operator S as a |VectorArray| from `A.source` or `None`.
        trans
            Whether the first |Operator| in the Riccati equation is
            transposed.
        options
            The solver options to use (see
            :func:`ricc_lrcf_solver_options`).

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Riccati equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_ricc_check_args(A, E, B, C, R, S, trans)
        options = _parse_options(options, ricc_lrcf_solver_options(), 'slycot',
                                 None, False)
        if options['type'] != 'slycot':
            raise ValueError(
                f"Unexpected Riccati equation solver ({options['type']}).")

        A_source = A.source
        A = to_matrix(A, format='dense')
        E = to_matrix(E, format='dense') if E else None
        B = B.to_numpy().T
        C = C.to_numpy()
        S = S.to_numpy().T if S else None

        n = A.shape[0]
        dico = 'C'

        if E is None:
            if S is None:
                if not trans:
                    A = A.T
                    G = C.T.dot(C) if R is None else slycot.sb02mt(
                        n, C.shape[0], C.T, R)[-1]
                else:
                    G = B.dot(B.T) if R is None else slycot.sb02mt(
                        n, B.shape[1], B, R)[-1]
                Q = B.dot(B.T) if not trans else C.T.dot(C)
                X, rcond = slycot.sb02md(n, A, G, Q, dico)[:2]
                _ricc_rcond_check('slycot.sb02md', rcond)
            else:
                m = C.shape[0] if not trans else B.shape[1]
                p = B.shape[1] if not trans else C.shape[0]
                if R is None:
                    R = np.eye(m)
                if not trans:
                    A = A.T
                    B, C = C.T, B.T
                X, rcond = slycot.sb02od(n,
                                         m,
                                         A,
                                         B,
                                         C,
                                         R,
                                         dico,
                                         p=p,
                                         L=S,
                                         fact='C')[:2]
                _ricc_rcond_check('slycot.sb02od', rcond)
        else:
            jobb = 'B'
            fact = 'C'
            uplo = 'U'
            jobl = 'Z' if S is None else 'N'
            scal = 'N'
            sort = 'S'
            acc = 'R'
            m = C.shape[0] if not trans else B.shape[1]
            p = B.shape[1] if not trans else C.shape[0]
            if R is None:
                R = np.eye(m)
            if S is None:
                S = np.empty((n, m))
            if not trans:
                A = A.T
                E = E.T
                B, C = C.T, B.T
            out = slycot.sg02ad(dico, jobb, fact, uplo, jobl, scal, sort, acc,
                                n, m, p, A, E, B, C, R, S)
            X = out[1]
            rcond = out[0]
            _ricc_rcond_check('slycot.sg02ad', rcond)

        return A_source.from_numpy(_chol(X).T)
Beispiel #11
0
    def solve_ricc_lrcf(A, E, B, C, R=None, S=None, trans=False, options=None):
        """Compute an approximate low-rank solution of a Riccati equation.

        See :func:`pymor.algorithms.riccati.solve_ricc_lrcf` for a
        general description.

        This function uses `slycot.sb02md` (if E and S are `None`),
        `slycot.sb02od` (if E is `None` and S is not `None`) and
        `slycot.sg03ad` (if E is not `None`), which are dense solvers.
        Therefore, we assume all |Operators| and |VectorArrays| can be
        converted to |NumPy arrays| using
        :func:`~pymor.algorithms.to_matrix.to_matrix` and
        :func:`~pymor.vectorarrays.interfaces.VectorArrayInterface.to_numpy`.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        C
            The operator C as a |VectorArray| from `A.source`.
        R
            The operator R as a 2D |NumPy array| or `None`.
        S
            The operator S as a |VectorArray| from `A.source` or `None`.
        trans
            Whether the first |Operator| in the Riccati equation is
            transposed.
        options
            The solver options to use (see
            :func:`ricc_lrcf_solver_options`).

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Riccati equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_ricc_check_args(A, E, B, C, R, S, trans)
        options = _parse_options(options, ricc_lrcf_solver_options(), 'slycot', None, False)
        if options['type'] != 'slycot':
            raise ValueError(f"Unexpected Riccati equation solver ({options['type']}).")

        A_source = A.source
        A = to_matrix(A, format='dense')
        E = to_matrix(E, format='dense') if E else None
        B = B.to_numpy().T
        C = C.to_numpy()
        S = S.to_numpy().T if S else None

        n = A.shape[0]
        dico = 'C'

        if E is None:
            if S is None:
                if not trans:
                    A = A.T
                    G = C.T.dot(C) if R is None else slycot.sb02mt(n, C.shape[0], C.T, R)[-1]
                else:
                    G = B.dot(B.T) if R is None else slycot.sb02mt(n, B.shape[1], B, R)[-1]
                Q = B.dot(B.T) if not trans else C.T.dot(C)
                X, rcond = slycot.sb02md(n, A, G, Q, dico)[:2]
                _ricc_rcond_check('slycot.sb02md', rcond)
            else:
                m = C.shape[0] if not trans else B.shape[1]
                p = B.shape[1] if not trans else C.shape[0]
                if R is None:
                    R = np.eye(m)
                if not trans:
                    A = A.T
                    B, C = C.T, B.T
                X, rcond = slycot.sb02od(n, m, A, B, C, R, dico, p=p, L=S, fact='C')[:2]
                _ricc_rcond_check('slycot.sb02od', rcond)
        else:
            jobb = 'B'
            fact = 'C'
            uplo = 'U'
            jobl = 'Z' if S is None else 'N'
            scal = 'N'
            sort = 'S'
            acc = 'R'
            m = C.shape[0] if not trans else B.shape[1]
            p = B.shape[1] if not trans else C.shape[0]
            if R is None:
                R = np.eye(m)
            if S is None:
                S = np.empty((n, m))
            if not trans:
                A = A.T
                E = E.T
                B, C = C.T, B.T
            out = slycot.sg02ad(dico, jobb, fact, uplo, jobl, scal, sort, acc,
                                n, m, p,
                                A, E, B, C, R, S)
            X = out[1]
            rcond = out[0]
            _ricc_rcond_check('slycot.sg02ad', rcond)

        return A_source.from_numpy(_chol(X).T)
Beispiel #12
0
    def solve_ricc_lrcf(A, E, B, C, R=None, S=None, trans=False, options=None, default_solver=None):
        """Compute an approximate low-rank solution of a Riccati equation.

        See :func:`pymor.algorithms.riccati.solve_ricc_lrcf` for a
        general description.

        This function uses `pymess.dense_nm_gmpcare` and `pymess.lrnm`.
        For both methods,
        :meth:`~pymor.vectorarrays.interfaces.VectorArrayInterface.to_numpy`
        and
        :meth:`~pymor.vectorarrays.interfaces.VectorSpaceInterface.from_numpy`
        need to be implemented for `A.source`.
        Additionally, since `dense_nm_gmpcare` is a dense solver, it
        expects :func:`~pymor.algorithms.to_matrix.to_matrix` to work
        for A and E.

        If the solver is not specified using the options or
        default_solver arguments, `dense_nm_gmpcare` is used for small
        problems (smaller than defined with
        :func:`~pymor.algorithms.lyapunov.mat_eqn_sparse_min_size`) and
        `lrnm` for large problems.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        C
            The operator C as a |VectorArray| from `A.source`.
        R
            The operator R as a 2D |NumPy array| or `None`.
        S
            The operator S as a |VectorArray| from `A.source` or `None`.
        trans
            Whether the first |Operator| in the Riccati equation is
            transposed.
        options
            The solver options to use (see
            :func:`ricc_lrcf_solver_options`).
        default_solver
            Default solver to use (pymess_lrnm,
            pymess_dense_nm_gmpcare).
            If `None`, chose solver depending on dimension `A`.

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Riccati equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_ricc_check_args(A, E, B, C, R, S, trans)
        if default_solver is None:
            default_solver = 'pymess_lrnm' if A.source.dim >= mat_eqn_sparse_min_size() else 'pymess_dense_nm_gmpcare'
        options = _parse_options(options, ricc_lrcf_solver_options(), default_solver, None, False)

        if options['type'] == 'pymess_dense_nm_gmpcare':
            X = _call_pymess_dense_nm_gmpare(A, E, B, C, R, S, trans=trans, options=options['opts'], plus=False)
            Z = _chol(X)
        elif options['type'] == 'pymess_lrnm':
            if S is not None:
                raise NotImplementedError
            if R is not None:
                import scipy.linalg as spla
                Rc = spla.cholesky(R)                                 # R = Rc^T * Rc
                Rci = spla.solve_triangular(Rc, np.eye(Rc.shape[0]))  # R^{-1} = Rci * Rci^T
                if not trans:
                    C = C.lincomb(Rci.T)  # C <- Rci^T * C = (C^T * Rci)^T
                else:
                    B = B.lincomb(Rci.T)  # B <- B * Rci
            opts = options['opts']
            opts.type = pymess.MESS_OP_NONE if not trans else pymess.MESS_OP_TRANSPOSE
            eqn = RiccatiEquation(opts, A, E, B, C)
            Z, status = pymess.lrnm(eqn, opts)
        else:
            raise ValueError(f'Unexpected Riccati equation solver ({options["type"]}).')

        return A.source.from_numpy(Z.T)
Beispiel #13
0
    def solve_lyap_lrcf(A, E, B, trans=False, options=None, default_solver=None):
        """Compute an approximate low-rank solution of a Lyapunov equation.

        See :func:`pymor.algorithms.lyapunov.solve_lyap_lrcf` for a
        general description.

        This function uses `pymess.glyap` and `pymess.lradi`.
        For both methods,
        :meth:`~pymor.vectorarrays.interfaces.VectorArrayInterface.to_numpy`
        and
        :meth:`~pymor.vectorarrays.interfaces.VectorSpaceInterface.from_numpy`
        need to be implemented for `A.source`.
        Additionally, since `glyap` is a dense solver, it expects
        :func:`~pymor.algorithms.to_matrix.to_matrix` to work for A and
        E.

        If the solver is not specified using the options or
        default_solver arguments, `glyap` is used for small problems
        (smaller than defined with
        :func:`~pymor.algorithms.lyapunov.mat_eqn_sparse_min_size`) and
        `lradi` for large problems.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The operator B as a |VectorArray| from `A.source`.
        trans
            Whether the first |Operator| in the Lyapunov equation is
            transposed.
        options
            The solver options to use (see
            :func:`lyap_lrcf_solver_options`).
        default_solver
            Default solver to use (pymess_lradi, pymess_glyap).
            If `None`, choose solver depending on the dimension of A.

        Returns
        -------
        Z
            Low-rank Cholesky factor of the Lyapunov equation solution,
            |VectorArray| from `A.source`.
        """

        _solve_lyap_lrcf_check_args(A, E, B, trans)
        if default_solver is None:
            default_solver = 'pymess_lradi' if A.source.dim >= mat_eqn_sparse_min_size() else 'pymess_glyap'
        options = _parse_options(options, lyap_lrcf_solver_options(), default_solver, None, False)

        if options['type'] == 'pymess_glyap':
            X = solve_lyap_dense(to_matrix(A, format='dense'),
                                 to_matrix(E, format='dense') if E else None,
                                 B.to_numpy().T if not trans else B.to_numpy(),
                                 trans=trans, options=options)
            Z = _chol(X)
        elif options['type'] == 'pymess_lradi':
            opts = options['opts']
            opts.type = pymess.MESS_OP_NONE if not trans else pymess.MESS_OP_TRANSPOSE
            eqn = LyapunovEquation(opts, A, E, B)
            Z, status = pymess.lradi(eqn, opts)
        else:
            raise ValueError(f'Unexpected Lyapunov equation solver ({options["type"]}).')

        return A.source.from_numpy(Z.T)
Beispiel #14
0
def solve_ricc_lrcf(A, E, B, C, R=None, S=None, trans=False, options=None):
    """Compute an approximate low-rank solution of a Riccati equation.

    See :func:`pymor.algorithms.riccati.solve_ricc_lrcf` for a general
    description.

    This function uses `scipy.linalg.solve_continuous_are`, which
    is a dense solver.
    Therefore, we assume all |Operators| and |VectorArrays| can be
    converted to |NumPy arrays| using
    :func:`~pymor.algorithms.to_matrix.to_matrix` and
    :func:`~pymor.vectorarrays.interfaces.VectorArrayInterface.to_numpy`.

    Parameters
    ----------
    A
        The |Operator| A.
    E
        The |Operator| E or `None`.
    B
        The operator B as a |VectorArray| from `A.source`.
    C
        The operator C as a |VectorArray| from `A.source`.
    R
        The operator R as a 2D |NumPy array| or `None`.
    S
        The operator S as a |VectorArray| from `A.source` or `None`.
    trans
        Whether the first |Operator| in the Riccati equation is
        transposed.
    options
        The solver options to use (see :func:`ricc_lrcf_solver_options`).

    Returns
    -------
    Z
        Low-rank Cholesky factor of the Riccati equation solution,
        |VectorArray| from `A.source`.
    """

    _solve_ricc_check_args(A, E, B, C, R, S, trans)
    options = _parse_options(options, ricc_lrcf_solver_options(), 'scipy', None, False)
    if options['type'] != 'scipy':
        raise ValueError(f"Unexpected Riccati equation solver ({options['type']}).")

    A_source = A.source
    A = to_matrix(A, format='dense')
    E = to_matrix(E, format='dense') if E else None
    B = B.to_numpy().T
    C = C.to_numpy()
    S = S.to_numpy().T if S else None
    if R is None:
        R = np.eye(C.shape[0] if not trans else B.shape[1])
    if not trans:
        if E is not None:
            E = E.T
        X = solve_continuous_are(A.T, C.T, B.dot(B.T), R, E, S)
    else:
        X = solve_continuous_are(A, B, C.T.dot(C), R, E, S)

    return A_source.from_numpy(_chol(X).T)