def apply_inverse(self, V, mu=None, least_squares=False): from pymor.operators.constructions import FixedParameterOperator assembled_op = self.assemble(mu) if assembled_op != self and not isinstance(assembled_op, FixedParameterOperator): return assembled_op.apply_inverse(V, least_squares=least_squares) elif self.linear: options = self.solver_options.get('inverse') if self.solver_options else None return genericsolvers.apply_inverse(assembled_op, V, options=options, least_squares=least_squares) else: from pymor.algorithms.newton import newton from pymor.core.exceptions import NewtonError options = self.solver_options.get('inverse') if self.solver_options else None if options: if isinstance(options, str): assert options == 'newton' options = {} else: assert options['type'] == 'newton' options = options.copy() options.pop('type') else: options = {} options['least_squares'] = least_squares R = V.empty(reserve=len(V)) for i in range(len(V)): try: R.append(newton(self, V[i], mu=mu, **options)[0]) except NewtonError as e: raise InversionError(e) return R
def apply_inverse(self, V, ind=None, mu=None, least_squares=False): assert V in self.range assert V.check_ind(ind) assert not self.functional and not self.vector if V.dim == 0: if self.source.dim == 0 and least_squares: return ListVectorArray([NumpyVector(np.zeros(0), copy=False) for _ in range(V.len_ind(ind))], subtype=self.source.subtype) else: raise InversionError options = (self.solver_options.get('inverse') if self.solver_options else 'least_squares' if least_squares else None) if options and not least_squares: solver_type = options if isinstance(options, str) else options['type'] if solver_type.startswith('least_squares'): self.logger.warn('Least squares solver selected but "least_squares == False"') if ind is None: vectors = V._list elif isinstance(ind, Number): vectors = [V._list[ind]] else: vectors = (V._list[i] for i in ind) try: return ListVectorArray([NumpyVector(_apply_inverse(self._matrix, v._array.reshape((1, -1)), options=options).ravel(), copy=False) for v in vectors], subtype=self.source.subtype) except InversionError as e: if least_squares and options: solver_type = options if isinstance(options, str) else options['type'] if not solver_type.startswith('least_squares'): msg = str(e) \ + '\nNote: linear solver was selected for solving least squares problem (maybe not invertible?)' raise InversionError(msg) raise e
def apply_inverse(self, V, ind=None, mu=None, least_squares=False): assert V in self.range assert V.check_ind(ind) if V.dim == 0: if self.source.dim == 0 or least_squares: return NumpyVectorArray( np.zeros((V.len_ind(ind), self.source.dim))) else: raise InversionError options = (self.solver_options.get('inverse') if self.solver_options else 'least_squares' if least_squares else None) if options and not least_squares: solver_type = options if isinstance(options, str) else options['type'] if solver_type.startswith('least_squares'): self.logger.warn( 'Least squares solver selected but "least_squares == False"' ) V = V.data if ind is None else \ V.data[ind] if hasattr(ind, '__len__') else V.data[ind:ind + 1] try: return NumpyVectorArray(_apply_inverse(self._matrix, V, options=options), copy=False) except InversionError as e: if least_squares and options: solver_type = options if isinstance(options, str) else options['type'] if not solver_type.startswith('least_squares'): msg = str(e) \ + '\nNote: linear solver was selected for solving least squares problem (maybe not invertible?)' raise InversionError(msg) raise e
def apply_inverse(self, V, mu=None, initial_guess=None, least_squares=False): """Apply the inverse operator. Parameters ---------- V |VectorArray| of vectors to which the inverse operator is applied. mu The |parameter values| for which to evaluate the inverse operator. initial_guess |VectorArray| with the same length as `V` containing initial guesses for the solution. Some implementations of `apply_inverse` may ignore this parameter. If `None` a solver-dependent default is used. least_squares If `True`, solve the least squares problem:: u = argmin ||op(u) - v||_2. Since for an invertible operator the least squares solution agrees with the result of the application of the inverse operator, setting this option should, in general, have no effect on the result for those operators. However, note that when no appropriate |solver_options| are set for the operator, most implementations will choose a least squares solver by default which may be undesirable. Returns ------- |VectorArray| of the inverse operator evaluations. Raises ------ InversionError The operator could not be inverted. """ assert V in self.range assert initial_guess is None or initial_guess in self.source and len( initial_guess) == len(V) from pymor.operators.constructions import FixedParameterOperator assembled_op = self.assemble(mu) if assembled_op != self and not isinstance(assembled_op, FixedParameterOperator): return assembled_op.apply_inverse(V, initial_guess=initial_guess, least_squares=least_squares) elif self.linear: options = self.solver_options.get( 'inverse') if self.solver_options else None return genericsolvers.apply_inverse(assembled_op, V, initial_guess=initial_guess, options=options, least_squares=least_squares) else: from pymor.algorithms.newton import newton from pymor.core.exceptions import NewtonError options = self.solver_options.get( 'inverse') if self.solver_options else None if options: if isinstance(options, str): assert options == 'newton' options = {} else: assert options['type'] == 'newton' options = options.copy() options.pop('type') else: options = {} options['least_squares'] = least_squares with self.logger.block( 'Solving nonlinear problem using newton algorithm ...'): R = V.empty(reserve=len(V)) for i in range(len(V)): try: R.append( newton(self, V[i], initial_guess=initial_guess[i] if initial_guess is not None else None, mu=mu, **options)[0]) except NewtonError as e: raise InversionError(e) return R
def apply_inverse(self, V, mu=None, least_squares=False, check_finite=True, default_sparse_solver_backend='scipy'): """Apply the inverse operator. Parameters ---------- V |VectorArray| of vectors to which the inverse operator is applied. mu The |Parameter| for which to evaluate the inverse operator. least_squares If `True`, solve the least squares problem:: u = argmin ||op(u) - v||_2. Since for an invertible operator the least squares solution agrees with the result of the application of the inverse operator, setting this option should, in general, have no effect on the result for those operators. However, note that when no appropriate |solver_options| are set for the operator, most implementations will choose a least squares solver by default which may be undesirable. check_finite Test if solution only contains finite values. default_sparse_solver_backend Default sparse solver backend to use (scipy, pyamg, generic). Returns ------- |VectorArray| of the inverse operator evaluations. Raises ------ InversionError The operator could not be inverted. """ assert V in self.range if V.dim == 0: if self.source.dim == 0 or least_squares: return self.source.make_array( np.zeros((len(V), self.source.dim))) else: raise InversionError options = self.solver_options.get( 'inverse') if self.solver_options else None assert self.sparse or not options if self.sparse: if options: solver = options if isinstance(options, str) else options['type'] backend = solver.split('_')[0] else: backend = default_sparse_solver_backend if backend == 'scipy': from pymor.bindings.scipy import apply_inverse as apply_inverse_impl elif backend == 'pyamg': if not config.HAVE_PYAMG: raise RuntimeError('PyAMG support not enabled.') from pymor.bindings.pyamg import apply_inverse as apply_inverse_impl elif backend == 'generic': logger = getLogger('pymor.bindings.scipy.scipy_apply_inverse') logger.warning( 'You have selected a (potentially slow) generic solver for a NumPy matrix operator!' ) from pymor.algorithms.genericsolvers import apply_inverse as apply_inverse_impl else: raise NotImplementedError return apply_inverse_impl(self, V, options=options, least_squares=least_squares, check_finite=check_finite) else: if least_squares: try: R, _, _, _ = np.linalg.lstsq(self.matrix, V.to_numpy().T) except np.linalg.LinAlgError as e: raise InversionError(f'{str(type(e))}: {str(e)}') R = R.T else: try: R = np.linalg.solve(self.matrix, V.to_numpy().T).T except np.linalg.LinAlgError as e: raise InversionError(f'{str(type(e))}: {str(e)}') if check_finite: if not np.isfinite(np.sum(R)): raise InversionError('Result contains non-finite values') return self.source.make_array(R)
def apply_inverse(self, V, mu=None, least_squares=False): if not least_squares: raise InversionError('ConstantOperator is not invertible.') return self.source.zeros(len(V))
def apply_inverse(op, V, initial_guess=None, options=None, least_squares=False, check_finite=True, default_solver='generic_lgmres', default_least_squares_solver='generic_least_squares_lsmr'): """Solve linear equation system. Applies the inverse of `op` to the vectors in `V` using a generic iterative solver. Parameters ---------- op The linear, non-parametric |Operator| to invert. V |VectorArray| of right-hand sides for the equation system. initial_guess |VectorArray| with the same length as `V` containing initial guesses for the solution. Some implementations of `apply_inverse` may ignore this parameter. If `None` a solver-dependent default is used. options The |solver_options| to use (see :func:`solver_options`). least_squares If `True`, return least squares solution. check_finite Test if solution only contains finite values. default_solver Default solver to use (generic_lgmres, generic_least_squares_lsmr, generic_least_squares_lsqr). default_least_squares_solver Default solver to use for least squares problems (generic_least_squares_lsmr, generic_least_squares_lsqr). Returns ------- |VectorArray| of the solution vectors. """ assert V in op.range assert initial_guess is None or initial_guess in op.source and len( initial_guess) == len(V) options = _parse_options(options, solver_options(), default_solver, default_least_squares_solver, least_squares) R = op.source.empty(reserve=len(V)) if options['type'] == 'generic_lgmres': for i in range(len(V)): r, info = lgmres( op, V[i], initial_guess[i] if initial_guess is not None else None, tol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( f'lgmres failed to converge after {info} iterations') assert info == 0 R.append(r) elif options['type'] == 'generic_least_squares_lsmr': for i in range(len(V)): r, info, itn, _, _, _, _, _ = lsmr(op, V[i], damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( f'lsmr failed to converge after {itn} iterations') getLogger('pymor.algorithms.genericsolvers.lsmr').info( f'Converged after {itn} iterations') R.append(r) elif options['type'] == 'generic_least_squares_lsqr': for i in range(len(V)): r, info, itn, _, _, _, _, _, _ = lsqr(op, V[i], damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( f'lsmr failed to converge after {itn} iterations') getLogger('pymor.algorithms.genericsolvers.lsqr').info( f'Converged after {itn} iterations') R.append(r) else: raise ValueError('Unknown solver type') if check_finite: if not np.isfinite(np.all(R.norm())): raise InversionError('Result contains non-finite values') return R
def apply_inverse(op, V, initial_guess=None, options=None, least_squares=False, check_finite=True, default_solver='scipy_spsolve', default_least_squares_solver='scipy_least_squares_lsmr'): """Solve linear equation system. Applies the inverse of `op` to the vectors in `V` using SciPy. Parameters ---------- op The linear, non-parametric |Operator| to invert. V |VectorArray| of right-hand sides for the equation system. initial_guess |VectorArray| with the same length as `V` containing initial guesses for the solution. Some implementations of `apply_inverse` may ignore this parameter. If `None` a solver-dependent default is used. options The |solver_options| to use (see :func:`solver_options`). least_squares If `True`, return least squares solution. check_finite Test if solution only contains finite values. default_solver Default solver to use (scipy_spsolve, scipy_bicgstab, scipy_bicgstab_spilu, scipy_lgmres, scipy_least_squares_lsmr, scipy_least_squares_lsqr). default_least_squares_solver Default solver to use for least squares problems (scipy_least_squares_lsmr, scipy_least_squares_lsqr). Returns ------- |VectorArray| of the solution vectors. """ assert V in op.range assert initial_guess is None or initial_guess in op.source and len( initial_guess) == len(V) if isinstance(op, NumpyMatrixOperator): matrix = op.matrix else: from pymor.algorithms.to_matrix import to_matrix matrix = to_matrix(op) options = _parse_options(options, solver_options(), default_solver, default_least_squares_solver, least_squares) V = V.to_numpy() initial_guess = initial_guess.to_numpy( ) if initial_guess is not None else None promoted_type = np.promote_types(matrix.dtype, V.dtype) R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type) if options['type'] == 'scipy_bicgstab': for i, VV in enumerate(V): R[i], info = bicgstab( matrix, VV, initial_guess[i] if initial_guess is not None else None, tol=options['tol'], maxiter=options['maxiter']) if info != 0: if info > 0: raise InversionError( f'bicgstab failed to converge after {info} iterations') else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'scipy_bicgstab_spilu': ilu = spilu(matrix, drop_tol=options['spilu_drop_tol'], fill_factor=options['spilu_fill_factor'], drop_rule=options['spilu_drop_rule'], permc_spec=options['spilu_permc_spec']) precond = LinearOperator(matrix.shape, ilu.solve) for i, VV in enumerate(V): R[i], info = bicgstab( matrix, VV, initial_guess[i] if initial_guess is not None else None, tol=options['tol'], maxiter=options['maxiter'], M=precond) if info != 0: if info > 0: raise InversionError( f'bicgstab failed to converge after {info} iterations') else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'scipy_spsolve': try: # maybe remove unusable factorization: if hasattr(matrix, 'factorization'): fdtype = matrix.factorizationdtype if not np.can_cast(V.dtype, fdtype, casting='safe'): del matrix.factorization if hasattr(matrix, 'factorization'): # we may use a complex factorization of a real matrix to # apply it to a real vector. In that case, we downcast # the result here, removing the imaginary part, # which should be zero. R = matrix.factorization.solve(V.T).T.astype(promoted_type, copy=False) elif options['keep_factorization']: # the matrix is always converted to the promoted type. # if matrix.dtype == promoted_type, this is a no_op matrix.factorization = splu(matrix_astype_nocopy( matrix.tocsc(), promoted_type), permc_spec=options['permc_spec']) matrix.factorizationdtype = promoted_type R = matrix.factorization.solve(V.T).T else: # the matrix is always converted to the promoted type. # if matrix.dtype == promoted_type, this is a no_op R = spsolve(matrix_astype_nocopy(matrix, promoted_type), V.T, permc_spec=options['permc_spec']).T except RuntimeError as e: raise InversionError(e) elif options['type'] == 'scipy_lgmres': for i, VV in enumerate(V): R[i], info = lgmres( matrix, VV, initial_guess[i] if initial_guess is not None else None, tol=options['tol'], atol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( f'lgmres failed to converge after {info} iterations') assert info == 0 elif options['type'] == 'scipy_least_squares_lsmr': from scipy.sparse.linalg import lsmr for i, VV in enumerate(V): R[i], info, itn, _, _, _, _, _ = lsmr( matrix, VV, damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show'], x0=initial_guess[i] if initial_guess is not None else None) assert 0 <= info <= 7 if info == 7: raise InversionError( f'lsmr failed to converge after {itn} iterations') elif options['type'] == 'scipy_least_squares_lsqr': for i, VV in enumerate(V): R[i], info, itn, _, _, _, _, _, _, _ = lsqr( matrix, VV, damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show'], x0=initial_guess[i] if initial_guess is not None else None) assert 0 <= info <= 7 if info == 7: raise InversionError( f'lsmr failed to converge after {itn} iterations') else: raise ValueError('Unknown solver type') if check_finite: if not np.isfinite(np.sum(R)): raise InversionError('Result contains non-finite values') return op.source.from_numpy(R)
def apply_inverse(matrix, U, options=None): """Solve linear equation system. Applies the inverse of `matrix` to the row vectors in `U`. See :func:`sparse_options` for documentation of all possible options for sparse matrices. This method is called by :meth:`pymor.core.NumpyMatrixOperator.apply_inverse` and usually should not be used directly. Parameters ---------- matrix The |NumPy| matrix to invert. U 2-dimensional |NumPy array| containing as row vectors the right-hand sides of the linear equation systems to solve. options |invert_options| to use. (See :func:`invert_options`.) Returns ------- |NumPy array| of the solution vectors. """ default_options = invert_options(matrix) if options is None: options = default_options.values()[0] elif isinstance(options, str): if options == 'least_squares': for k, v in default_options.iteritems(): if k.startswith('least_squares'): options = v break assert not isinstance(options, str) else: options = default_options[options] else: assert 'type' in options and options['type'] in default_options \ and options.viewkeys() <= default_options[options['type']].viewkeys() user_options = options options = default_options[user_options['type']] options.update(user_options) R = np.empty((len(U), matrix.shape[1])) if options['type'] == 'solve': for i, UU in enumerate(U): try: R[i] = np.linalg.solve(matrix, UU) except np.linalg.LinAlgError as e: raise InversionError('{}: {}'.format(str(type(e)), str(e))) elif options['type'] == 'least_squares_lstsq': for i, UU in enumerate(U): try: R[i], _, _, _ = np.linalg.lstsq(matrix, UU, rcond=options['rcond']) except np.linalg.LinAlgError as e: raise InversionError('{}: {}'.format(str(type(e)), str(e))) elif options['type'] == 'bicgstab': for i, UU in enumerate(U): R[i], info = bicgstab(matrix, UU, tol=options['tol'], maxiter=options['maxiter']) if info != 0: if info > 0: raise InversionError( 'bicgstab failed to converge after {} iterations'. format(info)) else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'bicgstab_spilu': ilu = spilu(matrix, drop_tol=options['spilu_drop_tol'], fill_factor=options['spilu_fill_factor'], drop_rule=options['spilu_drop_rule'], permc_spec=options['spilu_permc_spec']) precond = LinearOperator(matrix.shape, ilu.solve) for i, UU in enumerate(U): R[i], info = bicgstab(matrix, UU, tol=options['tol'], maxiter=options['maxiter'], M=precond) if info != 0: if info > 0: raise InversionError( 'bicgstab failed to converge after {} iterations'. format(info)) else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'spsolve': if scipy.version.version >= '0.14': if hasattr(matrix, 'factorization'): R = matrix.factorization.solve(U.T).T elif options['keep_factorization']: matrix.factorization = splu(matrix, permc_spec=options['permc_spec']) R = matrix.factorization.solve(U.T).T else: R = spsolve(matrix, U.T, permc_spec=options['permc_spec']).T else: if hasattr(matrix, 'factorization'): for i, UU in enumerate(U): R[i] = matrix.factorization.solve(UU) elif options['keep_factorization']: matrix.factorization = splu(matrix, permc_spec=options['permc_spec']) for i, UU in enumerate(U): R[i] = matrix.factorization.solve(UU) elif len(U) > 1: factorization = splu(matrix, permc_spec=options['permc_spec']) for i, UU in enumerate(U): R[i] = factorization.solve(UU) else: R = spsolve(matrix, U.T, permc_spec=options['permc_spec']).reshape((1, -1)) elif options['type'] == 'lgmres': for i, UU in enumerate(U): R[i], info = lgmres(matrix, UU.copy(i), tol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( 'lgmres failed to converge after {} iterations'.format( info)) assert info == 0 elif options['type'] == 'least_squares_lsmr': for i, UU in enumerate(U): R[i], info, itn, _, _, _, _, _ = lsmr(matrix, UU.copy(i), damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) elif options['type'] == 'least_squares_lsqr': for i, UU in enumerate(U): R[i], info, itn, _, _, _, _, _, _, _ = lsqr( matrix, UU.copy(i), damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) elif options['type'] == 'pyamg': if len(U) > 0: U_iter = iter(enumerate(U)) R[0], ml = pyamg.solve(matrix, next(U_iter)[1], tol=options['tol'], maxiter=options['maxiter'], return_solver=True) for i, UU in U_iter: R[i] = pyamg.solve(matrix, UU, tol=options['tol'], maxiter=options['maxiter'], existing_solver=ml) elif options['type'] == 'pyamg-rs': ml = pyamg.ruge_stuben_solver(matrix, strength=options['strength'], CF=options['CF'], presmoother=options['presmoother'], postsmoother=options['postsmoother'], max_levels=options['max_levels'], max_coarse=options['max_coarse'], coarse_solver=options['coarse_solver']) for i, UU in enumerate(U): R[i] = ml.solve(UU, tol=options['tol'], maxiter=options['maxiter'], cycle=options['cycle'], accel=options['accel']) elif options['type'] == 'pyamg-sa': ml = pyamg.smoothed_aggregation_solver( matrix, symmetry=options['symmetry'], strength=options['strength'], aggregate=options['aggregate'], smooth=options['smooth'], presmoother=options['presmoother'], postsmoother=options['postsmoother'], improve_candidates=options['improve_candidates'], max_levels=options['max_levels'], max_coarse=options['max_coarse'], diagonal_dominance=options['diagonal_dominance']) for i, UU in enumerate(U): R[i] = ml.solve(UU, tol=options['tol'], maxiter=options['maxiter'], cycle=options['cycle'], accel=options['accel']) elif options['type'].startswith('generic') or options['type'].startswith( 'least_squares_generic'): logger = getLogger('pymor.la.numpysolvers.apply_inverse') logger.warn( 'You have selected a (potentially slow) generic solver for a NumPy matrix operator!' ) from pymor.operators.numpy import NumpyMatrixOperator from pymor.la.numpyvectorarray import NumpyVectorArray return genericsolvers.apply_inverse(NumpyMatrixOperator(matrix), NumpyVectorArray(U, copy=False), options=options).data else: raise ValueError('Unknown solver type') return R
def apply_inverse(self, V, mu=None, least_squares=False): """Apply the inverse operator. Parameters ---------- V |VectorArray| of vectors to which the inverse operator is applied. mu The |Parameter| for which to evaluate the inverse operator. least_squares If `True`, solve the least squares problem:: u = argmin ||op(u) - v||_2. Since for an invertible operator the least squares solution agrees with the result of the application of the inverse operator, setting this option should, in general, have no effect on the result for those operators. However, note that when no appropriate |solver_options| are set for the operator, most implementations will choose a least squares solver by default which may be undesirable. Returns ------- |VectorArray| of the inverse operator evaluations. Raises ------ InversionError The operator could not be inverted. """ from pymor.operators.constructions import FixedParameterOperator assembled_op = self.assemble(mu) if assembled_op != self and not isinstance(assembled_op, FixedParameterOperator): return assembled_op.apply_inverse(V, least_squares=least_squares) elif self.linear: options = self.solver_options.get( 'inverse') if self.solver_options else None return genericsolvers.apply_inverse(assembled_op, V, options=options, least_squares=least_squares) else: from pymor.algorithms.newton import newton from pymor.core.exceptions import NewtonError options = self.solver_options.get( 'inverse') if self.solver_options else None if options: if isinstance(options, str): assert options == 'newton' options = {} else: assert options['type'] == 'newton' options = options.copy() options.pop('type') else: options = {} options['least_squares'] = least_squares R = V.empty(reserve=len(V)) for i in range(len(V)): try: R.append(newton(self, V[i], mu=mu, **options)[0]) except NewtonError as e: raise InversionError(e) return R
def apply_inverse(self, V, ind=None, mu=None, least_squares=False): from pymor.operators.constructions import FixedParameterOperator assembled_op = self.assemble(mu) if assembled_op != self and not isinstance(assembled_op, FixedParameterOperator): return assembled_op.apply_inverse(V, ind=ind, least_squares=least_squares) elif self.linear: options = (self.solver_options.get('inverse') if self.solver_options else 'least_squares' if least_squares else None) if options and not least_squares: solver_type = options if isinstance(options, str) else options['type'] if solver_type.startswith('least_squares'): self.logger.warn( 'Least squares solver selected but "least_squares == False"' ) try: return genericsolvers.apply_inverse(assembled_op, V.copy(ind), options=options) except InversionError as e: if least_squares and options: solver_type = options if isinstance( options, str) else options['type'] if not solver_type.startswith('least_squares'): msg = str(e) \ + '\nNote: linear solver was selected for solving least squares problem ' \ + '(maybe not invertible?)' raise InversionError(msg) raise e else: from pymor.algorithms.newton import newton from pymor.core.exceptions import NewtonError assert V.check_ind(ind) options = self.solver_options if options: if isinstance(options, str): assert options == 'newton' options = {} else: assert options['type'] == 'newton' options = options.copy() options.pop('type') else: options = {} options['least_squares'] = least_squares ind = (list(range(len(V))) if ind is None else [ind] if isinstance(ind, Number) else ind) R = V.empty(reserve=len(ind)) for i in ind: try: R.append(newton(self, V.copy(i), **options)[0]) except NewtonError as e: raise InversionError(e) return R
def apply_inverse(op, V, options=None, least_squares=False, check_finite=True, default_solver='scipy_spsolve', default_least_squares_solver='scipy_least_squares_lsmr'): """Solve linear equation system. Applies the inverse of `op` to the vectors in `rhs` using PyAMG. Parameters ---------- op The linear, non-parametric |Operator| to invert. rhs |VectorArray| of right-hand sides for the equation system. options The |solver_options| to use (see :func:`solver_options`). check_finite Test if solution only containes finite values. default_solver Default solver to use (scipy_spsolve, scipy_bicgstab, scipy_bicgstab_spilu, scipy_lgmres, scipy_least_squares_lsmr, scipy_least_squares_lsqr). default_least_squares_solver Default solver to use for least squares problems (scipy_least_squares_lsmr, scipy_least_squares_lsqr). Returns ------- |VectorArray| of the solution vectors. """ assert V in op.range if isinstance(op, NumpyMatrixOperator): matrix = op._matrix else: from pymor.algorithms.to_matrix import to_matrix matrix = to_matrix(op) options = _parse_options(options, solver_options(), default_solver, default_least_squares_solver, least_squares) V = V.data promoted_type = np.promote_types(matrix.dtype, V.dtype) R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type) if options['type'] == 'scipy_bicgstab': for i, VV in enumerate(V): R[i], info = bicgstab(matrix, VV, tol=options['tol'], maxiter=options['maxiter']) if info != 0: if info > 0: raise InversionError( 'bicgstab failed to converge after {} iterations'. format(info)) else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'scipy_bicgstab_spilu': if Version(scipy.version.version) >= Version('0.19'): ilu = spilu(matrix, drop_tol=options['spilu_drop_tol'], fill_factor=options['spilu_fill_factor'], drop_rule=options['spilu_drop_rule'], permc_spec=options['spilu_permc_spec']) else: if options['spilu_drop_rule']: logger = getLogger('pymor.operators.numpy._apply_inverse') logger.error( "ignoring drop_rule in ilu factorization due to old SciPy") ilu = spilu(matrix, drop_tol=options['spilu_drop_tol'], fill_factor=options['spilu_fill_factor'], permc_spec=options['spilu_permc_spec']) precond = LinearOperator(matrix.shape, ilu.solve) for i, VV in enumerate(V): R[i], info = bicgstab(matrix, VV, tol=options['tol'], maxiter=options['maxiter'], M=precond) if info != 0: if info > 0: raise InversionError( 'bicgstab failed to converge after {} iterations'. format(info)) else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'scipy_spsolve': try: # maybe remove unusable factorization: if hasattr(matrix, 'factorization'): fdtype = matrix.factorizationdtype if not np.can_cast(V.dtype, fdtype, casting='safe'): del matrix.factorization if Version(scipy.version.version) >= Version('0.14'): if hasattr(matrix, 'factorization'): # we may use a complex factorization of a real matrix to # apply it to a real vector. In that case, we downcast # the result here, removing the imaginary part, # which should be zero. R = matrix.factorization.solve(V.T).T.astype(promoted_type, copy=False) elif options['keep_factorization']: # the matrix is always converted to the promoted type. # if matrix.dtype == promoted_type, this is a no_op matrix.factorization = splu( matrix_astype_nocopy(matrix.tocsc(), promoted_type), permc_spec=options['permc_spec']) matrix.factorizationdtype = promoted_type R = matrix.factorization.solve(V.T).T else: # the matrix is always converted to the promoted type. # if matrix.dtype == promoted_type, this is a no_op R = spsolve(matrix_astype_nocopy(matrix, promoted_type), V.T, permc_spec=options['permc_spec']).T else: # see if-part for documentation if hasattr(matrix, 'factorization'): for i, VV in enumerate(V): R[i] = matrix.factorization.solve(VV).astype( promoted_type, copy=False) elif options['keep_factorization']: matrix.factorization = splu( matrix_astype_nocopy(matrix.tocsc(), promoted_type), permc_spec=options['permc_spec']) matrix.factorizationdtype = promoted_type for i, VV in enumerate(V): R[i] = matrix.factorization.solve(VV) elif len(V) > 1: factorization = splu(matrix_astype_nocopy( matrix.tocsc(), promoted_type), permc_spec=options['permc_spec']) for i, VV in enumerate(V): R[i] = factorization.solve(VV) else: R = spsolve(matrix_astype_nocopy(matrix, promoted_type), V.T, permc_spec=options['permc_spec']).reshape( (1, -1)) except RuntimeError as e: raise InversionError(e) elif options['type'] == 'scipy_lgmres': for i, VV in enumerate(V): R[i], info = lgmres(matrix, VV, tol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( 'lgmres failed to converge after {} iterations'.format( info)) assert info == 0 elif options['type'] == 'scipy_least_squares_lsmr': from scipy.sparse.linalg import lsmr for i, VV in enumerate(V): R[i], info, itn, _, _, _, _, _ = lsmr(matrix, VV, damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) elif options['type'] == 'scipy_least_squares_lsqr': for i, VV in enumerate(V): R[i], info, itn, _, _, _, _, _, _, _ = lsqr( matrix, VV, damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) else: raise ValueError('Unknown solver type') if check_finite: if not np.isfinite(np.sum(R)): raise InversionError('Result contains non-finite values') return op.source.from_data(R)
def apply_inverse(op, V, options=None, least_squares=False, check_finite=True, default_solver='pyamg_solve'): """Solve linear equation system. Applies the inverse of `op` to the vectors in `rhs` using PyAMG. Parameters ---------- op The linear, non-parametric |Operator| to invert. rhs |VectorArray| of right-hand sides for the equation system. options The |solver_options| to use (see :func:`solver_options`). least_squares Must be `False`. check_finite Test if solution only contains finite values. default_solver Default solver to use (pyamg_solve, pyamg_rs, pyamg_sa). Returns ------- |VectorArray| of the solution vectors. """ assert V in op.range if least_squares: raise NotImplementedError if isinstance(op, NumpyMatrixOperator): matrix = op.matrix else: from pymor.algorithms.to_matrix import to_matrix matrix = to_matrix(op) options = _parse_options(options, solver_options(), default_solver, None, least_squares) V = V.to_numpy() promoted_type = np.promote_types(matrix.dtype, V.dtype) R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type) if options['type'] == 'pyamg_solve': if len(V) > 0: V_iter = iter(enumerate(V)) R[0], ml = pyamg.solve(matrix, next(V_iter)[1], tol=options['tol'], maxiter=options['maxiter'], return_solver=True) for i, VV in V_iter: R[i] = pyamg.solve(matrix, VV, tol=options['tol'], maxiter=options['maxiter'], existing_solver=ml) elif options['type'] == 'pyamg_rs': ml = pyamg.ruge_stuben_solver( matrix, strength=options['strength'], CF=options['CF'], presmoother=options['presmoother'], postsmoother=options['postsmoother'], max_levels=options['max_levels'], max_coarse=options['max_coarse'], coarse_solver=options['coarse_solver']) for i, VV in enumerate(V): R[i] = ml.solve(VV, tol=options['tol'], maxiter=options['maxiter'], cycle=options['cycle'], accel=options['accel']) elif options['type'] == 'pyamg_sa': ml = pyamg.smoothed_aggregation_solver( matrix, symmetry=options['symmetry'], strength=options['strength'], aggregate=options['aggregate'], smooth=options['smooth'], presmoother=options['presmoother'], postsmoother=options['postsmoother'], improve_candidates=options['improve_candidates'], max_levels=options['max_levels'], max_coarse=options['max_coarse'], diagonal_dominance=options['diagonal_dominance']) for i, VV in enumerate(V): R[i] = ml.solve(VV, tol=options['tol'], maxiter=options['maxiter'], cycle=options['cycle'], accel=options['accel']) else: raise ValueError('Unknown solver type') if check_finite: if not np.isfinite(np.sum(R)): raise InversionError('Result contains non-finite values') return op.source.from_numpy(R)
def apply_inverse(op, rhs, options=None): """Solve linear equation system. Applies the inverse of `op` to the vectors in `rhs`. Parameters ---------- op The linear, non-parametric |Operator| to invert. rhs |VectorArray| of right-hand sides for the equation system. options The solver options to use. (See :func:`options`.) Returns ------- |VectorArray| of the solution vectors. """ def_opts = globals()['options']() if options is None: options = next(iter(def_opts.values())) elif isinstance(options, str): if options == 'least_squares': for k, v in def_opts.items(): if k.startswith('least_squares'): options = v break assert not isinstance(options, str) else: options = def_opts[options] else: assert 'type' in options and options['type'] in def_opts \ and options.keys() <= def_opts[options['type']].keys() user_options = options options = def_opts[user_options['type']] options.update(user_options) R = op.source.empty(reserve=len(rhs)) if options['type'] == 'generic_lgmres': for i in range(len(rhs)): r, info = lgmres(op, rhs.copy(i), tol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( 'lgmres failed to converge after {} iterations'.format( info)) assert info == 0 R.append(r) elif options['type'] == 'least_squares_generic_lsmr': for i in range(len(rhs)): r, info, itn, _, _, _, _, _ = lsmr(op, rhs.copy(i), damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) getLogger('pymor.algorithms.genericsolvers.lsmr').info( 'Converged after {} iterations'.format(itn)) R.append(r) elif options['type'] == 'least_squares_generic_lsqr': for i in range(len(rhs)): r, info, itn, _, _, _, _, _, _ = lsqr(op, rhs.copy(i), damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) getLogger('pymor.algorithms.genericsolvers.lsqr').info( 'Converged after {} iterations'.format(itn)) R.append(r) else: raise ValueError('Unknown solver type') return R
def _apply_inverse(matrix, V, options=None): """Solve linear equation system. Applies the inverse of `matrix` to the row vectors in `V`. See :func:`dense_options` for documentation of all possible options for sparse matrices. See :func:`sparse_options` for documentation of all possible options for sparse matrices. This method is called by :meth:`pymor.core.NumpyMatrixOperator.apply_inverse` and usually should not be used directly. Parameters ---------- matrix The |NumPy| matrix to invert. V 2-dimensional |NumPy array| containing as row vectors the right-hand sides of the linear equation systems to solve. options The solver options to use. (See :func:`_options`.) Returns ------- |NumPy array| of the solution vectors. """ default_options = _options(matrix) if options is None: options = next(iter(default_options.values())) elif isinstance(options, str): if options == 'least_squares': for k, v in default_options.items(): if k.startswith('least_squares'): options = v break assert not isinstance(options, str) else: options = default_options[options] else: assert 'type' in options and options['type'] in default_options \ and options.keys() <= default_options[options['type']].keys() user_options = options options = default_options[user_options['type']] options.update(user_options) promoted_type = np.promote_types(matrix.dtype, V.dtype) R = np.empty((len(V), matrix.shape[1]), dtype=promoted_type) if options['type'] == 'solve': for i, VV in enumerate(V): try: R[i] = np.linalg.solve(matrix, VV) except np.linalg.LinAlgError as e: raise InversionError('{}: {}'.format(str(type(e)), str(e))) elif options['type'] == 'least_squares_lstsq': for i, VV in enumerate(V): try: R[i], _, _, _ = np.linalg.lstsq(matrix, VV, rcond=options['rcond']) except np.linalg.LinAlgError as e: raise InversionError('{}: {}'.format(str(type(e)), str(e))) elif options['type'] == 'bicgstab': for i, VV in enumerate(V): R[i], info = bicgstab(matrix, VV, tol=options['tol'], maxiter=options['maxiter']) if info != 0: if info > 0: raise InversionError( 'bicgstab failed to converge after {} iterations'. format(info)) else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'bicgstab_spilu': # workaround for https://github.com/pymor/pymor/issues/171 try: ilu = spilu(matrix, drop_tol=options['spilu_drop_tol'], fill_factor=options['spilu_fill_factor'], drop_rule=options['spilu_drop_rule'], permc_spec=options['spilu_permc_spec']) except TypeError as t: logger = getLogger('pymor.operators.numpy._apply_inverse') logger.error("ignoring drop_rule in ilu factorization") ilu = spilu(matrix, drop_tol=options['spilu_drop_tol'], fill_factor=options['spilu_fill_factor'], permc_spec=options['spilu_permc_spec']) precond = LinearOperator(matrix.shape, ilu.solve) for i, VV in enumerate(V): R[i], info = bicgstab(matrix, VV, tol=options['tol'], maxiter=options['maxiter'], M=precond) if info != 0: if info > 0: raise InversionError( 'bicgstab failed to converge after {} iterations'. format(info)) else: raise InversionError( 'bicgstab failed with error code {} (illegal input or breakdown)' .format(info)) elif options['type'] == 'spsolve': try: # maybe remove unusable factorization: if hasattr(matrix, 'factorization'): fdtype = matrix.factorizationdtype if not np.can_cast(V.dtype, fdtype, casting='safe'): del matrix.factorization if list(map(int, scipy.version.version.split('.'))) >= [0, 14, 0]: if hasattr(matrix, 'factorization'): # we may use a complex factorization of a real matrix to # apply it to a real vector. In that case, we downcast # the result here, removing the imaginary part, # which should be zero. R = matrix.factorization.solve(V.T).T.astype(promoted_type, copy=False) elif options['keep_factorization']: # the matrix is always converted to the promoted type. # if matrix.dtype == promoted_type, this is a no_op matrix.factorization = splu( matrix_astype_nocopy(matrix, promoted_type), permc_spec=options['permc_spec']) matrix.factorizationdtype = promoted_type R = matrix.factorization.solve(V.T).T else: # the matrix is always converted to the promoted type. # if matrix.dtype == promoted_type, this is a no_op R = spsolve(matrix_astype_nocopy(matrix, promoted_type), V.T, permc_spec=options['permc_spec']).T else: # see if-part for documentation if hasattr(matrix, 'factorization'): for i, VV in enumerate(V): R[i] = matrix.factorization.solve(VV).astype( promoted_type, copy=False) elif options['keep_factorization']: matrix.factorization = splu( matrix_astype_nocopy(matrix, promoted_type), permc_spec=options['permc_spec']) matrix.factorizationdtype = promoted_type for i, VV in enumerate(V): R[i] = matrix.factorization.solve(VV) elif len(V) > 1: factorization = splu(matrix_astype_nocopy( matrix, promoted_type), permc_spec=options['permc_spec']) for i, VV in enumerate(V): R[i] = factorization.solve(VV) else: R = spsolve(matrix_astype_nocopy(matrix, promoted_type), V.T, permc_spec=options['permc_spec']).reshape( (1, -1)) except RuntimeError as e: raise InversionError(e) elif options['type'] == 'lgmres': for i, VV in enumerate(V): R[i], info = lgmres(matrix, VV.copy(i), tol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( 'lgmres failed to converge after {} iterations'.format( info)) assert info == 0 elif options['type'] == 'least_squares_lsmr': for i, VV in enumerate(V): R[i], info, itn, _, _, _, _, _ = lsmr(matrix, VV.copy(i), damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) elif options['type'] == 'least_squares_lsqr': for i, VV in enumerate(V): R[i], info, itn, _, _, _, _, _, _, _ = lsqr( matrix, VV.copy(i), damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) elif options['type'] == 'pyamg': if len(V) > 0: V_iter = iter(enumerate(V)) R[0], ml = pyamg.solve(matrix, next(V_iter)[1], tol=options['tol'], maxiter=options['maxiter'], return_solver=True) for i, VV in V_iter: R[i] = pyamg.solve(matrix, VV, tol=options['tol'], maxiter=options['maxiter'], existing_solver=ml) elif options['type'] == 'pyamg-rs': ml = pyamg.ruge_stuben_solver(matrix, strength=options['strength'], CF=options['CF'], presmoother=options['presmoother'], postsmoother=options['postsmoother'], max_levels=options['max_levels'], max_coarse=options['max_coarse'], coarse_solver=options['coarse_solver']) for i, VV in enumerate(V): R[i] = ml.solve(VV, tol=options['tol'], maxiter=options['maxiter'], cycle=options['cycle'], accel=options['accel']) elif options['type'] == 'pyamg-sa': ml = pyamg.smoothed_aggregation_solver( matrix, symmetry=options['symmetry'], strength=options['strength'], aggregate=options['aggregate'], smooth=options['smooth'], presmoother=options['presmoother'], postsmoother=options['postsmoother'], improve_candidates=options['improve_candidates'], max_levels=options['max_levels'], max_coarse=options['max_coarse'], diagonal_dominance=options['diagonal_dominance']) for i, VV in enumerate(V): R[i] = ml.solve(VV, tol=options['tol'], maxiter=options['maxiter'], cycle=options['cycle'], accel=options['accel']) elif options['type'].startswith('generic') or options['type'].startswith( 'least_squares_generic'): logger = getLogger('pymor.operators.numpy._apply_inverse') logger.warn( 'You have selected a (potentially slow) generic solver for a NumPy matrix operator!' ) from pymor.operators.numpy import NumpyMatrixOperator from pymor.vectorarrays.numpy import NumpyVectorArray return genericsolvers.apply_inverse(NumpyMatrixOperator(matrix), NumpyVectorArray(V, copy=False), options=options).data else: raise ValueError('Unknown solver type') return R
def apply_inverse(op, rhs, options=None, least_squares=False, check_finite=True, default_solver='generic_lgmres', default_least_squares_solver='generic_least_squares_lsmr'): """Solve linear equation system. Applies the inverse of `op` to the vectors in `rhs` using a generic iterative solver. Parameters ---------- op The linear, non-parametric |Operator| to invert. rhs |VectorArray| of right-hand sides for the equation system. options The |solver_options| to use (see :func:`solver_options`). check_finite Test if solution only containes finite values. default_solver Default solver to use (generic_lgmres, generic_least_squares_lsmr, generic_least_squares_lsqr). default_least_squares_solver Default solver to use for least squares problems (generic_least_squares_lsmr, generic_least_squares_lsqr). Returns ------- |VectorArray| of the solution vectors. """ options = _parse_options(options, solver_options(), default_solver, default_least_squares_solver, least_squares) R = op.source.empty(reserve=len(rhs)) if options['type'] == 'generic_lgmres': for i in range(len(rhs)): r, info = lgmres(op, rhs[i], tol=options['tol'], maxiter=options['maxiter'], inner_m=options['inner_m'], outer_k=options['outer_k']) if info > 0: raise InversionError( 'lgmres failed to converge after {} iterations'.format( info)) assert info == 0 R.append(r) elif options['type'] == 'generic_least_squares_lsmr': for i in range(len(rhs)): r, info, itn, _, _, _, _, _ = lsmr(op, rhs[i], damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], maxiter=options['maxiter'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) getLogger('pymor.algorithms.genericsolvers.lsmr').info( 'Converged after {} iterations'.format(itn)) R.append(r) elif options['type'] == 'generic_least_squares_lsqr': for i in range(len(rhs)): r, info, itn, _, _, _, _, _, _ = lsqr(op, rhs[i], damp=options['damp'], atol=options['atol'], btol=options['btol'], conlim=options['conlim'], iter_lim=options['iter_lim'], show=options['show']) assert 0 <= info <= 7 if info == 7: raise InversionError( 'lsmr failed to converge after {} iterations'.format(itn)) getLogger('pymor.algorithms.genericsolvers.lsqr').info( 'Converged after {} iterations'.format(itn)) R.append(r) else: raise ValueError('Unknown solver type') if check_finite: if not np.isfinite(np.all(R.l2_norm())): raise InversionError('Result contains non-finite values') return R