def template_riccati(self, rhs, para): """template to reduce code in the test cases""" #set some options self.opt.adi.output = 0 self.opt.adi.res2_tol = 1e-10 self.opt.nm.output = 0 self.opt.nm.res2_tol = 1e-3 # setup equation if para < 0: eqn = MyEquation2(self.opt, self.a, self.e, rhs, self.b, self.c) z, status = lrnm(eqn, self.opt) else: eqn2 = MyEquation(self.opt, self.a, self.e, rhs, self.b, self.c) self.opt.adi.shifts.paratype = para z, status = lrnm(eqn2, self.opt) #get res2 from lrnm res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("it=%d\t rel_res2=%e\t res2=%e\t tol=%e\n" % (it, res2 / res2_0, res2, self.opt.nm.res2_tol)) self.assertLessEqual(res2 / res2_0, self.opt.nm.res2_tol) #check norm nrmr, nrmrhs, relnrm = res2_ric(self.a, self.e, self.b, self.c, z, self.opt.type) print("check:\t rel_res2=%e\t res2=%e\t nrmrhs=%e\n" % (relnrm, nrmr, nrmrhs)) # compare residual, should be roughly the same magnitude self.assertLessEqual(abs(log10(relnrm) - log10(res2 / res2_0)), 1)
def main(): """Solve standard/generalized Riccati equation.""" # read data e = mmread("@CMAKE_SOURCE_DIR@/tests/data/filter2D/filter2D.E").tocsr() a = mmread("@CMAKE_SOURCE_DIR@/tests/data/filter2D/filter2D.A").tocsr() b = mmread("@CMAKE_SOURCE_DIR@/tests/data/filter2D/filter2D.B") c = mmread("@CMAKE_SOURCE_DIR@/tests/data/filter2D/filter2D.C") #create opt instance opt1 = Options() opt1.nm.output = 1 opt1.adi.output = 0 opt1.nm.res2_tol = 1e-4 opt2 = Options() opt2.nm.output = 1 opt2.adi.output = 0 opt2.nm.res2_tol = 1e-4 # create standard and generalized equation eqn1 = EquationGRiccati(opt1, a, None, b, c) eqn2 = EquationGRiccati(opt2, a, e, b, c) # solve both equations z1, stat1 = lrnm(eqn1, opt1) z2, stat2 = lrnm(eqn2, opt2) print("Standard Riccati Equation \n") print("Size of Low Rank Solution Factor Z1: %d x %d \n"%(z1.shape)) print(stat1.lrnm_stat()) print(stat1) print("Generalized Riccati Equation \n") print("Size of Low Rank Solution Factor Z2: %d x %d \n"%(z2.shape)) print(stat2.lrnm_stat()) print(stat2)
def template_so1(self, mytype): """template to reduce code in the test cases""" #set type self.opt.type = mytype #get matrices n = self.m.shape[0] if self.opt.type == MESS_OP_NONE: b = np.random.rand(2 * n, 1) c = np.random.rand(1, n) else: b = np.random.rand(n, 1) c = np.random.rand(1, 2 * n) #build equation eqn = EquationGRiccatiSO1(self.opt, self.m, self.d, self.k, b, c, self.lowerbound, self.upperbound) #get res2 from lrnm _, status = lrnm(eqn, self.opt) res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("it=%d\t rel_res2=%e\t res2=%e\t tol=%e\n" % (it, res2 / res2_0, res2, self.opt.nm.res2_tol)) self.assertLessEqual(res2 / res2_0, self.opt.nm.res2_tol)
def template_dae2(self, mytype, re, mem_usage): """template to reduce code in the test cases""" #set type self.opt.type = mytype self.opt.adi.memory_usage = mem_usage #get matrices m = mmread(self.matmtx.format(re, 'M')) a = mmread(self.matmtx.format(re, 'A')) g = mmread(self.matmtx.format(re, 'G')) b = mmread(self.matmtx.format(re, 'B')) c = mmread(self.matmtx.format(re, 'C')) if self.opt.type == MESS_OP_NONE: if re >= 300: self.opt.nm.k0 = mmread(self.matmtx.format(re, 'Feed1')) else: if re >= 300: self.opt.nm.k0 = mmread(self.matmtx.format(re, 'Feed0')) #build equation eqn = EquationGRiccatiDAE2(self.opt, m, a, g, b, c, self.delta) #get res2 from lrnm _, status = lrnm(eqn, self.opt) res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("it=%d\t rel_res2=%e\t res2=%e\t tol=%e\n" % (it, res2 / res2_0, res2, self.opt.nm.res2_tol)) self.assertLessEqual(res2 / res2_0, self.opt.nm.res2_tol)
def main(): """Solve Riccati Equation DAE2 System.""" # read data m = mmread("@CMAKE_SOURCE_DIR@/tests/data/NSE/NSE_RE_100_lvl1_M.mtx").tocsr() a = mmread("@CMAKE_SOURCE_DIR@/tests/data/NSE/NSE_RE_100_lvl1_A.mtx").tocsr() g = mmread("@CMAKE_SOURCE_DIR@/tests/data/NSE/NSE_RE_100_lvl1_G.mtx").tocsr() b = mmread("@CMAKE_SOURCE_DIR@/tests/data/NSE/NSE_RE_100_lvl1_B.mtx") c = mmread("@CMAKE_SOURCE_DIR@/tests/data/NSE/NSE_RE_100_lvl1_C.mtx") delta = -0.02 #create opt instance opt = Options() opt.adi.output = 0 opt.nm.output = 0 opt.nm.res2_tol = 1e-2 opt.adi.paratype = MESS_LRCFADI_PARA_ADAPTIVE_V # create equation eqn = EquationGRiccatiDAE2(opt, m, a, g, b, c, delta) # solve equation z, status = lrnm(eqn, opt) # get residual res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("Size of Low Rank Solution Factor Z: %d x %d \n"%(z.shape)) print("it = %d \t rel_res2 = %e\t res2 = %e \n" % (it, res2 / res2_0, res2)) print(status.lrnm_stat())
def template_dae1(self, mytype): """template to reduce code in the test cases""" #set type self.opt.type = mytype #get matrices e11 = mmread(self.matmtx.format('E11')) a11 = mmread(self.matmtx.format('A11')) a12 = mmread(self.matmtx.format('A12')) a21 = mmread(self.matmtx.format('A21')) a22 = mmread(self.matmtx.format('A22')) np.random.seed(1111) if mytype == MESS_OP_TRANSPOSE: b = np.random.rand(e11.shape[0], 2) c = np.random.rand(2, e11.shape[1] + a22.shape[1]) else: b = np.random.rand(e11.shape[0] + a22.shape[0], 2) c = np.random.rand(2, e11.shape[1]) b = b / np.linalg.norm(b, 'fro') c = c / np.linalg.norm(c, 'fro') #build equation eqn = EquationGRiccatiDAE1(self.opt, e11, a11, a12, a21, a22, b, c) #get res2 from lrnm _, status = lrnm(eqn, self.opt) res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("it=%d\t rel_res2=%e\t res2=%e\t tol=%e\n" % (it, res2 / res2_0, res2, self.opt.nm.res2_tol)) self.assertLessEqual(res2 / res2_0, self.opt.nm.res2_tol)
def main(): """Demonstrate Callback functionality""" # read data a = mmread('@CMAKE_SOURCE_DIR@/tests/data/Rail/A.mtx') b = mmread('@CMAKE_SOURCE_DIR@/tests/data/Rail/B.mtx') c = mmread('@CMAKE_SOURCE_DIR@/tests/data/Rail/C.mtx') e = mmread('@CMAKE_SOURCE_DIR@/tests/data/Rail/E.mtx') # create options opt = Options() opt.nm.output = 0 opt.adi.output = 0 # create instance of MyEquation and solve opt.type = MESS_OP_NONE eqn1 = MyEquation(opt, a, e, b, c) z1, stat1 = lrnm(eqn1, opt) # create instance of MyEquation and solve opt.type = MESS_OP_TRANSPOSE eqn2 = MyEquation(opt, a, e, b, c) z2, stat2 = lrnm(eqn2, opt) # print information and compute residual again to make sure that we have everything correct print("\n") print("MyEquation: eqn1") print("Size of Low Rank Solution Factor Z1: %d x %d \n" % (z1.shape)) print(stat1.lrnm_stat()) nrmr1, nrmrhs1, relnrm1 = res2_ric(a, e, b, c, z1, MESS_OP_NONE) print("check for eqn1:\t rel_res2=%e\t res2=%e\t nrmrhs=%e\n" % (relnrm1, nrmr1, nrmrhs1)) print("\n") print( "--------------------------------------------------------------------------------------" ) print("\n") # print information and compute residual again to make sure that we have everything correct print("MyEquation: eqn2") print("Size of Low Rank Solution Factor Z2: %d x %d \n" % (z2.shape)) print(stat2.lrnm_stat()) nrmr2, nrmrhs2, relnrm2 = res2_ric(a, e, b, c, z2, MESS_OP_TRANSPOSE) print("check for eqn1:\t rel_res2=%e\t res2=%e\t nrmrhs=%e\n" % (relnrm2, nrmr2, nrmrhs2))
def pymess_dae2_cnt_riccati(mmat=None, amat=None, jmat=None, bmat=None, wmat=None, z0=None, mtxoldb=None, transposed=False, aditol=5e-10, nwtn_res2_tol=5e-8, maxit=20, verbose=False, linesearch=False, **kw): """ solve the projected algebraic ricc via newton adi `M.T*X*A + A.T*X*M - M.T*X*B*B.T*X*M + J(Y) = -WW.T` `JXM = 0 and M.TXJ.T = 0` If `mtxb` is given, (e.g. as the feedback computed in a previous step of a Newton iteration), the coefficient matrix with feedback `A.T <- A.T - mtxb*b` is considered """ import pymess optns = pymess.Options() optns.adi.res2_tol = aditol optns.adi.output = 0 if verbose: optns.nm.output = 1 if linesearch: optns.nm.linesearch = 1 optns.nm.maxit = maxit optns.nm.res2_tol = nwtn_res2_tol optns.adi.shifts.paratype = pymess.MESS_LRCFADI_PARA_ADAPTIVE_V optns.type = pymess.MESS_OP_TRANSPOSE # solve the cont Riccati!!! delta = -0.02 if z0 is not None: mtxoldb = mmat.T*lau.comp_uvz_spdns(z0, z0.T, bmat) if mtxoldb is not None: optns.nm.K0 = mtxoldb.T # initial stabilizing feedback ricceq = pymess.EquationGRiccatiDAE2(optns, mmat, amat, jmat.T, bmat, wmat.T, delta) Z, status = pymess.lrnm(ricceq, optns) nwtn_upd_fnorms = [] return dict(zfac=Z, nwtn_upd_fnorms=nwtn_upd_fnorms)
def template_filter(self, mytype, cnum, e): """template to reduce code in the test cases""" self.opt.type = mytype c = mmread(self.cmtx.format(cnum)).todense() e = mmread(self.emtx).tocsr() if e else None # build equation eqn = EquationGRiccati(self.opt, self.a, e, self.b, c) #get res2 from lrnm _, status = lrnm(eqn, self.opt) res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("it=%d\t rel_res2=%e\t res2=%e\t tol=%e\n" % (it, res2 / res2_0, res2, self.opt.nm.res2_tol)) self.assertLessEqual(res2 / res2_0, self.opt.nm.res2_tol)
def main(): """Solve Riccati Equation DAE1 System.""" # read data e11 = mmread("@CMAKE_SOURCE_DIR@/tests/data/bips98_606/E11.mtx").tocsr() a11 = mmread("@CMAKE_SOURCE_DIR@/tests/data/bips98_606/A11.mtx").tocsr() a12 = mmread("@CMAKE_SOURCE_DIR@/tests/data/bips98_606/A12.mtx").tocsr() a21 = mmread("@CMAKE_SOURCE_DIR@/tests/data/bips98_606/A21.mtx").tocsr() a22 = mmread("@CMAKE_SOURCE_DIR@/tests/data/bips98_606/A22.mtx").tocsr() #create opt instance opt = Options() opt.adi.output = 0 opt.nm.output = 0 opt.nm.res2_tol = 1e-10 # generate some matrices b and c seed(1111) if opt.type == MESS_OP_TRANSPOSE: b = rand(e11.shape[0], 2) c = rand(2, e11.shape[1] + a22.shape[1]) else: b = rand(e11.shape[0] + a22.shape[0], 2) c = rand(2, e11.shape[1]) b = b / norm(b, 'fro') c = c / norm(c, 'fro') # create equation eqn = EquationGRiccatiDAE1(opt, e11, a11, a12, a21, a22, b, c) # solve equation z, status = lrnm(eqn, opt) # get residual res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("Size of Low Rank Solution Factor Z: %d x %d \n" % (z.shape)) print("it = %d \t rel_res2 = %e\t res2 = %e \n" % (it, res2 / res2_0, res2)) print(status.lrnm_stat())
def main(): """Solve Riccati Equation SO1 System equation.""" # read data m = mmread("@CMAKE_SOURCE_DIR@/tests/data/TripleChain/M_301.mtx").tocsr() d = mmread("@CMAKE_SOURCE_DIR@/tests/data/TripleChain/D_301.mtx").tocsr() k = mmread("@CMAKE_SOURCE_DIR@/tests/data/TripleChain/K_301.mtx").tocsr() # generate rhs b = np.ones((2 * m.shape[0], 1), dtype=np.double) c = np.ones((1, m.shape[0]), dtype=np.double) # bounds for shift parameters lowerbound = 1e-8 upperbound = 1e+8 # create options instance opt = Options() opt.adi.output = 0 opt.nm.output = 0 opt.nm.res2_tol = 1e-6 opt.adi.res2_tol = 1e-10 opt.adi.maxit = 1000 opt.type = MESS_OP_NONE opt.adi.shifts.paratype = MESS_LRCFADI_PARA_MINMAX # create equation eqn = EquationGRiccatiSO1(opt, m, d, k, b, c, lowerbound, upperbound) # solve equation z, status = lrnm(eqn, opt) # get residual res2 = status.res2_norm res2_0 = status.res2_0 it = status.it print("it = %d \t rel_res2 = %e\t res2 = %e \n" % (it, res2 / res2_0, res2)) print("Size of Low Rank Solution Factor Z: %d x %d \n" % (z.shape)) print(status.lrnm_stat())
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)
def solve_ricc(A, E=None, B=None, Q=None, C=None, R=None, G=None, trans=False, options=None, default_solver='pymess'): """Find a factor of the solution of a Riccati equation Returns factor :math:`Z` such that :math:`Z Z^T` is approximately the solution :math:`X` of a Riccati equation .. math:: A^T X E + E^T X A - E^T X B R^{-1} B^T X E + Q = 0. If E in `None`, it is taken to be the identity matrix. Q can instead be given as C^T * C. In this case, Q needs to be `None`, and C not `None`. B * R^{-1} B^T can instead be given by G. In this case, B and R need to be `None`, and G not `None`. If R and G are `None`, then R is taken to be the identity matrix. If trans is `True`, then the dual Riccati equation is solved .. math:: A X E^T + E X A^T - E X C^T R^{-1} C X E^T + Q = 0, where Q can be replaced by B * B^T and C^T * R^{-1} * C by G. This uses the `pymess` package, in particular its `care` and `lrnm` methods. Operators Q, R, and G are not supported, Both methods can be used for large-scale problems. The restrictions are: - `care` needs access to all matrix data, i.e., it expects :func:`~pymor.algorithms.to_matrix.to_matrix` to work for A, E, B, and C, - `lrnm` needs access to the data of the operators B and C, i.e., it expects :func:`~pymor.algorithms.to_matrix.to_matrix` to work for B and C. Parameters ---------- A The |Operator| A. B The |Operator| B or `None`. E The |Operator| E or `None`. Q The |Operator| Q or `None`. C The |Operator| C or `None`. R The |Operator| R or `None`. G The |Operator| G or `None`. trans If the dual equation needs to be solved. options The |solver_options| to use (see :func:`ricc_solver_options`). default_solver The solver to use when no `options` are specified (pymess, pymess_care, pymess_lrnm). Returns ------- Z Low-rank factor of the Riccati equation solution, |VectorArray| from `A.source`. """ _solve_ricc_check_args(A, E, B, Q, C, R, G, trans) options = _parse_options(options, ricc_solver_options(), default_solver, None, False) if options['type'] == 'pymess': if A.source.dim >= PYMESS_MIN_SPARSE_SIZE: options = dict(options, type='pymess_lrnm') # do not modify original dict! else: options = dict(options, type='pymess_care') # do not modify original dict! if options['type'] == 'pymess_care': if Q is not None or R is not None or G is not None: raise NotImplementedError A_mat = to_matrix(A, format='dense') if A.source.dim < PYMESS_MIN_SPARSE_SIZE else to_matrix(A) if E is not None: E_mat = to_matrix(E, format='dense') if A.source.dim < PYMESS_MIN_SPARSE_SIZE else to_matrix(E) else: E_mat = None B_mat = to_matrix(B, format='dense') if B else None C_mat = to_matrix(C, format='dense') if C else None if not trans: Z = pymess.care(A_mat, E_mat, B_mat, C_mat) else: if E is None: Z = pymess.care(A_mat.T, None, C_mat.T, B_mat.T) else: Z = pymess.care(A_mat.T, E_mat.T, C_mat.T, B_mat.T) elif options['type'] == 'pymess_lrnm': if Q is not None or R is not None or G is not None: raise NotImplementedError opts = options['opts'] if not trans: opts.type = pymess.MESS_OP_TRANSPOSE else: opts.type = pymess.MESS_OP_NONE eqn = RiccatiEquation(opts, A, E, B, C) Z, status = pymess.lrnm(eqn, opts) Z = A.source.from_numpy(np.array(Z).T) return Z
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)