Exemplo n.º 1
0
def dlyap_slycot(a, q):
    """Solves the discrete Lyapunov using the SLICOT library's implementation
    if available.  The routine attempts to call SB03MD to solve the discrete
    equation.  If a NameError is thrown, meaning SLICOT is not available,
    an appropriate RuntimeError is raised.
    
    More on SLICOT: http://www.slicot.org/
    
    Python Interface (Slycot): https://github.com/avventi/Slycot
    """

    x = None

    (m, n) = a.shape
    if m != n:
        raise ValueError("input 'a' must be square")

    try:
        x, scale, sep, ferr, w = slycot.sb03md(n,
                                               -q,
                                               a,
                                               numpy.eye(n),
                                               'D',
                                               trana='T')
    except NameError:
        raise RuntimeError('SLICOT not available')

    return x
Exemplo n.º 2
0
    def solve_lyap_dense(A, E, B, trans=False, options=None):
        """Compute the solution of a Lyapunov equation.

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

        This function uses `slycot.sb03md` (if `E is None`) and
        `slycot.sg03ad` (if `E is not None`), which are based on the
        Bartels-Stewart algorithm.

        Parameters
        ----------
        A
            The operator A as a 2D |NumPy array|.
        E
            The operator E as a 2D |NumPy array| or `None`.
        B
            The operator B as a 2D |NumPy array|.
        trans
            Whether the first operator in the Lyapunov equation is
            transposed.
        options
            The solver options to use (see
            :func:`lyap_dense_solver_options`).

        Returns
        -------
        X
            Lyapunov equation solution as a |NumPy array|.
        """

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

        if options['type'] == 'slycot_bartels-stewart':
            n = A.shape[0]
            C = -B.dot(B.T) if not trans else -B.T.dot(B)
            trana = 'T' if not trans else 'N'
            dico = 'C'
            job = 'B'
            if E is None:
                U = np.zeros((n, n))
                X, scale, sep, ferr, _ = slycot.sb03md(n, C, A, U, dico, job=job, trana=trana)
                _solve_check(A.dtype, 'slycot.sb03md', sep, ferr)
            else:
                fact = 'N'
                uplo = 'L'
                Q = np.zeros((n, n))
                Z = np.zeros((n, n))
                _, _, _, _, X, scale, sep, ferr, _, _, _ = slycot.sg03ad(dico, job, fact, trana, uplo,
                                                                         n, A, E,
                                                                         Q, Z, C)
                _solve_check(A.dtype, 'slycot.sg03ad', sep, ferr)
            X /= scale
        else:
            raise ValueError(f"Unexpected Lyapunov equation solver ({options['type']}).")

        return X
Exemplo n.º 3
0
    def solve_lyap_dense(A, E, B, trans=False, options=None):
        """Compute the solution of a Lyapunov equation.

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

        This function uses `slycot.sb03md` (if `E is None`) and
        `slycot.sg03ad` (if `E is not None`), which are based on the
        Bartels-Stewart algorithm.

        Parameters
        ----------
        A
            The operator A as a 2D |NumPy array|.
        E
            The operator E as a 2D |NumPy array| or `None`.
        B
            The operator B as a 2D |NumPy array|.
        trans
            Whether the first operator in the Lyapunov equation is
            transposed.
        options
            The solver options to use (see
            :func:`lyap_dense_solver_options`).

        Returns
        -------
        X
            Lyapunov equation solution as a |NumPy array|.
        """

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

        if options['type'] == 'slycot_bartels-stewart':
            n = A.shape[0]
            C = -B.dot(B.T) if not trans else -B.T.dot(B)
            trana = 'T' if not trans else 'N'
            dico = 'C'
            job = 'B'
            if E is None:
                U = np.zeros((n, n))
                X, scale, sep, ferr, _ = slycot.sb03md(n, C, A, U, dico, job=job, trana=trana)
                _solve_check(A.dtype, 'slycot.sb03md', sep, ferr)
            else:
                fact = 'N'
                uplo = 'L'
                Q = np.zeros((n, n))
                Z = np.zeros((n, n))
                _, _, _, _, X, scale, sep, ferr, _, _, _ = slycot.sg03ad(dico, job, fact, trana, uplo,
                                                                         n, A, E,
                                                                         Q, Z, C)
                _solve_check(A.dtype, 'slycot.sg03ad', sep, ferr)
            X /= scale
        else:
            raise ValueError(f"Unexpected Lyapunov equation solver ({options['type']}).")

        return X
