def __init__(self, blocks): blocks = np.array(blocks) assert isinstance(blocks, np.ndarray) and blocks.ndim == 2 self._blocks = blocks assert all(isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all(any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all(any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range types for every column/row source_types = [None for j in range(blocks.shape[1])] range_types = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_types[j] is None or op.source == source_types[j] source_types[j] = op.source assert range_types[i] is None or op.range == range_types[i] range_types[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self._blocks[i, j] = ZeroOperator(range_types[i], source_types[j]) self.source = BlockVectorSpace(source_types) self.range = BlockVectorSpace(range_types) self.num_source_blocks = len(source_types) self.num_range_blocks = len(range_types) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators())
def __init__(self, subdomain, solution_space, grid, block_space): self.subdomain, self.grid, self.block_space = subdomain, grid, block_space self.neighborhood = grid.neighborhood_of(subdomain) self.source = solution_space.subspaces[subdomain] self.range = BlockVectorSpace( [solution_space.subspaces[ii] for ii in self.neighborhood], 'OI_{}'.format(subdomain))
class OswaldInterpolationErrorOperator(OperatorBase): linear = True def __init__(self, subdomain, solution_space, grid, block_space): self.subdomain, self.grid, self.block_space = subdomain, grid, block_space self.neighborhood = grid.neighborhood_of(subdomain) self.source = solution_space.subspaces[subdomain] self.range = BlockVectorSpace( [solution_space.subspaces[ii] for ii in self.neighborhood], 'OI_{}'.format(subdomain)) def apply(self, U, mu=None): assert U in self.source results = self.range.empty(reserve=len(U)) for u_i in range(len(U)): result = self.range.zeros() result._blocks[self.neighborhood.index(self.subdomain)].axpy( 1, U[u_i]) for i_ii, ii in enumerate(self.neighborhood): ii_neighborhood = self.grid.neighborhood_of(ii) ii_neighborhood_space = self.block_space.restricted_to_neighborhood( ii_neighborhood) subdomain_uh_with_neighborhood_support = make_discrete_function( ii_neighborhood_space, ii_neighborhood_space.project_onto_neighborhood([ U._list[u_i].impl if nn == self.subdomain else Vector( self.block_space.local_space(nn).size(), 0.) for nn in ii_neighborhood ], ii_neighborhood)) interpolated_u_vector = ii_neighborhood_space.project_onto_neighborhood( [ Vector(self.block_space.local_space(nn).size(), 0.) for nn in ii_neighborhood ], ii_neighborhood) interpolated_u = make_discrete_function( ii_neighborhood_space, interpolated_u_vector) apply_oswald_interpolation_operator( self.grid, ii, make_subdomain_boundary_info( self.grid, {'type': 'xt.grid.boundaryinfo.alldirichlet'}), subdomain_uh_with_neighborhood_support, interpolated_u) local_sizes = np.array([ ii_neighborhood_space.local_space(nn).size() for nn in ii_neighborhood ]) offsets = np.hstack(([0], np.cumsum(local_sizes))) ind = ii_neighborhood.index(ii) result._blocks[i_ii]._list[0].data[:] -= \ np.frombuffer(interpolated_u_vector)[offsets[ind]:offsets[ind+1]] results.append(result) return results
class FluxReconstructionOperator(OperatorBase): linear = True def __init__(self, subdomain, solution_space, grid, block_space, global_rt_space, subdomain_rt_spaces, lambda_xi, kappa): self.grid = grid self.block_space = block_space self.global_rt_space = global_rt_space self.subdomain_rt_spaces = subdomain_rt_spaces self.subdomain = subdomain self.neighborhood = grid.neighborhood_of(subdomain) self.lambda_xi = lambda_xi self.kappa = kappa self.source = solution_space.subspaces[subdomain] vector_type = solution_space.subspaces[0].vector_type self.range = BlockVectorSpace([ DuneXTVectorSpace(vector_type, subdomain_rt_spaces[ii].size(), 'LOCALRT_' + str(ii)) for ii in self.grid.neighborhood_of(subdomain) ], 'RT_{}'.format(subdomain)) def apply(self, U, mu=None): assert U in self.source result = self.range.empty(reserve=len(U)) local_subdomains, num_local_subdomains, num_global_subdomains = _get_subdomains( self.grid) for u_i in range(len(U)): subdomain_uhs_with_global_support = \ make_discrete_function( self.block_space, self.block_space.project_onto_neighborhood( [U._list[u_i].impl if nn == self.subdomain else Vector(self.block_space.local_space(nn).size(), 0.) for nn in range(num_global_subdomains)], [nn for nn in range(num_global_subdomains)] ) ) reconstructed_uh_kk_with_global_support = make_discrete_function( self.global_rt_space) apply_diffusive_flux_reconstruction_in_neighborhood( self.grid, self.subdomain, self.lambda_xi, self.kappa, subdomain_uhs_with_global_support, reconstructed_uh_kk_with_global_support) blocks = [ s.make_array([ self.subdomain_rt_spaces[ii].restrict( reconstructed_uh_kk_with_global_support.vector_copy()) ]) # NOQA for s, ii in zip(self.range.subspaces, self.grid.neighborhood_of(self.subdomain)) ] result.append(self.range.make_array(blocks)) return result
def test_block_identity_lincomb(): space = NumpyVectorSpace(10) space2 = BlockVectorSpace([space, space]) identity = BlockDiagonalOperator([IdentityOperator(space), IdentityOperator(space)]) identity2 = IdentityOperator(space2) ones = space.ones() ones2 = space2.make_array([ones, ones]) idid = identity + identity2 assert almost_equal(ones2 * 2, idid.apply(ones2)) assert almost_equal(ones2 * 2, idid.apply_adjoint(ones2)) assert almost_equal(ones2 * 0.5, idid.apply_inverse(ones2)) assert almost_equal(ones2 * 0.5, idid.apply_inverse_adjoint(ones2))
def __init__(self, blocks): blocks = np.array(blocks) assert 1 <= blocks.ndim <= 2 if self.blocked_source and self.blocked_range: assert blocks.ndim == 2 elif self.blocked_source: if blocks.ndim == 1: blocks.shape = (1, len(blocks)) else: if blocks.ndim == 1: blocks.shape = (len(blocks), 1) self.blocks = blocks assert all( isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all( any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all( any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range spaces for every column/row source_spaces = [None for j in range(blocks.shape[1])] range_spaces = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_spaces[j] is None or op.source == source_spaces[j] source_spaces[j] = op.source assert range_spaces[i] is None or op.range == range_spaces[i] range_spaces[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self.blocks[i, j] = ZeroOperator(range_spaces[i], source_spaces[j]) self.source = BlockVectorSpace( source_spaces) if self.blocked_source else source_spaces[0] self.range = BlockVectorSpace( range_spaces) if self.blocked_range else range_spaces[0] self.num_source_blocks = len(source_spaces) self.num_range_blocks = len(range_spaces) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators())
def test_blockspace(): from pymor.vectorarrays.block import BlockVectorSpace, BlockVectorArray from pymor.core.pickle import dump, load b = BlockVectorSpace([]) with tempfile.TemporaryFile('wb') as dp_file: dump(b, file=dp_file)
def __init__(self, subdomain, solution_space, grid, block_space, global_rt_space, subdomain_rt_spaces, lambda_xi, kappa): self.grid = grid self.block_space = block_space self.global_rt_space = global_rt_space self.subdomain_rt_spaces = subdomain_rt_spaces self.subdomain = subdomain self.neighborhood = grid.neighborhood_of(subdomain) self.lambda_xi = lambda_xi self.kappa = kappa self.source = solution_space.subspaces[subdomain] vector_type = solution_space.subspaces[0].vector_type self.range = BlockVectorSpace([ DuneXTVectorSpace(vector_type, subdomain_rt_spaces[ii].size(), 'LOCALRT_' + str(ii)) for ii in self.grid.neighborhood_of(subdomain) ], 'RT_{}'.format(subdomain))
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.range = BlockVectorSpace([self.global_space.subspaces[ii] for ii in self.grid.neighborhood_of(self.jj)], 'OI_{}'.format(self.jj)) self.source = BlockVectorSpace([self.global_space.subspaces[ii] for ii in self.grid.neighborhood_of(self.kk)], 'OI_{}'.format(self.kk)) if self.subdomain not in self._matrices: matrix = make_local_elliptic_matrix_operator(self.grid, self.subdomain, self.block_space.local_space(self.subdomain), self.lambda_bar, self.kappa) matrix.assemble() matrix = matrix.matrix() self._matrices[self.subdomain] = DuneXTMatrixOperator(matrix, range_id='domain_{}'.format(self.subdomain), source_id='domain_{}'.format(self.subdomain)) self.matrix = self._matrices[self.subdomain] self.range_index = self.grid.neighborhood_of(self.jj).index(self.subdomain) self.source_index = self.grid.neighborhood_of(self.kk).index(self.subdomain)
def __init__(self, blocks, source_id='STATE', range_id='STATE'): blocks = np.array(blocks) assert 1 <= blocks.ndim <= 2 if self.blocked_source and self.blocked_range: assert blocks.ndim == 2 elif self.blocked_source: if blocks.ndim == 1: blocks.shape = (1, len(blocks)) else: if blocks.ndim == 1: blocks.shape = (len(blocks), 1) self._blocks = blocks assert all(isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all(any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all(any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range spaces for every column/row source_spaces = [None for j in range(blocks.shape[1])] range_spaces = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_spaces[j] is None or op.source == source_spaces[j] source_spaces[j] = op.source assert range_spaces[i] is None or op.range == range_spaces[i] range_spaces[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self._blocks[i, j] = ZeroOperator(range_spaces[i], source_spaces[j]) self.source = BlockVectorSpace(source_spaces, id_=source_id) if self.blocked_source else source_spaces[0] self.range = BlockVectorSpace(range_spaces, id_=range_id) if self.blocked_range else range_spaces[0] self.num_source_blocks = len(source_spaces) self.num_range_blocks = len(range_spaces) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators())
class OswaldInterpolationErrorOperator(EstimatorOperatorBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) assert self.subdomain == self.kk == self.jj self.range = BlockVectorSpace([self.global_space.subspaces[ii] for ii in self.neighborhood], 'OI_{}'.format(self.subdomain)) def _apply(self, U, mu=None): from dune.gdt import apply_oswald_interpolation_operator assert len(U) == 1 assert U in self.source result = self.range.zeros() result._blocks[self.neighborhood.index(self.subdomain)].axpy(1, U) for i_ii, ii in enumerate(self.neighborhood): ii_neighborhood = self.grid.neighborhood_of(ii) ii_neighborhood_space = self.block_space.restricted_to_neighborhood(ii_neighborhood) subdomain_uh_with_neighborhood_support = make_discrete_function( ii_neighborhood_space, ii_neighborhood_space.project_onto_neighborhood( [U._list[0].impl if nn == self.subdomain else Vector(self.block_space.local_space(nn).size(), 0.) for nn in ii_neighborhood], ii_neighborhood ) ) interpolated_u_vector = ii_neighborhood_space.project_onto_neighborhood( [Vector(self.block_space.local_space(nn).size(), 0.) for nn in ii_neighborhood], ii_neighborhood) interpolated_u = make_discrete_function(ii_neighborhood_space, interpolated_u_vector) apply_oswald_interpolation_operator( self.grid, ii, make_subdomain_boundary_info(self.grid, {'type': 'xt.grid.boundaryinfo.alldirichlet'}), subdomain_uh_with_neighborhood_support, interpolated_u ) local_sizes = np.array([ii_neighborhood_space.local_space(nn).size() for nn in ii_neighborhood]) offsets = np.hstack(([0], np.cumsum(local_sizes))) ind = ii_neighborhood.index(ii) result._blocks[i_ii]._list[0].data[:] -= np.frombuffer(interpolated_u_vector)[offsets[ind]:offsets[ind+1]] return result
def test_blk_diag_apply_inverse_adjoint(): np.random.seed(0) A = np.random.randn(2, 2) B = np.random.randn(3, 3) C = spla.block_diag(A, B) Aop = NumpyMatrixOperator(A) Bop = NumpyMatrixOperator(B) Cop = BlockDiagonalOperator((Aop, Bop)) v1 = np.random.randn(2) v2 = np.random.randn(3) v = np.hstack((v1, v2)) v1va = NumpyVectorSpace.from_numpy(v1) v2va = NumpyVectorSpace.from_numpy(v2) vva = BlockVectorSpace.make_array((v1va, v2va)) wva = Cop.apply_inverse_adjoint(vva) w = np.hstack((wva.block(0).to_numpy(), wva.block(1).to_numpy())) assert np.allclose(spla.solve(C.T, v), w)
def test_blk_diag_apply_inverse(): np.random.seed(0) A = np.random.randn(2, 2) B = np.random.randn(3, 3) C = spla.block_diag(A, B) Aop = NumpyMatrixOperator(A) Bop = NumpyMatrixOperator(B) Cop = BlockDiagonalOperator((Aop, Bop)) v1 = np.random.randn(2) v2 = np.random.randn(3) v = np.hstack((v1, v2)) v1va = NumpyVectorSpace.from_data(v1) v2va = NumpyVectorSpace.from_data(v2) vva = BlockVectorSpace.make_array((v1va, v2va)) wva = Cop.apply_inverse(vva) w = np.hstack((wva.block(0).data, wva.block(1).data)) assert np.allclose(spla.solve(C, v), w)
def test_apply_adjoint(): np.random.seed(0) A11 = np.random.randn(2, 3) A12 = np.random.randn(2, 4) A21 = np.zeros((5, 3)) A22 = np.random.randn(5, 4) A = np.vstack((np.hstack((A11, A12)), np.hstack((A21, A22)))) A11op = NumpyMatrixOperator(A11) A12op = NumpyMatrixOperator(A12) A22op = NumpyMatrixOperator(A22) Aop = BlockOperator(np.array([[A11op, A12op], [None, A22op]])) v1 = np.random.randn(2) v2 = np.random.randn(5) v = np.hstack((v1, v2)) v1va = NumpyVectorSpace.from_numpy(v1) v2va = NumpyVectorSpace.from_numpy(v2) vva = BlockVectorSpace.make_array((v1va, v2va)) wva = Aop.apply_adjoint(vva) w = np.hstack((wva.block(0).to_numpy(), wva.block(1).to_numpy())) assert np.allclose(A.T.dot(v), w)
def _block_vector_spaces(draw, np_data_list, compatible, count, dims): ret = [] rr = draw(hyst.randoms()) def _block_dims(d): bd = [] while d > 1: block_size = rr.randint(1, d) bd.append(block_size) d -= block_size if d > 0: bd.append(d) return bd for c, (d, ar) in enumerate(zip(dims, np_data_list)): # only redraw after initial for (potentially) incompatible arrays if c == 0 or (not compatible and c > 0): block_dims = _block_dims(d) constituent_spaces = [NumpyVectorSpace(dim) for dim in block_dims] # TODO this needs to be relaxed again assume(len(constituent_spaces)) ret.append((BlockVectorSpace(constituent_spaces), ar)) return ret
def test_apply_transpose(): np.random.seed(0) A11 = np.random.randn(2, 3) A12 = np.random.randn(2, 4) A21 = np.zeros((5, 3)) A22 = np.random.randn(5, 4) A = np.vstack((np.hstack((A11, A12)), np.hstack((A21, A22)))) A11op = NumpyMatrixOperator(A11) A12op = NumpyMatrixOperator(A12) A22op = NumpyMatrixOperator(A22) Aop = BlockOperator(np.array([[A11op, A12op], [None, A22op]])) v1 = np.random.randn(2) v2 = np.random.randn(5) v = np.hstack((v1, v2)) v1va = NumpyVectorSpace.from_data(v1) v2va = NumpyVectorSpace.from_data(v2) vva = BlockVectorSpace.make_array((v1va, v2va)) wva = Aop.apply_transpose(vva) w = np.hstack((wva.block(0).data, wva.block(1).data)) assert np.allclose(A.T.dot(v), w)
class BlockOperatorBase(OperatorBase): def _operators(self): """Iterator over operators.""" for (i, j) in np.ndindex(self._blocks.shape): yield self._blocks[i, j] def __init__(self, blocks): blocks = np.array(blocks) assert 1 <= blocks.ndim <= 2 if self.blocked_source and self.blocked_range: assert blocks.ndim == 2 elif self.blocked_source: if blocks.ndim == 1: blocks.shape = (1, len(blocks)) else: if blocks.ndim == 1: blocks.shape = (len(blocks), 1) self._blocks = blocks assert all(isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all(any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all(any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range spaces for every column/row source_spaces = [None for j in range(blocks.shape[1])] range_spaces = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_spaces[j] is None or op.source == source_spaces[j] source_spaces[j] = op.source assert range_spaces[i] is None or op.range == range_spaces[i] range_spaces[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self._blocks[i, j] = ZeroOperator(range_spaces[i], source_spaces[j]) self.source = BlockVectorSpace(source_spaces) if self.blocked_source else source_spaces[0] self.range = BlockVectorSpace(range_spaces) if self.blocked_range else range_spaces[0] self.num_source_blocks = len(source_spaces) self.num_range_blocks = len(range_spaces) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators()) @property def H(self): return self.adjoint_type(np.vectorize(lambda op: op.H if op else None)(self._blocks.T)) def apply(self, U, mu=None): assert U in self.source V_blocks = [None for i in range(self.num_range_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Vi = op.apply(U.block(j) if self.blocked_source else U, mu=mu) if V_blocks[i] is None: V_blocks[i] = Vi else: V_blocks[i] += Vi return self.range.make_array(V_blocks) if self.blocked_range else V_blocks[0] def apply_adjoint(self, V, mu=None): assert V in self.range U_blocks = [None for j in range(self.num_source_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Uj = op.apply_adjoint(V.block(i) if self.blocked_range else V, mu=mu) if U_blocks[j] is None: U_blocks[j] = Uj else: U_blocks[j] += Uj return self.source.make_array(U_blocks) if self.blocked_source else U_blocks[0] def assemble(self, mu=None): blocks = np.empty(self._blocks.shape, dtype=object) for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j].assemble(mu) if np.all(blocks == self._blocks): return self else: return self.__class__(blocks) def as_range_array(self, mu=None): def process_row(row, space): R = space.empty() for op in row: if op is not None: R.append(op.as_range_array(mu)) return R subspaces = self.range.subspaces if self.blocked_range else [self.range] blocks = [process_row(row, space) for row, space in zip(self._blocks, subspaces)] return self.range.make_array(blocks) if self.blocked_range else blocks[0] def as_source_array(self, mu=None): def process_col(col, space): R = space.empty() for op in col: if op is not None: R.append(op.as_source_array(mu)) return R subspaces = self.source.subspaces if self.blocked_source else [self.source] blocks = [process_col(col, space) for col, space in zip(self._blocks.T, subspaces)] return self.source.make_array(blocks) if self.blocked_source else blocks[0]
def block_vector_array_factory(length, dims, seed): return BlockVectorSpace( [NumpyVectorSpace(dim) for dim in dims]).from_numpy( numpy_vector_array_factory(length, sum(dims), seed).to_numpy())
class BlockOperator(OperatorBase): """A matrix of arbitrary |Operators|. This operator can be :meth:`applied <pymor.operators.interfaces.OperatorInterface.apply>` to a compatible :class:`BlockVectorArrays <pymor.vectorarrays.block.BlockVectorArray>`. Parameters ---------- blocks Two-dimensional array-like where each entry is an |Operator| or `None`. """ def _operators(self): """Iterator over operators.""" for (i, j) in np.ndindex(self._blocks.shape): yield self._blocks[i, j] def __init__(self, blocks): blocks = np.array(blocks) assert isinstance(blocks, np.ndarray) and blocks.ndim == 2 self._blocks = blocks assert all( isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all( any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all( any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range types for every column/row source_types = [None for j in range(blocks.shape[1])] range_types = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_types[j] is None or op.source == source_types[j] source_types[j] = op.source assert range_types[i] is None or op.range == range_types[i] range_types[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self._blocks[i, j] = ZeroOperator(source_types[j], range_types[i]) self.source = BlockVectorSpace(source_types) self.range = BlockVectorSpace(range_types) self.num_source_blocks = len(source_types) self.num_range_blocks = len(range_types) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators()) @property def T(self): return type(self)(np.vectorize(lambda op: op.T if op else None)( self._blocks.T)) @classmethod def hstack(cls, operators): """Horizontal stacking of |Operators|. Parameters ---------- operators An iterable where each item is an |Operator| or `None`. """ blocks = np.array([[op for op in operators]]) return cls(blocks) @classmethod def vstack(cls, operators): """Vertical stacking of |Operators|. Parameters ---------- operators An iterable where each item is an |Operator| or `None`. """ blocks = np.array([[op] for op in operators]) return cls(blocks) def apply(self, U, mu=None): assert U in self.source V_blocks = [None for i in range(self.num_range_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Vi = op.apply(U.block(j), mu=mu) if V_blocks[i] is None: V_blocks[i] = Vi else: V_blocks[i] += Vi return self.range.make_array(V_blocks) def apply_transpose(self, V, mu=None): assert V in self.range U_blocks = [None for j in range(self.num_source_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Uj = op.apply_transpose(V.block(i), mu=mu) if U_blocks[j] is None: U_blocks[j] = Uj else: U_blocks[j] += Uj U = self.source.make_array(U_blocks) return U def assemble(self, mu=None): blocks = np.empty(self._blocks.shape, dtype=object) for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j].assemble(mu) if np.all(blocks == self._blocks): return self else: return self.__class__(blocks) def assemble_lincomb(self, operators, coefficients, solver_options=None, name=None): assert operators[0] is self blocks = np.empty(self._blocks.shape, dtype=object) if len(operators) > 1: for (i, j) in np.ndindex(self._blocks.shape): operators_ij = [op._blocks[i, j] for op in operators] blocks[i, j] = operators_ij[0].assemble_lincomb( operators_ij, coefficients, solver_options=solver_options, name=name) if blocks[i, j] is None: return None return self.__class__(blocks) else: c = coefficients[0] if c == 1: return self for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j] * c return self.__class__(blocks)
class BlockOperatorBase(OperatorBase): def _operators(self): """Iterator over operators.""" for (i, j) in np.ndindex(self._blocks.shape): yield self._blocks[i, j] def __init__(self, blocks, source_id='STATE', range_id='STATE'): blocks = np.array(blocks) assert 1 <= blocks.ndim <= 2 if self.blocked_source and self.blocked_range: assert blocks.ndim == 2 elif self.blocked_source: if blocks.ndim == 1: blocks.shape = (1, len(blocks)) else: if blocks.ndim == 1: blocks.shape = (len(blocks), 1) self._blocks = blocks assert all(isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all(any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all(any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range spaces for every column/row source_spaces = [None for j in range(blocks.shape[1])] range_spaces = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_spaces[j] is None or op.source == source_spaces[j] source_spaces[j] = op.source assert range_spaces[i] is None or op.range == range_spaces[i] range_spaces[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self._blocks[i, j] = ZeroOperator(range_spaces[i], source_spaces[j]) self.source = BlockVectorSpace(source_spaces, id_=source_id) if self.blocked_source else source_spaces[0] self.range = BlockVectorSpace(range_spaces, id_=range_id) if self.blocked_range else range_spaces[0] self.num_source_blocks = len(source_spaces) self.num_range_blocks = len(range_spaces) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators()) @property def H(self): return self.adjoint_type(np.vectorize(lambda op: op.H if op else None)(self._blocks.T)) def apply(self, U, mu=None): assert U in self.source V_blocks = [None for i in range(self.num_range_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Vi = op.apply(U.block(j) if self.blocked_source else U, mu=mu) if V_blocks[i] is None: V_blocks[i] = Vi else: V_blocks[i] += Vi return self.range.make_array(V_blocks) if self.blocked_range else V_blocks[0] def apply_adjoint(self, V, mu=None): assert V in self.range U_blocks = [None for j in range(self.num_source_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Uj = op.apply_adjoint(V.block(i) if self.blocked_range else V, mu=mu) if U_blocks[j] is None: U_blocks[j] = Uj else: U_blocks[j] += Uj return self.source.make_array(U_blocks) if self.blocked_source else U_blocks[0] def assemble(self, mu=None): blocks = np.empty(self._blocks.shape, dtype=object) for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j].assemble(mu) if np.all(blocks == self._blocks): return self else: return self.__class__(blocks) def _assemble_lincomb_preprocess_operators(self, operators): return [ BlockDiagonalOperator([IdentityOperator(s) for s in op.source.subspaces], source_id=op.source.id, range_id=op.range.id) if isinstance(op, IdentityOperator) else op for op in operators if not isinstance(op, ZeroOperator) ] def assemble_lincomb(self, operators, coefficients, solver_options=None, name=None): operators = self._assemble_lincomb_preprocess_operators(operators) if not all(isinstance(op, BlockOperatorBase) for op in operators): return None assert operators[0] is self blocks = np.empty(self._blocks.shape, dtype=object) if len(operators) > 1: for (i, j) in np.ndindex(self._blocks.shape): operators_ij = [op._blocks[i, j] for op in operators] blocks[i, j] = operators_ij[0].assemble_lincomb(operators_ij, coefficients, solver_options=solver_options, name=name) if blocks[i, j] is None: return None return self.__class__(blocks) else: c = coefficients[0] if c == 1: return self for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j] * c return self.__class__(blocks) def as_range_array(self, mu=None): def process_row(row, space): R = space.empty() for op in row: if op is not None: R.append(op.as_range_array(mu)) return R blocks = [process_row(row, space) for row, space in zip(self._blocks, self.range.subspaces)] return self.range.make_array(blocks) if self.blocked_range else blocks[0] def as_source_array(self, mu=None): def process_col(col, space): R = space.empty() for op in col: if op is not None: R.append(op.as_source_array(mu)) return R blocks = [process_col(col, space) for col, space in zip(self._blocks.T, self.source.subspaces)] return self.source.make_array(blocks) if self.blocked_source else blocks[0]
class BlockOperator(OperatorBase): """A matrix of arbitrary |Operators|. This operator can be :meth:`applied <pymor.operators.interfaces.OperatorInterface.apply>` to a compatible :class:`BlockVectorArrays <pymor.vectorarrays.block.BlockVectorArray>`. Parameters ---------- blocks Two-dimensional array-like where each entry is an |Operator| or `None`. """ def _operators(self): """Iterator over operators.""" for (i, j) in np.ndindex(self._blocks.shape): yield self._blocks[i, j] def __init__(self, blocks): blocks = np.array(blocks) assert isinstance(blocks, np.ndarray) and blocks.ndim == 2 self._blocks = blocks assert all(isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all(any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all(any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range types for every column/row source_types = [None for j in range(blocks.shape[1])] range_types = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_types[j] is None or op.source == source_types[j] source_types[j] = op.source assert range_types[i] is None or op.range == range_types[i] range_types[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self._blocks[i, j] = ZeroOperator(range_types[i], source_types[j]) self.source = BlockVectorSpace(source_types) self.range = BlockVectorSpace(range_types) self.num_source_blocks = len(source_types) self.num_range_blocks = len(range_types) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators()) @property def T(self): return type(self)(np.vectorize(lambda op: op.T if op else None)(self._blocks.T)) @classmethod def hstack(cls, operators): """Horizontal stacking of |Operators|. Parameters ---------- operators An iterable where each item is an |Operator| or `None`. """ blocks = np.array([[op for op in operators]]) return cls(blocks) @classmethod def vstack(cls, operators): """Vertical stacking of |Operators|. Parameters ---------- operators An iterable where each item is an |Operator| or `None`. """ blocks = np.array([[op] for op in operators]) return cls(blocks) def apply(self, U, mu=None): assert U in self.source V_blocks = [None for i in range(self.num_range_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Vi = op.apply(U.block(j), mu=mu) if V_blocks[i] is None: V_blocks[i] = Vi else: V_blocks[i] += Vi return self.range.make_array(V_blocks) def apply_transpose(self, V, mu=None): assert V in self.range U_blocks = [None for j in range(self.num_source_blocks)] for (i, j), op in np.ndenumerate(self._blocks): Uj = op.apply_transpose(V.block(i), mu=mu) if U_blocks[j] is None: U_blocks[j] = Uj else: U_blocks[j] += Uj U = self.source.make_array(U_blocks) return U def assemble(self, mu=None): blocks = np.empty(self._blocks.shape, dtype=object) for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j].assemble(mu) if np.all(blocks == self._blocks): return self else: return self.__class__(blocks) def assemble_lincomb(self, operators, coefficients, solver_options=None, name=None): assert operators[0] is self blocks = np.empty(self._blocks.shape, dtype=object) if len(operators) > 1: for (i, j) in np.ndindex(self._blocks.shape): operators_ij = [op._blocks[i, j] for op in operators] blocks[i, j] = operators_ij[0].assemble_lincomb(operators_ij, coefficients, solver_options=solver_options, name=name) if blocks[i, j] is None: return None return self.__class__(blocks) else: c = coefficients[0] if c == 1: return self for (i, j) in np.ndindex(self._blocks.shape): blocks[i, j] = self._blocks[i, j] * c return self.__class__(blocks)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) assert self.subdomain == self.kk == self.jj self.range = BlockVectorSpace([self.global_space.subspaces[ii] for ii in self.neighborhood], 'OI_{}'.format(self.subdomain))
class BlockOperatorBase(OperatorBase): def _operators(self): """Iterator over operators.""" for (i, j) in np.ndindex(self.blocks.shape): yield self.blocks[i, j] def __init__(self, blocks): blocks = np.array(blocks) assert 1 <= blocks.ndim <= 2 if self.blocked_source and self.blocked_range: assert blocks.ndim == 2 elif self.blocked_source: if blocks.ndim == 1: blocks.shape = (1, len(blocks)) else: if blocks.ndim == 1: blocks.shape = (len(blocks), 1) self.blocks = blocks assert all( isinstance(op, OperatorInterface) or op is None for op in self._operators()) # check if every row/column contains at least one operator assert all( any(blocks[i, j] is not None for j in range(blocks.shape[1])) for i in range(blocks.shape[0])) assert all( any(blocks[i, j] is not None for i in range(blocks.shape[0])) for j in range(blocks.shape[1])) # find source/range spaces for every column/row source_spaces = [None for j in range(blocks.shape[1])] range_spaces = [None for i in range(blocks.shape[0])] for (i, j), op in np.ndenumerate(blocks): if op is not None: assert source_spaces[j] is None or op.source == source_spaces[j] source_spaces[j] = op.source assert range_spaces[i] is None or op.range == range_spaces[i] range_spaces[i] = op.range # turn Nones to ZeroOperators for (i, j) in np.ndindex(blocks.shape): if blocks[i, j] is None: self.blocks[i, j] = ZeroOperator(range_spaces[i], source_spaces[j]) self.source = BlockVectorSpace( source_spaces) if self.blocked_source else source_spaces[0] self.range = BlockVectorSpace( range_spaces) if self.blocked_range else range_spaces[0] self.num_source_blocks = len(source_spaces) self.num_range_blocks = len(range_spaces) self.linear = all(op.linear for op in self._operators()) self.build_parameter_type(*self._operators()) @property def H(self): return self.adjoint_type( np.vectorize(lambda op: op.H if op else None)(self.blocks.T)) def apply(self, U, mu=None): assert U in self.source V_blocks = [None for i in range(self.num_range_blocks)] for (i, j), op in np.ndenumerate(self.blocks): Vi = op.apply(U.block(j) if self.blocked_source else U, mu=mu) if V_blocks[i] is None: V_blocks[i] = Vi else: V_blocks[i] += Vi return self.range.make_array( V_blocks) if self.blocked_range else V_blocks[0] def apply_adjoint(self, V, mu=None): assert V in self.range U_blocks = [None for j in range(self.num_source_blocks)] for (i, j), op in np.ndenumerate(self.blocks): Uj = op.apply_adjoint(V.block(i) if self.blocked_range else V, mu=mu) if U_blocks[j] is None: U_blocks[j] = Uj else: U_blocks[j] += Uj return self.source.make_array( U_blocks) if self.blocked_source else U_blocks[0] def assemble(self, mu=None): blocks = np.empty(self.blocks.shape, dtype=object) for (i, j) in np.ndindex(self.blocks.shape): blocks[i, j] = self.blocks[i, j].assemble(mu) if np.all(blocks == self.blocks): return self else: return self.__class__(blocks) def as_range_array(self, mu=None): def process_row(row, space): R = space.empty() for op in row: if op is not None: R.append(op.as_range_array(mu)) return R subspaces = self.range.subspaces if self.blocked_range else [ self.range ] blocks = [ process_row(row, space) for row, space in zip(self.blocks, subspaces) ] return self.range.make_array( blocks) if self.blocked_range else blocks[0] def as_source_array(self, mu=None): def process_col(col, space): R = space.empty() for op in col: if op is not None: R.append(op.as_source_array(mu)) return R subspaces = self.source.subspaces if self.blocked_source else [ self.source ] blocks = [ process_col(col, space) for col, space in zip(self.blocks.T, subspaces) ] return self.source.make_array( blocks) if self.blocked_source else blocks[0]