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, U, ind=None, mu=None, options=None): 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(U, ind=ind, options=options) else: return genericsolvers.apply_inverse(assembled_op, U.copy(ind), options=options)
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(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(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 = 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) 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': 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, 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 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(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(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 = 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(V), matrix.shape[1]), dtype=np.promote_types(matrix.dtype, V.dtype)) 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": 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, 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: if scipy.version.version >= "0.14": if hasattr(matrix, "factorization"): R = matrix.factorization.solve(V.T).T elif options["keep_factorization"]: matrix.factorization = splu(matrix, permc_spec=options["permc_spec"]) R = matrix.factorization.solve(V.T).T else: R = spsolve(matrix, V.T, permc_spec=options["permc_spec"]).T else: if hasattr(matrix, "factorization"): for i, VV in enumerate(V): R[i] = matrix.factorization.solve(VV) elif options["keep_factorization"]: matrix.factorization = splu(matrix, permc_spec=options["permc_spec"]) for i, VV in enumerate(V): R[i] = matrix.factorization.solve(VV) elif len(V) > 1: factorization = splu(matrix, permc_spec=options["permc_spec"]) for i, VV in enumerate(V): R[i] = factorization.solve(VV) else: R = spsolve(matrix, 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(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