Exemplo n.º 4
0
def sb03md_example():
    from numpy import zeros
    A = array([[3, 1, 1], [1, 3, 0], [0, 0, 3]])
    C = array([[25, 24, 15], [24, 32, 8], [15, 8, 40]])
    U = zeros((3, 3))
    out = slycot.sb03md(3, C, A, U, 'D')
    print('--- Example for sb03md ---')
    print('The solution X is')
    print(out[0])
    print('scaling factor:', out[1])
Exemplo n.º 5
0
def sb03md_example():
    from numpy import zeros
    A = array([ [3, 1, 1],
                [1, 3, 0],
                [0, 0, 3]])
    C = array([ [25, 24, 15],
                [24, 32,  8],
                [15,  8, 40]])
    U = zeros((3,3))
    out = slycot.sb03md(3,C,A,U,'D')
    print('--- Example for sb03md ---')   
    print('The solution X is')
    print(out[0])
    print('scaling factor:', out[1])
Exemplo n.º 6
0
def dlyap_slycot(a,q):
    """Solves the discrete Lyapunov using the SLICOT library's implementation
    if available.  The routine attempts to call SB03MD to solve the discrete
    equation.  If a NameError is thrown, meaning SLICOT is not available,
    an appropriate RuntimeError is raised.
    
    More on SLICOT: http://www.slicot.org/
    
    Python Interface (Slycot): https://github.com/avventi/Slycot
    """

    x = None
    
    (m,n) = a.shape
    if m != n:
        raise ValueError("input 'a' must be square") 
    
    try:
        x,scale,sep,ferr,w = slycot.sb03md(n, -q, a, numpy.eye(n), 'D', trana='T')
    except NameError:
        raise RuntimeError('SLICOT not available')
    
    return x
