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
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
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])
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])
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
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)
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
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
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)
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)
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
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