Exemplo n.º 7
0
def gram(sys, type):
    """Gramian (controllability or observability)

    Parameters
    ----------
    sys : StateSpace
        System description
    type : String
        Type of desired computation.  `type` is either 'c' (controllability)
        or 'o' (observability). To compute the Cholesky factors of Gramians
        use 'cf' (controllability) or 'of' (observability)

    Returns
    -------
    gram : 2D array (or matrix)
        Gramian of system

    Raises
    ------
    ValueError
        * if system is not instance of StateSpace class
        * if `type` is not 'c', 'o', 'cf' or 'of'
        * if system is unstable (sys.A has eigenvalues not in left half plane)

    ControlSlycot
        if slycot routine sb03md cannot be found
        if slycot routine sb03od cannot be found

    Notes
    -----
    The return type for 2D arrays depends on the default class set for
    state space operations.  See :func:`~control.use_numpy_matrix`.

    Examples
    --------
    >>> Wc = gram(sys, 'c')
    >>> Wo = gram(sys, 'o')
    >>> Rc = gram(sys, 'cf'), where Wc = Rc' * Rc
    >>> Ro = gram(sys, 'of'), where Wo = Ro' * Ro

    """

    # Check for ss system object
    if not isinstance(sys, statesp.StateSpace):
        raise ValueError("System must be StateSpace!")
    if type not in ['c', 'o', 'cf', 'of']:
        raise ValueError("That type is not supported!")

    # TODO: Check for continuous or discrete, only continuous supported for now
        # if isCont():
        #    dico = 'C'
        # elif isDisc():
        #    dico = 'D'
        # else:
    dico = 'C'

    # TODO: Check system is stable, perhaps a utility in ctrlutil.py
    # or a method of the StateSpace class?
    if np.any(np.linalg.eigvals(sys.A).real >= 0.0):
        raise ValueError("Oops, the system is unstable!")

    if type == 'c' or type == 'o':
        # Compute Gramian by the Slycot routine sb03md
        # make sure Slycot is installed
        if sb03md is None:
            raise ControlSlycot("can't find slycot module 'sb03md'")
        if type == 'c':
            tra = 'T'
            C = -sys.B @ sys.B.T
        elif type == 'o':
            tra = 'N'
            C = -sys.C.T @ sys.C
        n = sys.nstates
        U = np.zeros((n, n))
        A = np.array(sys.A)         # convert to NumPy array for slycot
        X, scale, sep, ferr, w = sb03md(
            n, C, A, U, dico, job='X', fact='N', trana=tra)
        gram = X
        return _ssmatrix(gram)

    elif type == 'cf' or type == 'of':
        # Compute cholesky factored gramian from slycot routine sb03od
        if sb03od is None:
            raise ControlSlycot("can't find slycot module 'sb03od'")
        tra = 'N'
        n = sys.nstates
        Q = np.zeros((n, n))
        A = np.array(sys.A)         # convert to NumPy array for slycot
        if type == 'cf':
            m = sys.B.shape[1]
            B = np.zeros_like(A)
            B[0:m, 0:n] = sys.B.transpose()
            X, scale, w = sb03od(
                n, m, A.transpose(), Q, B, dico, fact='N', trans=tra)
        elif type == 'of':
            m = sys.C.shape[0]
            C = np.zeros_like(A)
            C[0:n, 0:m] = sys.C.transpose()
            X, scale, w = sb03od(
                n, m, A, Q, C.transpose(), dico, fact='N', trans=tra)
        gram = X
        return _ssmatrix(gram)
Exemplo n.º 8
0
def lyap(A,Q,C=None,E=None):
    """ X = lyap(A,Q) solves the continuous-time Lyapunov equation
    
        A X + X A^T + Q = 0

    where A and Q are square matrices of the same dimension. 
    Further, Q must be symmetric.

    X = lyap(A,Q,C) solves the Sylvester equation

        A X + X Q + C = 0

    where A and Q are square matrices.

    X = lyap(A,Q,None,E) solves the generalized continuous-time
    Lyapunov equation

        A X E^T + E X A^T + Q = 0

    where Q is a symmetric matrix and A, Q and E are square matrices
    of the same dimension. """

    # Make sure we have access to the right slycot routines
    try:
        from slycot import sb03md
    except ImportError:
        raise ControlSlycot("can't find slycot module 'sb03md'")

    try:
        from slycot import sb04md
    except ImportError:
        raise ControlSlycot("can't find slycot module 'sb04md'")

    # Reshape 1-d arrays
    if len(shape(A)) == 1:
        A = A.reshape(1,A.size)

    if len(shape(Q)) == 1:
        Q = Q.reshape(1,Q.size)

    if C != None and len(shape(C)) == 1:
        C = C.reshape(1,C.size)

    if E != None and len(shape(E)) == 1:
        E = E.reshape(1,E.size)

    # Determine main dimensions
    if size(A) == 1:
        n = 1
    else:
        n = size(A,0)

    if size(Q) == 1:
        m = 1
    else:
        m = size(Q,0)

    # Solve standard Lyapunov equation
    if C==None and E==None:
        # Check input data for consistency
        if shape(A) != shape(Q):
            raise ControlArgument("A and Q must be matrices of identical \
                                sizes.")

        if size(A) > 1 and shape(A)[0] != shape(A)[1]:
            raise ControlArgument("A must be a quadratic matrix.")

        if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
            raise ControlArgument("Q must be a quadratic matrix.")

        if not (asarray(Q) == asarray(Q).T).all():
            raise ControlArgument("Q must be a symmetric matrix.")

        # Solve the Lyapunov equation by calling Slycot function sb03md
        try:
            X,scale,sep,ferr,w = sb03md(n,-Q,A,eye(n,n),'C',trana='T')
        except ValueError(ve):
            if ve.info < 0:
                e = ValueError(ve.message)
                e.info = ve.info
            elif ve.info == n+1:
                e = ValueError("The matrix A and -A have common or very \
                    close eigenvalues.")
                e.info = ve.info
            else:
                e = ValueError("The QR algorithm failed to compute all \
                    the eigenvalues (see LAPACK Library routine DGEES).")
                e.info = ve.info
            raise e

    # Solve the Sylvester equation
    elif C != None and E==None:
        # Check input data for consistency
        if size(A) > 1 and shape(A)[0] != shape(A)[1]:
            raise ControlArgument("A must be a quadratic matrix.")

        if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
            raise ControlArgument("Q must be a quadratic matrix.")

        if (size(C) > 1 and shape(C)[0] != n) or \
            (size(C) > 1 and shape(C)[1] != m) or \
            (size(C) == 1 and size(A) != 1) or (size(C) == 1 and size(Q) != 1):
            raise ControlArgument("C matrix has incompatible dimensions.")

        # Solve the Sylvester equation by calling the Slycot function sb04md
        try:
            X = sb04md(n,m,A,Q,-C)
        except ValueError(ve):
            if ve.info < 0:
                e = ValueError(ve.message)
                e.info = ve.info
            elif ve.info > m:
                e = ValueError("A singular matrix was encountered whilst \
                    solving for the %i-th column of matrix X." % ve.info-m)
                e.info = ve.info
            else:
                e = ValueError("The QR algorithm failed to compute all the \
                    eigenvalues (see LAPACK Library routine DGEES).")
                e.info = ve.info
            raise e

    # Solve the generalized Lyapunov equation
    elif C == None and E != None:
        # Check input data for consistency
        if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
            (size(Q) > 1 and shape(Q)[0] != n) or \
            (size(Q) == 1 and n > 1):
            raise ControlArgument("Q must be a square matrix with the same \
                dimension as A.")

        if (size(E) > 1 and shape(E)[0] != shape(E)[1]) or \
            (size(E) > 1 and shape(E)[0] != n) or \
            (size(E) == 1 and n > 1):
            raise ControlArgument("E must be a square matrix with the same \
                dimension as A.")

        if not (asarray(Q) == asarray(Q).T).all():
            raise ControlArgument("Q must be a symmetric matrix.")

        # Make sure we have access to the write slicot routine
        try:
            from slycot import sg03ad
        except ImportError:
            raise ControlSlycot("can't find slycot module 'sg03ad'")

        # Solve the generalized Lyapunov equation by calling Slycot 
        # function sg03ad
        try:
            A,E,Q,Z,X,scale,sep,ferr,alphar,alphai,beta = \
                sg03ad('C','B','N','T','L',n,A,E,eye(n,n),eye(n,n),-Q)
        except ValueError(ve):
            if ve.info < 0 or ve.info > 4:
                e = ValueError(ve.message)
                e.info = ve.info
            elif ve.info == 1:
                e = ValueError("The matrix contained in the upper \
                                Hessenberg part of the array A is not in \
                                upper quasitriangular form")
                e.info = ve.info
            elif ve.info == 2:
                e = ValueError("The pencil A - lambda * E cannot be \
                                reduced to generalized Schur form: LAPACK \
                                routine DGEGS has failed to converge")
                e.info = ve.info
            elif ve.info == 4:
                e = ValueError("The pencil A - lambda * E has a \
                                degenerate pair of eigenvalues. That is, \
                                lambda_i = lambda_j for some i and j, where \
                                lambda_i and lambda_j are eigenvalues of \
                                A - lambda * E. Hence, the equation is \
                                singular;  perturbed values were \
                                used to solve the equation (but the matrices \
                                A and E are unchanged)")
                e.info = ve.info
            raise e    
    # Invalid set of input parameters    
    else:
        raise ControlArgument("Invalid set of input parameters")
            
    return X
Exemplo n.º 9
0
def gram(sys, type):
    """Gramian (controllability or observability)

    Parameters
    ----------
    sys: StateSpace
        State-space system to compute Gramian for
    type: String
        Type of desired computation.
        `type` is either 'c' (controllability) or 'o' (observability).

    Returns
    -------
    gram: array
        Gramian of system

    Raises
    ------
    ValueError
        * if system is not instance of StateSpace class
        * if `type` is not 'c' or 'o'
        * if system is unstable (sys.A has eigenvalues not in left half plane)

    ImportError
        if slycot routin sb03md cannot be found

    Examples
    --------
    >>> Wc = gram(sys,'c')
    >>> Wo = gram(sys,'o')

    """

    #Check for ss system object
    if not isinstance(sys, statesp.StateSpace):
        raise ValueError("System must be StateSpace!")

    #TODO: Check for continous or discrete, only continuous supported right now
    # if isCont():
    #    dico = 'C'
    # elif isDisc():
    #    dico = 'D'
    # else:
    dico = 'C'

    #TODO: Check system is stable, perhaps a utility in ctrlutil.py
    # or a method of the StateSpace class?
    D, V = np.linalg.eig(sys.A)
    for e in D:
        if e.real >= 0:
            raise ValueError("Oops, the system is unstable!")
    if type == 'c':
        tra = 'T'
        C = -np.dot(sys.B, sys.B.transpose())
    elif type == 'o':
        tra = 'N'
        C = -np.dot(sys.C.transpose(), sys.C)
    else:
        raise ValueError("Oops, neither observable, nor controllable!")

    #Compute Gramian by the Slycot routine sb03md
    #make sure Slycot is installed
    try:
        from slycot import sb03md
    except ImportError:
        raise ControlSlycot("can't find slycot module 'sb03md'")
    n = sys.states
    U = np.zeros((n, n))
    A = np.array(sys.A)  # convert to NumPy array for slycot
    X, scale, sep, ferr, w = sb03md(n,
                                    C,
                                    A,
                                    U,
                                    dico,
                                    job='X',
                                    fact='N',
                                    trana=tra)
    gram = X
    return gram
Exemplo n.º 10
0
def dlyap(A, Q, C=None, E=None):
    """ dlyap(A,Q) solves the discrete-time Lyapunov equation

        :math:`A X A^T - X + Q = 0`

    where A and Q are square matrices of the same dimension. Further
    Q must be symmetric.

    dlyap(A,Q,C) solves the Sylvester equation

        :math:`A X Q^T - X + C = 0`

    where A and Q are square matrices.

    dlyap(A,Q,None,E) solves the generalized discrete-time Lyapunov
    equation

        :math:`A X A^T - E X E^T + Q = 0`

    where Q is a symmetric matrix and A, Q and E are square matrices
    of the same dimension. """

    # Make sure we have access to the right slycot routines
    if sb03md is None:
        raise ControlSlycot("can't find slycot module 'sb03md'")
    if sb04qd is None:
        raise ControlSlycot("can't find slycot module 'sb04qd'")
    if sg03ad is None:
        raise ControlSlycot("can't find slycot module 'sg03ad'")

    # Reshape 1-d arrays
    if len(shape(A)) == 1:
        A = A.reshape(1, A.size)

    if len(shape(Q)) == 1:
        Q = Q.reshape(1, Q.size)

    if C is not None and len(shape(C)) == 1:
        C = C.reshape(1, C.size)

    if E is not None and len(shape(E)) == 1:
        E = E.reshape(1, E.size)

    # Determine main dimensions
    if size(A) == 1:
        n = 1
    else:
        n = size(A, 0)

    if size(Q) == 1:
        m = 1
    else:
        m = size(Q, 0)

    # Solve standard Lyapunov equation
    if C is None and E is None:
        # Check input data for consistency
        if shape(A) != shape(Q):
            raise ControlArgument("A and Q must be matrices of identical \
                                 sizes.")

        if size(A) > 1 and shape(A)[0] != shape(A)[1]:
            raise ControlArgument("A must be a quadratic matrix.")

        if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
            raise ControlArgument("Q must be a quadratic matrix.")

        if not _is_symmetric(Q):
            raise ControlArgument("Q must be a symmetric matrix.")

        # Solve the Lyapunov equation by calling the Slycot function sb03md
        try:
            X, scale, sep, ferr, w = \
                sb03md(n, -Q, A, eye(n, n), 'D', trana='T')
        except ValueError as ve:
            if ve.info < 0:
                e = ValueError(ve.message)
                e.info = ve.info
            else:
                e = ValueError("The QR algorithm failed to compute all the \
                    eigenvalues (see LAPACK Library routine DGEES).")
                e.info = ve.info
            raise e

    # Solve the Sylvester equation
    elif C is not None and E is None:
        # Check input data for consistency
        if size(A) > 1 and shape(A)[0] != shape(A)[1]:
            raise ControlArgument("A must be a quadratic matrix")

        if size(Q) > 1 and shape(Q)[0] != shape(Q)[1]:
            raise ControlArgument("Q must be a quadratic matrix")

        if (size(C) > 1 and shape(C)[0] != n) or \
           (size(C) > 1 and shape(C)[1] != m) or \
           (size(C) == 1 and size(A) != 1) or (size(C) == 1 and size(Q) != 1):
            raise ControlArgument("C matrix has incompatible dimensions")

        # Solve the Sylvester equation by calling Slycot function sb04qd
        try:
            X = sb04qd(n, m, -A, asarray(Q).T, C)
        except ValueError as ve:
            if ve.info < 0:
                e = ValueError(ve.message)
                e.info = ve.info
            elif ve.info > m:
                e = ValueError("A singular matrix was encountered whilst \
                    solving for the %i-th column of matrix X." % ve.info - m)
                e.info = ve.info
            else:
                e = ValueError("The QR algorithm failed to compute all the \
                    eigenvalues (see LAPACK Library routine DGEES)")
                e.info = ve.info
            raise e

    # Solve the generalized Lyapunov equation
    elif C is None and E is not None:
        # Check input data for consistency
        if (size(Q) > 1 and shape(Q)[0] != shape(Q)[1]) or \
           (size(Q) > 1 and shape(Q)[0] != n) or \
           (size(Q) == 1 and n > 1):
            raise ControlArgument("Q must be a square matrix with the same \
                dimension as A.")

        if (size(E) > 1 and shape(E)[0] != shape(E)[1]) or \
           (size(E) > 1 and shape(E)[0] != n) or \
           (size(E) == 1 and n > 1):
            raise ControlArgument("E must be a square matrix with the same \
                dimension as A.")

        if not _is_symmetric(Q):
            raise ControlArgument("Q must be a symmetric matrix.")

        # Solve the generalized Lyapunov equation by calling Slycot
        # function sg03ad
        try:
            A, E, Q, Z, X, scale, sep, ferr, alphar, alphai, beta = \
                sg03ad('D', 'B', 'N', 'T', 'L', n,
                       A, E, eye(n, n), eye(n, n), -Q)
        except ValueError as ve:
            if ve.info < 0 or ve.info > 4:
                e = ValueError(ve.message)
                e.info = ve.info
            elif ve.info == 1:
                e = ValueError("The matrix contained in the upper \
                                Hessenberg part of the array A is not in \
                                upper quasitriangular form")
                e.info = ve.info
            elif ve.info == 2:
                e = ValueError("The pencil A - lambda * E cannot be \
                                reduced to generalized Schur form: LAPACK \
                                routine DGEGS has failed to converge")
                e.info = ve.info
            elif ve.info == 3:
                e = ValueError("The pencil A - lambda * E has a \
                                pair of reciprocal eigenvalues. That is, \
                                lambda_i = 1/lambda_j for some i and j, \
                                where  lambda_i and lambda_j are eigenvalues \
                                of A - lambda * E. Hence, the equation is \
                                singular;  perturbed values were \
                                used to solve the equation (but the \
                                matrices A and E are unchanged)")
                e.info = ve.info
            raise e
    # Invalid set of input parameters
    else:
        raise ControlArgument("Invalid set of input parameters")

    return _ssmatrix(X)
Exemplo n.º 11
0
    def calc_Wc(self, method=None):
        """
		Computes observers :math:`W_c`  with method 'method' :

		:math:`W_c` is solution of equation :
		.. math::
			A * W_c * A^T + B * B^T = W_c

		Available methods :

		- ``linalg`` : ``scipy.linalg.solve_discrete_lyapunov``, 4-digit precision with small sizes,
		1 digit precision with bilinear algorithm for big matrixes (really bad).
		not good enough with usual python data types

		- ``slycot`` : using ``slycot`` lib with func ``sb03md``, like in [matlab ,pydare]
		see http://slicot.org/objects/software/shared/libindex.html

		- ``None`` (default) : use the default method defined in the dSS class (dSS._W_method)

		..Example::

			>>> mydSS = random_dSS() ## define a new state space from random data
			>>> mydSS.calc_Wc('linalg') # use numpy
			>>> mydSS.calc_Wc('slycot') # use slycot
			>>> mydSS.calc_Wo() # use the default method defined in dSS

		.. warning::

			solve_discrete_lyapunov does not work as intended, see
			http://stackoverflow.com/questions/16315645/am-i-using-scipy-linalg-solve-discrete-lyapunov-correctl
			Precision is not good (4 digits, failed tests)

		"""
        if method is None:
            method = dSS._W_method

        if method == 'linalg':
            try:
                X = solve_discrete_lyapunov(self._A,
                                            self._B * self._B.transpose())
                self._Wc = mat(X)

            except LinAlgError as ve:
                if ve.info < 0:
                    e = LinAlgError(ve.message)
                    e.info = ve.info
                else:
                    e = LinAlgError(
                        "dSS: Wc: scipy Linalg failed to compute eigenvalues of Lyapunov equation"
                    )
                    e.info = ve.info
                raise e

        elif method == 'slycot':
            # Solve the Lyapunov equation by calling the Slycot function sb03md
            # If we don't use "copy" in the call, the result is plain false

            try:
                X, scale, sep, ferr, w = sb03md(self.n,
                                                -self._B * self._B.transpose(),
                                                copy(self._A),
                                                eye(self.n, self.n),
                                                dico='D',
                                                trana='T')
                self._Wc = mat(X)

            except ValueError as ve:

                if ve.info < 0:
                    e = ValueError(ve.message)
                    e.info = ve.info
                else:
                    e = ValueError(
                        "dSS: Wc: The QR algorithm failed to compute all the eigenvalues "
                        "(see LAPACK Library routine DGEES).")
                    e.info = ve.info
                raise e
            except NameError:
                return self.calc_Wc(method='linalg')

        else:
            raise ValueError(
                "dSS: Unknown method to calculate observers (method=%s)" %
                method)
Exemplo n.º 12
0
def gram(sys,type):
    """Gramian (controllability or observability)

    Parameters
    ----------
    sys: StateSpace
        State-space system to compute Gramian for
    type: String
        Type of desired computation.
        `type` is either 'c' (controllability) or 'o' (observability).

    Returns
    -------
    gram: array
        Gramian of system

    Raises
    ------
    ValueError
        * if system is not instance of StateSpace class
        * if `type` is not 'c' or 'o'
        * if system is unstable (sys.A has eigenvalues not in left half plane)

    ImportError
        if slycot routin sb03md cannot be found

    Examples
    --------
    >>> Wc = gram(sys,'c')
    >>> Wo = gram(sys,'o')

    """

    #Check for ss system object
    if not isinstance(sys,statesp.StateSpace):
        raise ValueError("System must be StateSpace!")

    #TODO: Check for continous or discrete, only continuous supported right now
        # if isCont():
        #    dico = 'C'
        # elif isDisc():
        #    dico = 'D'
        # else:
    dico = 'C'

    #TODO: Check system is stable, perhaps a utility in ctrlutil.py
        # or a method of the StateSpace class?
    if np.any(np.linalg.eigvals(sys.A).real >= 0.0):
        raise ValueError("Oops, the system is unstable!")

    if type=='c':
        tra = 'T'
        C = -np.dot(sys.B,sys.B.transpose())
    elif type=='o':
        tra = 'N'
        C = -np.dot(sys.C.transpose(),sys.C)
    else:
        raise ValueError("Oops, neither observable, nor controllable!")

    #Compute Gramian by the Slycot routine sb03md
        #make sure Slycot is installed
    try:
        from slycot import sb03md
    except ImportError:
        raise ControlSlycot("can't find slycot module 'sb03md'")
    n = sys.states
    U = np.zeros((n,n))
    A = np.array(sys.A)         # convert to NumPy array for slycot
    X,scale,sep,ferr,w = sb03md(n, C, A, U, dico, job='X', fact='N', trana=tra)
    gram = X
    return gram
Exemplo n.º 13
0
    def solve_lyap(A, E, B, trans=False, options=None):
        """Find a factor of the solution of a Lyapunov equation.

        Returns factor :math:`Z` such that :math:`Z Z^T` is
        approximately the solution :math:`X` of a Lyapunov equation (if
        E is `None`).

        .. math::
            A X + X A^T + B B^T = 0

        or generalized Lyapunov equation

        .. math::
            A X E^T + E X A^T + B B^T = 0.

        If trans is `True`, then it solves (if E is `None`)

        .. math::
            A^T X + X A + B^T B = 0

        or

        .. math::
            A^T X E + E^T X A + B^T B = 0.

        This uses the `slycot` package, in particular its interfaces to
        SLICOT functions `SB03MD` (for the standard Lyapunov equations)
        and `SG03AD` (for the generalized Lyapunov equations).
        These methods are only applicable to medium-sized dense
        problems and need access to the matrix data of all operators.

        Parameters
        ----------
        A
            The |Operator| A.
        E
            The |Operator| E or `None`.
        B
            The |Operator| B.
        trans
            If the dual equation needs to be solved.
        options
            The |solver_options| to use (see
            :func:`lyap_solver_options`).

        Returns
        -------
        Z
            Low-rank factor of the Lyapunov equation solution, |VectorArray| from `A.source`.
        """
        _solve_lyap_check_args(A, E, B, trans)
        options = _parse_options(options, lyap_solver_options(), 'slycot', None, False)
        assert options['type'] == 'slycot'

        import slycot
        A_mat = to_matrix(A, format='dense')
        if E is not None:
            E_mat = to_matrix(E, format='dense')
        B_mat = to_matrix(B, format='dense')

        n = A_mat.shape[0]
        if not trans:
            C = -B_mat.dot(B_mat.T)
            trana = 'T'
        else:
            C = -B_mat.T.dot(B_mat)
            trana = 'N'
        dico = 'C'

        if E is None:
            U = np.zeros((n, n))
            X, scale, _, _, _ = slycot.sb03md(n, C, A_mat, U, dico, trana=trana)
        else:
            job = 'B'
            fact = 'N'
            Q = np.zeros((n, n))
            Z = np.zeros((n, n))
            uplo = 'L'
            X = C
            _, _, _, _, X, scale, _, _, _, _, _ = slycot.sg03ad(dico, job, fact, trana, uplo, n, A_mat, E_mat,
                                                                Q, Z, X)

        from pymor.bindings.scipy import chol
        Z = chol(X, copy=False)

        Z = A.source.from_numpy(np.array(Z).T)

        return Z