Exemplo n.º 1
0
    def visualize(self, U, codim=2, **kwargs):
        """Visualize scalar data associated to the grid as a patch plot.

        Parameters
        ----------
        U
            |NumPy array| of the data to visualize. If `U.dim == 2 and len(U) > 1`, the
            data is visualized as a time series of plots. Alternatively, a tuple of
            |Numpy arrays| can be provided, in which case a subplot is created for
            each entry of the tuple. The lengths of all arrays have to agree.
        codim
            The codimension of the entities the data in `U` is attached to (either 0 or 2).
        kwargs
            See :func:`~pymor.gui.qt.visualize_patch`
        """
        from pymor.gui.qt import visualize_patch
        from pymor.vectorarrays.interfaces import VectorArrayInterface
        from pymor.vectorarrays.numpy import NumpyVectorSpace, NumpyVectorArray
        if isinstance(U, (np.ndarray, VectorArrayInterface)):
            U = (U,)
        assert all(isinstance(u, (np.ndarray, VectorArrayInterface)) for u in U)
        U = tuple(NumpyVectorSpace.make_array(u) if isinstance(u, np.ndarray) else
                  u if isinstance(u, NumpyVectorArray) else
                  NumpyVectorSpace.make_array(u.data)
                  for u in U)
        bounding_box = kwargs.pop('bounding_box', self.domain)
        visualize_patch(self, U, codim=codim, bounding_box=bounding_box, **kwargs)
Exemplo n.º 2
0
    def projected(self, range_basis, source_basis, product=None, name=None):
        assert source_basis is None or source_basis in self.source
        assert range_basis is None or range_basis in self.range
        assert product is None or product.source == product.range == self.range

        if len(self.interpolation_dofs) == 0:
            return ZeroOperator(self.source, self.range, self.name).projected(range_basis, source_basis, product, name)
        elif not hasattr(self, 'restricted_operator') or source_basis is None:
            return super().projected(range_basis, source_basis, product, name)
        else:
            name = name or self.name + '_projected'

            if range_basis is not None:
                if product is None:
                    projected_collateral_basis = NumpyVectorSpace.make_array(self.collateral_basis.dot(range_basis),
                                                                             self.range.id)
                else:
                    projected_collateral_basis = NumpyVectorSpace.make_array(product.apply2(self.collateral_basis,
                                                                                            range_basis),
                                                                             self.range.id)
            else:
                projected_collateral_basis = self.collateral_basis

            return ProjectedEmpiciralInterpolatedOperator(self.restricted_operator, self.interpolation_matrix,
                                                          NumpyVectorSpace.make_array(source_basis.components(self.source_dofs)),
                                                          projected_collateral_basis, self.triangular,
                                                          self.source.id, None, name)
Exemplo n.º 3
0
    def projected_to_subbasis(self, dim_range=None, dim_source=None, dim_collateral=None, name=None):
        assert dim_source is None or dim_source <= self.source.dim
        assert dim_range is None or dim_range <= self.range.dim
        assert dim_collateral is None or dim_collateral <= self.restricted_operator.range.dim
        if not isinstance(self.projected_collateral_basis.space, NumpyVectorSpace):
            raise NotImplementedError
        name = name or '{}_projected_to_subbasis'.format(self.name)

        interpolation_matrix = self.interpolation_matrix[:dim_collateral, :dim_collateral]

        if dim_collateral is not None:
            restricted_operator, source_dofs = self.restricted_operator.restricted(np.arange(dim_collateral))
        else:
            restricted_operator = self.restricted_operator

        old_pcb = self.projected_collateral_basis
        projected_collateral_basis = NumpyVectorSpace.make_array(old_pcb.data[:dim_collateral, :dim_range],
                                                                 old_pcb.space.id)

        old_sbd = self.source_basis_dofs
        source_basis_dofs = NumpyVectorSpace.make_array(old_sbd.data[:dim_source]) if dim_collateral is None \
            else NumpyVectorSpace.make_array(old_sbd.data[:dim_source, source_dofs])

        return ProjectedEmpiciralInterpolatedOperator(restricted_operator, interpolation_matrix,
                                                      source_basis_dofs, projected_collateral_basis, self.triangular,
                                                      self.source.id, solver_options=self.solver_options, name=name)
Exemplo n.º 4
0
Arquivo: qt.py Projeto: renemilk/pyMor
 def save(self):
     if not config.HAVE_PYVTK:
         msg = QMessageBox(QMessageBox.Critical, 'Error', 'VTK output disabled. Pleas install pyvtk.')
         msg.exec_()
         return
     filename = QFileDialog.getSaveFileName(self, 'Save as vtk file')[0]
     base_name = filename.split('.vtu')[0].split('.vtk')[0].split('.pvd')[0]
     if base_name:
         if len(self.U) == 1:
             write_vtk(self.grid, NumpyVectorSpace.make_array(self.U[0]), base_name, codim=self.codim)
         else:
             for i, u in enumerate(self.U):
                 write_vtk(self.grid, NumpyVectorSpace.make_array(u), '{}-{}'.format(base_name, i),
                           codim=self.codim)
Exemplo n.º 5
0
 def projected(self, range_basis, source_basis, product=None, name=None):
     assert source_basis is None or source_basis in self.source
     assert range_basis is None or range_basis in self.range
     assert product is None or product.source == product.range == self.range
     if range_basis is not None:
         if product:
             projected_value = NumpyVectorSpace.make_array(product.apply2(range_basis, self._value).T, self.range.id)
         else:
             projected_value = NumpyVectorSpace.make_array(range_basis.dot(self._value).T, self.range.id)
     else:
         projected_value = self._value
     if source_basis is None:
         return ConstantOperator(projected_value, self.source, name=self.name + '_projected')
     else:
         return ConstantOperator(projected_value, NumpyVectorSpace(len(source_basis), self.source.id),
                                 name=self.name + '_projected')
Exemplo n.º 6
0
class ComponentProjection(OperatorBase):
    """|Operator| representing the projection of a |VectorArray| on some of its components.

    Parameters
    ----------
    components
        List or 1D |NumPy array| of the indices of the vector
        :meth:`~pymor.vectorarrays.interfaces.VectorArrayInterface.components` that ar
        to be extracted by the operator.
    source
        Source |VectorSpace| of the operator.
    name
        Name of the operator.
    """

    linear = True

    def __init__(self, components, source, name=None):
        assert all(0 <= c < source.dim for c in components)
        self.components = np.array(components, dtype=np.int32)
        self.range = NumpyVectorSpace(len(components))
        self.source = source
        self.name = name

    def apply(self, U, mu=None):
        assert U in self.source
        return self.range.make_array(U.components(self.components))

    def restricted(self, dofs):
        assert all(0 <= c < self.range.dim for c in dofs)
        source_dofs = self.components[dofs]
        return IdentityOperator(NumpyVectorSpace(len(source_dofs))), source_dofs
Exemplo n.º 7
0
Arquivo: ei.py Projeto: pymor/pymor
    def with_cb_dim(self, dim):
        assert dim <= self.restricted_operator.range.dim

        interpolation_matrix = self.interpolation_matrix[:dim, :dim]

        restricted_operator, source_dofs = self.restricted_operator.restricted(np.arange(dim))

        old_pcb = self.projected_collateral_basis
        projected_collateral_basis = NumpyVectorSpace.make_array(old_pcb.to_numpy()[:dim, :])

        old_sbd = self.source_basis_dofs
        source_basis_dofs = NumpyVectorSpace.make_array(old_sbd.to_numpy()[:, source_dofs])

        return ProjectedEmpiciralInterpolatedOperator(restricted_operator, interpolation_matrix,
                                                      source_basis_dofs, projected_collateral_basis, self.triangular,
                                                      solver_options=self.solver_options, name=self.name)
Exemplo n.º 8
0
 def restricted(self, dofs):
     assert all(0 <= c < self.range.dim for c in dofs)
     if not self.transposed:
         restricted_value = NumpyVectorSpace.make_array(self._array.components(dofs))
         return VectorArrayOperator(restricted_value, False), np.arange(self.source.dim, dtype=np.int32)
     else:
         raise NotImplementedError
Exemplo n.º 9
0
    def action_ProjectedEmpiciralInterpolatedOperator(self, op):
        if not isinstance(op.projected_collateral_basis.space, NumpyVectorSpace):
            raise NotImplementedError

        restricted_operator = op.restricted_operator

        old_pcb = op.projected_collateral_basis
        projected_collateral_basis = NumpyVectorSpace.make_array(old_pcb.to_numpy()[:, :self.dim_range],
                                                                 old_pcb.space.id)

        old_sbd = op.source_basis_dofs
        source_basis_dofs = NumpyVectorSpace.make_array(old_sbd.to_numpy()[:self.dim_source])

        return ProjectedEmpiciralInterpolatedOperator(restricted_operator, op.interpolation_matrix,
                                                      source_basis_dofs, projected_collateral_basis, op.triangular,
                                                      op.source.id, solver_options=op.solver_options, name=op.name)
Exemplo n.º 10
0
def test_to_matrix():
    np.random.seed(0)
    A = np.random.randn(2, 2)
    B = np.random.randn(3, 3)
    C = np.random.randn(3, 3)

    X = np.bmat([[np.eye(2) + A, np.zeros((2, 3))], [np.zeros((3, 2)), B.dot(C.T)]])

    C = sps.csc_matrix(C)

    Aop = NumpyMatrixOperator(A)
    Bop = NumpyMatrixOperator(B)
    Cop = NumpyMatrixOperator(C)

    Xop = BlockDiagonalOperator([LincombOperator([IdentityOperator(NumpyVectorSpace(2)), Aop],
                                                 [1, 1]), Concatenation(Bop, AdjointOperator(Cop))])

    assert np.allclose(X, to_matrix(Xop))
    assert np.allclose(X, to_matrix(Xop, format='csr').toarray())

    np.random.seed(0)
    V = np.random.randn(10, 2)
    Vva = NumpyVectorSpace.make_array(V.T)
    Vop = VectorArrayOperator(Vva)
    assert np.allclose(V, to_matrix(Vop))
    Vop = VectorArrayOperator(Vva, transposed=True)
    assert np.allclose(V, to_matrix(Vop).T)
Exemplo n.º 11
0
    def action_EmpiricalInterpolatedOperator(self, op):
        range_basis, source_basis, product = self.range_basis, self.source_basis, self.product
        if len(op.interpolation_dofs) == 0:
            return self.apply(ZeroOperator(op.range, op.source, op.name))
        elif not hasattr(op, 'restricted_operator') or source_basis is None:
            raise RuleNotMatchingError('Has no restricted operator or source_basis is None')
        else:
            if range_basis is not None:
                projected_collateral_basis = NumpyVectorSpace.make_array(op.collateral_basis.inner(range_basis,
                                                                                                   product),
                                                                         op.range.id)
            else:
                projected_collateral_basis = op.collateral_basis

            return ProjectedEmpiciralInterpolatedOperator(op.restricted_operator, op.interpolation_matrix,
                                                          NumpyVectorSpace.make_array(source_basis.dofs(op.source_dofs)),
                                                          projected_collateral_basis, op.triangular,
                                                          op.source.id, None, op.name)
Exemplo n.º 12
0
 def reconstruct(self, U):
     """Reconstruct high-dimensional vector from reduced vector `U`."""
     assert isinstance(U.space, NumpyVectorSpace)
     UU = np.zeros((len(U), self.dim))
     UU[:, :self.dim_subbasis] = U.data
     UU = NumpyVectorSpace.make_array(UU, U.space.id)
     if self.old_recontructor:
         return self.old_recontructor.reconstruct(UU)
     else:
         return UU
Exemplo n.º 13
0
Arquivo: ei.py Projeto: pymor/pymor
    def apply(self, U, mu=None):
        mu = self.parse_parameter(mu)
        if len(self.interpolation_dofs) == 0:
            return self.range.zeros(len(U))

        if hasattr(self, 'restricted_operator'):
            U_dofs = NumpyVectorSpace.make_array(U.dofs(self.source_dofs))
            AU = self.restricted_operator.apply(U_dofs, mu=mu)
        else:
            AU = NumpyVectorSpace.make_array(self.operator.apply(U, mu=mu).dofs(self.interpolation_dofs))
        try:
            if self.triangular:
                interpolation_coefficients = solve_triangular(self.interpolation_matrix, AU.to_numpy().T,
                                                              lower=True, unit_diagonal=True).T
            else:
                interpolation_coefficients = solve(self.interpolation_matrix, AU.to_numpy().T).T
        except ValueError:  # this exception occurs when AU contains NaNs ...
            interpolation_coefficients = np.empty((len(AU), len(self.collateral_basis))) + np.nan
        return self.collateral_basis.lincomb(interpolation_coefficients)
Exemplo n.º 14
0
def test_to_matrix_VectorArrayOperator():
    np.random.seed(0)
    V = np.random.randn(10, 2)

    Vva = NumpyVectorSpace.make_array(V.T)
    Vop = VectorArrayOperator(Vva)
    assert_type_and_allclose(V, Vop, 'dense')

    Vop = VectorArrayOperator(Vva, adjoint=True)
    assert_type_and_allclose(V.T, Vop, 'dense')
Exemplo n.º 15
0
 def action_ConstantOperator(self, op):
     range_basis, source_basis, product = self.range_basis, self.source_basis, self.product
     if range_basis is not None:
         projected_value = NumpyVectorSpace.make_array(range_basis.inner(op._value, product).T, op.range.id)
     else:
         projected_value = op._value
     if source_basis is None:
         return ConstantOperator(projected_value, op.source, name=op.name)
     else:
         return ConstantOperator(projected_value, NumpyVectorSpace(len(source_basis), op.source.id),
                                 name=op.name)
Exemplo n.º 16
0
 def action_ConstantOperator(self, op):
     range_basis, source_basis, product = self.range_basis, self.source_basis, self.product
     if range_basis is not None:
         projected_value = NumpyVectorSpace.make_array(
             range_basis.inner(op._value, product).T, op.range.id)
     else:
         projected_value = op._value
     if source_basis is None:
         return ConstantOperator(projected_value, op.source, name=op.name)
     else:
         return ConstantOperator(projected_value,
                                 NumpyVectorSpace(len(source_basis),
                                                  op.source.id),
                                 name=op.name)
Exemplo n.º 17
0
    def action_ProjectedEmpiciralInterpolatedOperator(self, op):
        if not isinstance(op.projected_collateral_basis.space,
                          NumpyVectorSpace):
            raise NotImplementedError

        restricted_operator = op.restricted_operator

        old_pcb = op.projected_collateral_basis
        projected_collateral_basis = NumpyVectorSpace.make_array(
            old_pcb.to_numpy()[:, :self.dim_range])

        old_sbd = op.source_basis_dofs
        source_basis_dofs = NumpyVectorSpace.make_array(
            old_sbd.to_numpy()[:self.dim_source])

        return ProjectedEmpiciralInterpolatedOperator(
            restricted_operator,
            op.interpolation_matrix,
            source_basis_dofs,
            projected_collateral_basis,
            op.triangular,
            solver_options=op.solver_options,
            name=op.name)
Exemplo n.º 18
0
    def with_cb_dim(self, dim):
        assert dim <= self.restricted_operator.range.dim

        interpolation_matrix = self.interpolation_matrix[:dim, :dim]

        restricted_operator, source_dofs = self.restricted_operator.restricted(
            np.arange(dim))

        old_pcb = self.projected_collateral_basis
        projected_collateral_basis = NumpyVectorSpace.make_array(
            old_pcb.to_numpy()[:dim, :])

        old_sbd = self.source_basis_dofs
        source_basis_dofs = NumpyVectorSpace.make_array(
            old_sbd.to_numpy()[:, source_dofs])

        return ProjectedEmpiciralInterpolatedOperator(
            restricted_operator,
            interpolation_matrix,
            source_basis_dofs,
            projected_collateral_basis,
            self.triangular,
            solver_options=self.solver_options,
            name=self.name)
Exemplo n.º 19
0
    def apply_inverse(self, V, mu=None, least_squares=False):
        assert V in self.range
        assert not self.functional and not self.vector

        if V.dim == 0:
            if self.source.dim == 0 and least_squares:
                return self.source.make_array([np.zeros(0) for _ in range(len(V))])
            else:
                raise InversionError

        op = NumpyMatrixOperator(self.matrix, solver_options=self.solver_options)

        return self.source.make_array([op.apply_inverse(NumpyVectorSpace.make_array(v._array),
                                                        least_squares=least_squares).to_numpy().ravel()
                                       for v in V._list])
Exemplo n.º 20
0
    class PyClawModel():
        def __init__(self, claw):
            self.claw = claw
            self.num_eqn = claw.solution.state.q.shape[0]
            self.mx = claw.solution.state.q.shape[1]
            self.my = claw.solution.state.q.shape[2]
            self.mz = claw.solution.state.q.shape[3]
            self.solution_space = NumpyVectorSpace(claw.solution.state.q.size)
            self.claw.start_frame = self.claw.solution.start_frame

        def solve_manual(self, mu=None):
            assert mu is None
            use_claw_run = True
            if use_claw_run:
                status = self.claw.run()
            else:
                import copy
                from clawpack.pyclaw.util import FrameCounter
                frame = FrameCounter()
                frame.set_counter(self.claw.start_frame)
                self.claw.frames.append(copy.deepcopy(
                    self.claw.solution))  # initial solution
                self.claw.solution.write(frame, self.claw.outdir,
                                         self.claw.output_format,
                                         self.claw.output_file_prefix,
                                         self.claw.write_aux_always,
                                         self.claw.output_options)
                t = 0.0
                t_increment = 10.0
                for i in range(12):
                    self.claw.solver.evolve_to_time(self.claw.solution,
                                                    t + t_increment)
                    frame.increment()
                    self.claw.frames.append(copy.deepcopy(self.claw.solution))
                    self.claw.solution.write(frame, self.claw.outdir,
                                             self.claw.output_format,
                                             self.claw.output_file_prefix,
                                             self.claw.write_aux_always,
                                             self.claw.output_options)
                    self.claw.solution._start_frame = len(self.claw.frames)
                    t += t_increment

            dim = self.claw.frames[0].q.size
            print(dim)
            qr = []
            for f in range(len(self.claw.frames)):
                qr.append(np.reshape(self.claw.frames[f].q, dim, order='F'))
            return self.solution_space.make_array(qr)
Exemplo n.º 21
0
    def estimate(self, U, mu, d):
        if len(U) > 1:
            raise NotImplementedError
        if not d.rhs.parametric:
            CR = np.ones(1)
        else:
            CR = np.array(d.rhs.evaluate_coefficients(mu))

        if not d.operator.parametric:
            CO = np.ones(1)
        else:
            CO = np.array(d.operator.evaluate_coefficients(mu))

        C = np.hstack((CR, np.dot(CO[..., np.newaxis], U.data).ravel()))

        est = self.norm(NumpyVectorSpace.make_array(C))
        if self.coercivity_estimator:
            est /= self.coercivity_estimator(mu)

        return est
Exemplo n.º 22
0
    def estimate(self, U, mu, m):
        if len(U) > 1:
            raise NotImplementedError
        if not m.rhs.parametric:
            CR = np.ones(1)
        else:
            CR = np.array(m.rhs.evaluate_coefficients(mu))

        if not m.operator.parametric:
            CO = np.ones(1)
        else:
            CO = np.array(m.operator.evaluate_coefficients(mu))

        C = np.hstack((CR, np.dot(CO[..., np.newaxis], U.to_numpy()).ravel()))

        est = self.norm(NumpyVectorSpace.make_array(C))
        if self.coercivity_estimator:
            est /= self.coercivity_estimator(mu)

        return est
Exemplo n.º 23
0
def calculate_csis(gq, lq):
    spaces = gq["spaces"]
    for space in spaces:
        ldict = lq[space]
        T = ldict["solution_matrix_robin"]
        u_s = ldict["local_sol2"]
        product = ldict["omega_star_product"]
        norm = induced_norm(product)
        if norm(u_s).real < 1e-14:
            result = 1
        else:
            x = np.linalg.lstsq(T, u_s.data.T, rcond=None)[0]
            y = T.dot(x)
            y_p = NumpyVectorSpace.make_array(y.T)
            y_p = y_p.lincomb(1 / norm(y_p).real)
            result = np.sqrt(
                norm(u_s).real[0]**2 /
                (norm(u_s).real[0]**2 -
                 product.apply2(u_s, y_p).real[0][0]**2))
        ldict["csi"] = result
    print("calculated csis")
Exemplo n.º 24
0
    def _localizedly_project_functional(self, op):
        if isinstance(op, LincombOperator):
            ops = [
                self._localizedly_project_functional(foo)
                for foo in op.operators
            ]
            return LincombOperator(ops, coefficients=op.coefficients)

        assert op.linear and not op.parametric
        v = op.as_vector()

        def project_block(ids, basis):
            o = self.l.localize_vector_array(v, ids)
            if basis is None:
                return o.data
            else:
                return basis.dot(o).T

        mats = [
            project_block(ids, basis)
            for ids, basis in zip(self.range_spaces, self.range_bases)
        ]
        return NumpyVectorSpace.make_array(np.concatenate(mats, axis=1))
Exemplo n.º 25
0
class NumpyMatrixOperator(NumpyMatrixBasedOperator):
    """Wraps a 2D |NumPy Array| as an |Operator|.

    Parameters
    ----------
    matrix
        The |NumPy array| which is to be wrapped.
    source_id
        The id of the operator's `source` |VectorSpace|.
    range_id
        The id of the operator's `range` |VectorSpace|.
    solver_options
        The |solver_options| for the operator.
    name
        Name of the operator.
    """
    def __init__(self,
                 matrix,
                 source_id=None,
                 range_id=None,
                 solver_options=None,
                 name=None):
        assert matrix.ndim <= 2
        if matrix.ndim == 1:
            matrix = np.reshape(matrix, (1, -1))
        try:
            matrix.setflags(write=False)  # make numpy arrays read-only
        except AttributeError:
            pass
        self.source = NumpyVectorSpace(matrix.shape[1], source_id)
        self.range = NumpyVectorSpace(matrix.shape[0], range_id)
        self.solver_options = solver_options
        self.name = name
        self.matrix = matrix
        self.source_id = source_id
        self.range_id = range_id
        self.sparse = issparse(matrix)

    @classmethod
    def from_file(cls,
                  path,
                  key=None,
                  source_id=None,
                  range_id=None,
                  solver_options=None,
                  name=None):
        from pymor.tools.io import load_matrix
        matrix = load_matrix(path, key=key)
        return cls(matrix,
                   solver_options=solver_options,
                   source_id=source_id,
                   range_id=range_id,
                   name=name or key or path)

    @property
    def H(self):
        options = {
            'inverse': self.solver_options.get('inverse_adjoint'),
            'inverse_adjoint': self.solver_options.get('inverse')
        } if self.solver_options else None
        if self.sparse:
            adjoint_matrix = self.matrix.transpose(copy=False).conj(copy=False)
        elif np.isrealobj(self.matrix):
            adjoint_matrix = self.matrix.T
        else:
            adjoint_matrix = self.matrix.T.conj()
        return self.with_(matrix=adjoint_matrix,
                          source_id=self.range_id,
                          range_id=self.source_id,
                          solver_options=options,
                          name=self.name + '_adjoint')

    def _assemble(self, mu=None):
        pass

    def assemble(self, mu=None):
        return self

    def as_range_array(self, mu=None):
        return self.range.make_array(self.matrix.T.copy())

    def as_source_array(self, mu=None):
        return self.source.make_array(self.matrix.copy()).conj()

    def apply(self, U, mu=None):
        assert U in self.source
        return self.range.make_array(self.matrix.dot(U.to_numpy().T).T)

    def apply_adjoint(self, V, mu=None):
        assert V in self.range
        return self.H.apply(V, mu=mu)

    @defaults('check_finite', 'default_sparse_solver_backend')
    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_adjoint(self, U, mu=None, least_squares=False):
        return self.H.apply_inverse(U, mu=mu, least_squares=least_squares)

    def assemble_lincomb(self,
                         operators,
                         coefficients,
                         solver_options=None,
                         name=None):
        if not all(
                isinstance(op, (NumpyMatrixOperator, ZeroOperator,
                                IdentityOperator)) for op in operators):
            return None

        common_mat_dtype = reduce(
            np.promote_types,
            (op.matrix.dtype for op in operators if hasattr(op, 'matrix')))
        common_coef_dtype = reduce(np.promote_types,
                                   (type(c) for c in coefficients))
        common_dtype = np.promote_types(common_mat_dtype, common_coef_dtype)

        if coefficients[0] == 1:
            matrix = operators[0].matrix.astype(common_dtype)
        else:
            matrix = operators[0].matrix * coefficients[0]
            if matrix.dtype != common_dtype:
                matrix = matrix.astype(common_dtype)

        for op, c in zip(operators[1:], coefficients[1:]):
            if type(op) is ZeroOperator:
                continue
            elif type(op) is IdentityOperator:
                if c.imag == 0:
                    c = c.real
                if operators[0].sparse:
                    try:
                        matrix += (scipy.sparse.eye(matrix.shape[0]) * c)
                    except NotImplementedError:
                        matrix = matrix + (scipy.sparse.eye(matrix.shape[0]) *
                                           c)
                else:
                    matrix += (np.eye(matrix.shape[0]) * c)
            elif c == 1:
                try:
                    matrix += op.matrix
                except NotImplementedError:
                    matrix = matrix + op.matrix
            elif c == -1:
                try:
                    matrix -= op.matrix
                except NotImplementedError:
                    matrix = matrix - op.matrix
            else:
                try:
                    matrix += (op.matrix * c)
                except NotImplementedError:
                    matrix = matrix + (op.matrix * c)
        return NumpyMatrixOperator(matrix,
                                   source_id=self.source.id,
                                   range_id=self.range.id,
                                   solver_options=solver_options)

    def __getstate__(self):
        if hasattr(
                self.matrix,
                'factorization'):  # remove unplicklable SuperLU factorization
            del self.matrix.factorization
        return self.__dict__
Exemplo n.º 26
0
    class NeuralNetworkModel(Model):
        """Class for models of stationary problems that use artificial neural networks.

        This class implements a |Model| that uses a neural network for solving.

        Parameters
        ----------
        neural_network
            The neural network that approximates the mapping from parameter space
            to solution space. Should be an instance of
            :class:`~pymor.models.neural_network.FullyConnectedNN` with input size that
            matches the (total) number of parameters and output size equal to the
            dimension of the reduced space.
        output_functional
            |Operator| mapping a given solution to the model output. In many applications,
            this will be a |Functional|, i.e. an |Operator| mapping to scalars.
            This is not required, however.
        products
            A dict of inner product |Operators| defined on the discrete space the
            problem is posed on. For each product with key `'x'` a corresponding
            attribute `x_product`, as well as a norm method `x_norm` is added to
            the model.
        error_estimator
            An error estimator for the problem. This can be any object with
            an `estimate_error(U, mu, m)` method. If `error_estimator` is
            not `None`, an `estimate_error(U, mu)` method is added to the
            model which will call `error_estimator.estimate_error(U, mu, self)`.
        visualizer
            A visualizer for the problem. This can be any object with
            a `visualize(U, m, ...)` method. If `visualizer`
            is not `None`, a `visualize(U, *args, **kwargs)` method is added
            to the model which forwards its arguments to the
            visualizer's `visualize` method.
        name
            Name of the model.
        """

        def __init__(self, neural_network, output_functional=None, products=None,
                     error_estimator=None, visualizer=None, name=None):

            super().__init__(products=products, error_estimator=error_estimator, visualizer=visualizer, name=name)

            self.__auto_init(locals())
            self.solution_space = NumpyVectorSpace(neural_network.output_dimension)
            self.linear = output_functional is None or output_functional.linear
            if output_functional is not None:
                self.output_space = output_functional.range

        def _solve(self, mu=None, return_output=False):
            if not self.logging_disabled:
                self.logger.info(f'Solving {self.name} for {mu} ...')

            # convert the parameter `mu` into a form that is usable in PyTorch
            converted_input = torch.from_numpy(mu.to_numpy()).double()
            # obtain (reduced) coordinates by forward pass of the parameter values through the neural network
            U = self.neural_network(converted_input).data.numpy()
            # convert plain numpy array to element of the actual solution space
            U = self.solution_space.make_array(U)

            if return_output:
                if self.output_functional is None:
                    raise ValueError('Model has no output')
                return U, self.output_functional.apply(U, mu=mu)
            else:
                return U
Exemplo n.º 27
0
class NumpyMatrixOperator(NumpyMatrixBasedOperator):
    """Wraps a 2D |NumPy Array| as an |Operator|.

    Parameters
    ----------
    matrix
        The |NumPy array| which is to be wrapped.
    name
        Name of the operator.
    """

    def __init__(self, matrix, source_id=None, range_id=None, solver_options=None, name=None):
        assert matrix.ndim <= 2
        if matrix.ndim == 1:
            matrix = np.reshape(matrix, (1, -1))
        try:
            matrix.setflags(write=False)  # make numpy arrays read-only
        except AttributeError:
            pass
        self.source = NumpyVectorSpace(matrix.shape[1], source_id)
        self.range = NumpyVectorSpace(matrix.shape[0], range_id)
        self.solver_options = solver_options
        self.name = name
        self.matrix = matrix
        self.source_id = source_id
        self.range_id = range_id
        self.sparse = issparse(matrix)

    @classmethod
    def from_file(cls, path, key=None, source_id=None, range_id=None, solver_options=None, name=None):
        from pymor.tools.io import load_matrix
        matrix = load_matrix(path, key=key)
        return cls(matrix, solver_options=solver_options, source_id=source_id, range_id=range_id,
                   name=name or key or path)

    @property
    def H(self):
        options = {'inverse': self.solver_options.get('inverse_adjoint'),
                   'inverse_adjoint': self.solver_options.get('inverse')} if self.solver_options else None
        if self.sparse:
            adjoint_matrix = self.matrix.transpose(copy=False).conj(copy=False)
        elif np.isrealobj(self.matrix):
            adjoint_matrix = self.matrix.T
        else:
            adjoint_matrix = self.matrix.T.conj()
        return self.with_(matrix=adjoint_matrix, source_id=self.range_id, range_id=self.source_id,
                          solver_options=options, name=self.name + '_adjoint')

    def _assemble(self, mu=None):
        pass

    def assemble(self, mu=None):
        return self

    def as_range_array(self, mu=None):
        return self.range.make_array(self.matrix.T.copy())

    def as_source_array(self, mu=None):
        return self.source.make_array(self.matrix.copy()).conj()

    def apply(self, U, mu=None):
        assert U in self.source
        return self.range.make_array(self.matrix.dot(U.to_numpy().T).T)

    def apply_adjoint(self, V, mu=None):
        assert V in self.range
        return self.H.apply(V, mu=mu)

    @defaults('check_finite', 'default_sparse_solver_backend')
    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 containes finite values.
        default_solver
            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('{}: {}'.format(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('{}: {}'.format(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_adjoint(self, U, mu=None, least_squares=False):
        return self.H.apply_inverse(U, mu=mu, least_squares=least_squares)

    def assemble_lincomb(self, operators, coefficients, solver_options=None, name=None):
        if not all(isinstance(op, (NumpyMatrixOperator, ZeroOperator, IdentityOperator)) for op in operators):
            return None

        common_mat_dtype = reduce(np.promote_types,
                                  (op.matrix.dtype for op in operators if hasattr(op, 'matrix')))
        common_coef_dtype = reduce(np.promote_types, (type(c) for c in coefficients))
        common_dtype = np.promote_types(common_mat_dtype, common_coef_dtype)

        if coefficients[0] == 1:
            matrix = operators[0].matrix.astype(common_dtype)
        else:
            matrix = operators[0].matrix * coefficients[0]
            if matrix.dtype != common_dtype:
                matrix = matrix.astype(common_dtype)

        for op, c in zip(operators[1:], coefficients[1:]):
            if type(op) is ZeroOperator:
                continue
            elif type(op) is IdentityOperator:
                if c.imag == 0:
                    c = c.real
                if operators[0].sparse:
                    try:
                        matrix += (scipy.sparse.eye(matrix.shape[0]) * c)
                    except NotImplementedError:
                        matrix = matrix + (scipy.sparse.eye(matrix.shape[0]) * c)
                else:
                    matrix += (np.eye(matrix.shape[0]) * c)
            elif c == 1:
                try:
                    matrix += op.matrix
                except NotImplementedError:
                    matrix = matrix + op.matrix
            elif c == -1:
                try:
                    matrix -= op.matrix
                except NotImplementedError:
                    matrix = matrix - op.matrix
            else:
                try:
                    matrix += (op.matrix * c)
                except NotImplementedError:
                    matrix = matrix + (op.matrix * c)
        return NumpyMatrixOperator(matrix,
                                   source_id=self.source.id,
                                   range_id=self.range.id,
                                   solver_options=solver_options)

    def __getstate__(self):
        if hasattr(self.matrix, 'factorization'):  # remove unplicklable SuperLU factorization
            del self.matrix.factorization
        return self.__dict__
Exemplo n.º 28
0
 def transfer(va):
     range_solution = localizer.to_space(
         local_op.apply_inverse(
             -rhsop.apply(NumpyVectorSpace.make_array(va))), training_space,
         range_space)
     return pou[range_space](range_solution).data
Exemplo n.º 29
0
def _random_array(dims, length, seed):
    np.random.seed(seed + mpi.rank)
    dim = dims[mpi.rank] if len(dims) > 1 else dims[0]
    array = NumpyVectorSpace.make_array(np.random.random((length, dim)))
    obj_id = mpi.manage_object(array)
    return obj_id
Exemplo n.º 30
0
class ProjectedOperator(OperatorBase):
    """Generic |Operator| representing the projection of an |Operator| to a subspace.

    This operator is implemented as the concatenation of the linear combination with
    `source_basis`, application of the original `operator` and projection onto
    `range_basis`. As such, this operator can be used to obtain a reduced basis
    projection of any given |Operator|. However, no offline/online decomposition is
    performed, so this operator is mainly useful for testing before implementing
    offline/online decomposition for a specific application.

    This operator is instantiated in :func:`pymor.algorithms.projection.project`
    as a default implementation for parametric or nonlinear operators.

    Parameters
    ----------
    operator
        The |Operator| to project.
    range_basis
        See :func:`pymor.algorithms.projection.project`.
    source_basis
        See :func:`pymor.algorithms.projection.project`.
    product
        See :func:`pymor.algorithms.projection.project`.
    solver_options
        The |solver_options| for the projected operator.
    """

    linear = False

    def __init__(self, operator, range_basis, source_basis, product=None, solver_options=None):
        assert isinstance(operator, OperatorInterface)
        assert source_basis is None or source_basis in operator.source
        assert range_basis is None or range_basis in operator.range
        assert (product is None
                or (isinstance(product, OperatorInterface)
                    and range_basis is not None
                    and operator.range == product.source
                    and product.range == product.source))
        self.build_parameter_type(operator)
        self.source = NumpyVectorSpace(len(source_basis)) if source_basis is not None else operator.source
        self.range = NumpyVectorSpace(len(range_basis)) if range_basis is not None else operator.range
        self.solver_options = solver_options
        self.name = operator.name
        self.operator = operator
        self.source_basis = source_basis.copy() if source_basis is not None else None
        self.range_basis = range_basis.copy() if range_basis is not None else None
        self.linear = operator.linear
        self.product = product

    @property
    def H(self):
        if self.product:
            return super().H
        else:
            options = {'inverse': self.solver_options.get('inverse_adjoint'),
                       'inverse_adjoint': self.solver_options.get('inverse')} if self.solver_options else None
            return ProjectedOperator(self.operator.H, self.source_basis, self.range_basis, solver_options=options)

    def apply(self, U, mu=None):
        mu = self.parse_parameter(mu)
        if self.source_basis is None:
            if self.range_basis is None:
                return self.operator.apply(U, mu=mu)
            elif self.product is None:
                return self.range.make_array(self.operator.apply2(self.range_basis, U, mu=mu).T)
            else:
                V = self.operator.apply(U, mu=mu)
                return self.range.make_array(self.product.apply2(V, self.range_basis))
        else:
            UU = self.source_basis.lincomb(U.to_numpy())
            if self.range_basis is None:
                return self.operator.apply(UU, mu=mu)
            elif self.product is None:
                return self.range.make_array(self.operator.apply2(self.range_basis, UU, mu=mu).T)
            else:
                V = self.operator.apply(UU, mu=mu)
                return self.range.make_array(self.product.apply2(V, self.range_basis))

    def jacobian(self, U, mu=None):
        if self.linear:
            return self.assemble(mu)
        assert len(U) == 1
        mu = self.parse_parameter(mu)
        if self.source_basis is None:
            J = self.operator.jacobian(U, mu=mu)
        else:
            J = self.operator.jacobian(self.source_basis.lincomb(U.to_numpy()), mu=mu)
        from pymor.algorithms.projection import project
        pop = project(J, range_basis=self.range_basis, source_basis=self.source_basis,
                      product=self.product, name=self.name + '_jacobian')
        if self.solver_options:
            options = self.solver_options.get('jacobian')
            if options:
                pop = pop.with_(solver_options=options)
        return pop

    def assemble(self, mu=None):
        op = self.operator.assemble(mu=mu)
        if op == self.operator:  # avoid infinite recursion in apply_inverse default impl
            return self
        from pymor.algorithms.projection import project
        pop = project(op, range_basis=self.range_basis, source_basis=self.source_basis,
                      product=self.product)
        if self.solver_options:
            pop = pop.with_(solver_options=self.solver_options)
        return pop

    def apply_adjoint(self, V, mu=None):
        assert V in self.range
        if self.range_basis is not None:
            V = self.range_basis.lincomb(V.to_numpy())
        U = self.operator.apply_adjoint(V, mu)
        if self.source_basis is not None:
            U = self.source.make_array(U.dot(self.source_basis))
        return U
Exemplo n.º 31
0
class NumpyMatrixOperator(NumpyMatrixBasedOperator):
    """Wraps a 2D |NumPy Array| as an |Operator|.

    Parameters
    ----------
    matrix
        The |NumPy array| which is to be wrapped.
    name
        Name of the operator.
    """

    def __init__(self, matrix, source_id=None, range_id=None, solver_options=None, name=None):
        assert matrix.ndim <= 2
        if matrix.ndim == 1:
            matrix = np.reshape(matrix, (1, -1))
        self.source = NumpyVectorSpace(matrix.shape[1], source_id)
        self.range = NumpyVectorSpace(matrix.shape[0], range_id)
        self.solver_options = solver_options
        self.name = name
        self._matrix = matrix
        self.source_id = source_id
        self.range_id = range_id
        self.sparse = issparse(matrix)

    @classmethod
    def from_file(cls, path, key=None, source_id=None, range_id=None, solver_options=None, name=None):
        from pymor.tools.io import load_matrix
        matrix = load_matrix(path, key=key)
        return cls(matrix, solver_options=solver_options, source_id=source_id, range_id=range_id,
                   name=name or key or path)

    @property
    def T(self):
        options = {'inverse': self.solver_options.get('inverse_transpose'),
                   'inverse_transpose': self.solver_options.get('inverse')} if self.solver_options else None
        return self.with_(matrix=self._matrix.T, source_id=self.range_id, range_id=self.source_id,
                          solver_options=options, name=self.name + '_transposed')

    def _assemble(self, mu=None):
        pass

    def assemble(self, mu=None):
        return self

    def as_range_array(self, mu=None):
        assert not self.sparse
        return self.range.make_array(self._matrix.T.copy())

    def as_source_array(self, mu=None):
        assert not self.sparse
        return self.source.make_array(self._matrix.copy())

    def apply(self, U, mu=None):
        assert U in self.source
        return self.range.make_array(self._matrix.dot(U.data.T).T)

    def apply_transpose(self, V, mu=None):
        assert V in self.range
        return self.source.make_array(self._matrix.T.dot(V.data.T).T)

    @defaults('check_finite', 'default_sparse_solver_backend',
              qualname='pymor.operators.numpy.NumpyMatrixOperator.apply_inverse')
    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 containes finite values.
        default_solver
            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.warn('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.data.T)
                except np.linalg.LinAlgError as e:
                    raise InversionError('{}: {}'.format(str(type(e)), str(e)))
                R = R.T
            else:
                try:
                    R = np.linalg.solve(self._matrix, V.data.T).T
                except np.linalg.LinAlgError as e:
                    raise InversionError('{}: {}'.format(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_transpose(self, U, mu=None, least_squares=False):
        options = {'inverse': self.solver_options.get('inverse_transpose') if self.solver_options else None}
        transpose_op = NumpyMatrixOperator(self._matrix.T, source_id=self.range.id, range_id=self.source.id,
                                           solver_options=options)
        return transpose_op.apply_inverse(U, mu=mu, least_squares=least_squares)

    def projected_to_subbasis(self, dim_range=None, dim_source=None, name=None):
        """Project the operator to a subbasis.

        The purpose of this method is to further project an operator that has been
        obtained through :meth:`~pymor.operators.interfaces.OperatorInterface.projected`
        to subbases of the original projection bases, i.e. ::

            op.projected(r_basis, s_basis, prod).projected_to_subbasis(dim_range, dim_source)

        should be the same as ::

            op.projected(r_basis[:dim_range], s_basis[:dim_source], prod)

        For a |NumpyMatrixOperator| this amounts to extracting the upper-left
        (dim_range, dim_source) corner of the matrix it wraps.

        Parameters
        ----------
        dim_range
            Dimension of the range subbasis.
        dim_source
            Dimension of the source subbasis.
        name
            optional name for the returned |Operator|
        Returns
        -------
        The projected |Operator|.
        """
        assert dim_source is None or dim_source <= self.source.dim
        assert dim_range is None or dim_range <= self.range.dim
        name = name or '{}_projected_to_subbasis'.format(self.name)
        # copy instead of just slicing the matrix to ensure contiguous memory
        return NumpyMatrixOperator(self._matrix[:dim_range, :dim_source].copy(),
                                   source_id=self.source.id,
                                   range_id=self.range.id,
                                   solver_options=self.solver_options,
                                   name=name)

    def assemble_lincomb(self, operators, coefficients, solver_options=None, name=None):
        if not all(isinstance(op, (NumpyMatrixOperator, ZeroOperator, IdentityOperator)) for op in operators):
            return None

        common_mat_dtype = reduce(np.promote_types,
                                  (op._matrix.dtype for op in operators if hasattr(op, '_matrix')))
        common_coef_dtype = reduce(np.promote_types, (type(c.real if c.imag == 0 else c) for c in coefficients))
        common_dtype = np.promote_types(common_mat_dtype, common_coef_dtype)

        if coefficients[0] == 1:
            matrix = operators[0]._matrix.astype(common_dtype)
        else:
            if coefficients[0].imag == 0:
                matrix = operators[0]._matrix * coefficients[0].real
            else:
                matrix = operators[0]._matrix * coefficients[0]
            if matrix.dtype != common_dtype:
                matrix = matrix.astype(common_dtype)

        for op, c in zip(operators[1:], coefficients[1:]):
            if type(op) is ZeroOperator:
                continue
            elif type(op) is IdentityOperator:
                if operators[0].sparse:
                    try:
                        matrix += (scipy.sparse.eye(matrix.shape[0]) * c)
                    except NotImplementedError:
                        matrix = matrix + (scipy.sparse.eye(matrix.shape[0]) * c)
                else:
                    matrix += (np.eye(matrix.shape[0]) * c)
            elif c == 1:
                try:
                    matrix += op._matrix
                except NotImplementedError:
                    matrix = matrix + op._matrix
            elif c == -1:
                try:
                    matrix -= op._matrix
                except NotImplementedError:
                    matrix = matrix - op._matrix
            elif c.imag == 0:
                try:
                    matrix += (op._matrix * c.real)
                except NotImplementedError:
                    matrix = matrix + (op._matrix * c.real)
            else:
                try:
                    matrix += (op._matrix * c)
                except NotImplementedError:
                    matrix = matrix + (op._matrix * c)
        return NumpyMatrixOperator(matrix,
                                   source_id=self.source.id,
                                   range_id=self.range.id,
                                   solver_options=solver_options)

    def __getstate__(self):
        if hasattr(self._matrix, 'factorization'):  # remove unplicklable SuperLU factorization
            del self._matrix.factorization
        return self.__dict__
Exemplo n.º 32
0
 def as_vector(self, copy=True):
     vec = self.matrix.AsVector().FV().NumPy()
     return NumpyVectorSpace.make_array(vec.copy() if copy else vec)
Exemplo n.º 33
0
 def solve(va):
     solution = local_op.apply_inverse(
         -rhsop.apply(NumpyVectorSpace.make_array(va)))
     return solution.data
Exemplo n.º 34
0
class VectorArrayOperator(OperatorBase):
    """Wraps a |VectorArray| as an |Operator|.

    If `transposed` is `False`, the operator maps from `NumpyVectorSpace(len(array))`
    to `array.space` by forming linear combinations of the vectors in the array
    with given coefficient arrays.

    If `transposed == True`, the operator maps from `array.space` to
    `NumpyVectorSpace(len(array))` by forming the inner products of the argument
    with the vectors in the given array.

    Parameters
    ----------
    array
        The |VectorArray| which is to be treated as an operator.
    transposed
        See description above.
    name
        The name of the operator.
    """

    linear = True

    def __init__(self, array, transposed=False, space_id=None, name=None):
        self._array = array.copy()
        if transposed:
            self.source = array.space
            self.range = NumpyVectorSpace(len(array), space_id)
        else:
            self.source = NumpyVectorSpace(len(array), space_id)
            self.range = array.space
        self.transposed = transposed
        self.space_id = space_id
        self.name = name

    @property
    def T(self):
        return VectorArrayOperator(self._array, not self.transposed, self.space_id, self.name + '_transposed')

    def apply(self, U, mu=None):
        assert U in self.source
        if not self.transposed:
            return self._array.lincomb(U.data)
        else:
            return self.range.make_array(U.dot(self._array))

    def apply_transpose(self, V, mu=None):
        assert V in self.range
        if not self.transposed:
            return self.source.make_array(self._array.dot(V).T)
        else:
            return self._array.lincomb(V.data)

    def apply_inverse_transpose(self, U, mu=None, least_squares=False):
        transpose_op = VectorArrayOperator(self._array, transposed=not self.transposed)
        return transpose_op.apply_inverse(U, mu=mu, least_squares=least_squares)

    def assemble_lincomb(self, operators, coefficients, solver_options=None, name=None):

        transposed = operators[0].transposed
        if not all(isinstance(op, VectorArrayOperator) and op.transposed == transposed for op in operators):
            return None
        assert not solver_options

        if coefficients[0] == 1:
            array = operators[0]._array.copy()
        else:
            array = operators[0]._array * coefficients[0]
        for op, c in zip(operators[1:], coefficients[1:]):
            array.axpy(c, op._array)
        return VectorArrayOperator(array, transposed=transposed, name=name)

    def as_range_array(self, mu=None):
        if not self.transposed:
            return self._array.copy()
        else:
            super().as_range_array(mu)

    def as_source_array(self, mu=None):
        if self.transposed:
            return self._array.copy()
        else:
            super().as_source_array(mu)

    def restricted(self, dofs):
        assert all(0 <= c < self.range.dim for c in dofs)
        if not self.transposed:
            restricted_value = NumpyVectorSpace.make_array(self._array.components(dofs))
            return VectorArrayOperator(restricted_value, False), np.arange(self.source.dim, dtype=np.int32)
        else:
            raise NotImplementedError
Exemplo n.º 35
0
 def restricted(self, dofs):
     assert all(0 <= c < self.range.dim for c in dofs)
     restricted_value = NumpyVectorSpace.make_array(self._value.components(dofs))
     return ConstantOperator(restricted_value, NumpyVectorSpace(len(dofs))), dofs
Exemplo n.º 36
0
 def solve(va):
     g = localizer.to_space(NumpyVectorSpace.make_array(va), source_space,
                            omega_star_space)
     solution = bilifo.apply_inverse(g)
     return solution.data
Exemplo n.º 37
0
def localize_problem(p,
                     coarse_grid_resolution,
                     fine_grid_resolution,
                     mus=None,
                     calT=False,
                     calTd=False,
                     calQ=False,
                     dof_codim=2,
                     localization_codim=2,
                     discretizer=None,
                     m=1):

    assert coarse_grid_resolution > (m + 1) * 2
    assert fine_grid_resolution % coarse_grid_resolution == 0

    print("localizing problem")
    global_quantities = {}
    global_quantities["coarse_grid_resolution"] = coarse_grid_resolution
    local_quantities = {}

    # Diskretisierung auf dem feinen Gitter:
    diameter = 1. / fine_grid_resolution
    global_quantities["diameter"] = diameter
    if discretizer is None:
        discretizer = discretize_stationary_cg
    d, data = discretizer(p, diameter=diameter)
    grid = data["grid"]

    global_quantities["d"] = d
    global_quantities["data"] = data

    global_operator = d.operator.assemble(mus)
    global_quantities["op"] = global_operator
    global_quantities["op_not_assembled"] = d.operator
    global_rhs = d.rhs.assemble(mus)
    global_quantities["rhs"] = global_rhs

    op_fixed = copy.deepcopy(d.operator)

    # Skalierung der Dirichlet-Freiheitsgrade:
    try:
        dirichlet_dofs = data['boundary_info'].dirichlet_boundaries(dof_codim)
        for op in op_fixed.operators:
            op.assemble(mus).matrix[dirichlet_dofs, dirichlet_dofs] *= 1e5
        # d.rhs.assemble(mus)._matrix[:, dirichlet_dofs] *= 1e5
    except KeyError:
        pass

    global_operator_fixed = op_fixed.assemble(mus)
    global_quantities["op_fixed"] = global_operator_fixed
    global_quantities["op_fixed_not_assembled"] = op_fixed

    global_quantities["p"] = p

    try:
        dmask = data['boundary_info'].dirichlet_mask(dof_codim)
    except KeyError:
        dmask = None

    # Konstruktion der Teilraeume:
    subspaces, subspaces_per_codim = build_subspaces(
        *partition_any_grid(grid,
                            num_intervals=(coarse_grid_resolution,
                                           coarse_grid_resolution),
                            dmask=dmask,
                            codim=dof_codim))
    global_quantities["subspaces"] = subspaces
    global_quantities["subspaces_per_codim"] = subspaces_per_codim
    localizer = NumpyLocalizer(d.solution_space, subspaces['dofs'])
    global_quantities["localizer"] = localizer

    def create_m_patch(xspace, m):
        for i in range(m):
            space = xspace
            cspace = tuple(
                set(i for k in space if subspaces[k]['codim'] == 0
                    for i in subspaces[k]['cpatch'] if i not in space))
            xspace = tuple(
                set(i for k in cspace if subspaces[k]['codim'] == 1
                    for i in subspaces[k]['env']) | set(space))
        cxspace = tuple(
            set(i for k in xspace if subspaces[k]['codim'] == 0
                for i in subspaces[k]['cpatch'] if i not in xspace))
        return xspace, cxspace

    pou = localized_pou(subspaces, subspaces_per_codim, localizer,
                        coarse_grid_resolution, grid, localization_codim,
                        dof_codim)
    global_quantities["pou"] = pou
    spaces = [subspaces[s_id]["env"] for s_id in subspaces_per_codim[2]]
    global_quantities["spaces"] = spaces

    full_l2_product = d.products["l2"].assemble()
    full_h1_semi_product = d.products["h1_semi"].assemble()
    k_product = LincombOperator((full_h1_semi_product, full_l2_product),
                                (1, mus["k"]**2)).assemble()
    global_quantities["full_norm"] = induced_norm(k_product)
    global_quantities["k_product"] = k_product
    for xpos in range(coarse_grid_resolution - 1):
        for ypos in range(coarse_grid_resolution - 1):
            # print "localizing..."
            s_id = subspaces_per_codim[localization_codim][
                ypos + xpos * (coarse_grid_resolution - 1)]
            space = subspaces[s_id]["env"]
            ldict = {}
            local_quantities[space] = ldict
            ldict["pos"] = (xpos, ypos)

            # Konstruktion der lokalen Raeume:
            range_space = subspaces[s_id]["env"]
            ldict["range_space"] = range_space
            omega_space = tuple(
                sorted(
                    set(subspaces[s_id]['env'])
                    | set(subspaces[s_id]['cenv'])))
            ldict["omega_space"] = omega_space
            training_space, source_space = create_m_patch(range_space, m)
            # source_space = subspaces[s_id]["cxenv"]
            ldict["source_space"] = source_space
            # training_space = subspaces[s_id]["xenv"]
            ldict["training_space"] = training_space
            omega_star_space = tuple(
                sorted(set(training_space) | set(source_space)))
            ldict["omega_star_space"] = omega_star_space

            # lokale Shift-Loesung mit f(Dirichlet)
            local_op = localizer.localize_operator(global_operator,
                                                   training_space,
                                                   training_space)
            local_rhs = localizer.localize_operator(global_rhs, None,
                                                    training_space)
            local_solution = local_op.apply_inverse(local_rhs.as_range_array())
            local_solution = localizer.to_space(local_solution, training_space,
                                                range_space)
            local_solution = pou[range_space](local_solution)
            ldict["local_solution_dirichlet"] = local_solution

            # Dirichlet Transferoperator:
            rhsop = localizer.localize_operator(global_operator,
                                                training_space, source_space)
            transop_dirichlet = create_dirichlet_transfer(
                localizer, local_op, rhsop, source_space, training_space,
                range_space, pou)
            ldict["dirichlet_transfer"] = transop_dirichlet

            # subgrid
            xmin = max(0, xpos - m)
            xsize = min(xpos + 2 + m, coarse_grid_resolution) - xmin
            ymin = max(0, ypos - m)
            ysize = min(ypos + 2 + m, coarse_grid_resolution) - ymin
            ldict["posext"] = [(xmin / coarse_grid_resolution,
                                ymin / coarse_grid_resolution),
                               ((xmin + xsize) / coarse_grid_resolution,
                                (ymin + ysize) / coarse_grid_resolution)]
            mysubgrid = getsubgrid(grid,
                                   xmin,
                                   ymin,
                                   coarse_grid_resolution,
                                   xsize=xsize,
                                   ysize=ysize)
            mysubbi = SubGridBoundaryInfo(mysubgrid, grid,
                                          data['boundary_info'], 'robin')
            ld, ldata = discretizer(p, grid=mysubgrid, boundary_info=mysubbi)
            lop = ld.operator.assemble(mus)

            # index conversion
            ndofsext = len(ldata['grid'].parent_indices(dof_codim))
            global_dofnrsext = -100000000 * np.ones(
                shape=(d.solution_space.dim, ))
            global_dofnrsext[ldata['grid'].parent_indices(
                dof_codim)] = np.array(range(ndofsext))
            lvecext = localizer.localize_vector_array(
                NumpyVectorSpace.make_array(global_dofnrsext),
                omega_star_space).data[0]

            # Robin Transferoperator:
            bilifo = NumpyMatrixOperator(lop.matrix[:, lvecext][lvecext, :])
            transop_robin = create_robin_transfer(localizer, bilifo,
                                                  source_space,
                                                  omega_star_space,
                                                  range_space, pou)
            ldict["robin_transfer"] = transop_robin

            # lokale Shift-Loesung mit f(Robin)
            lrhs = ld.rhs.assemble(mus)
            llrhs = NumpyMatrixOperator(lrhs.matrix[lvecext.astype(int)])
            local_solution = bilifo.apply_inverse(llrhs.as_range_array())
            ldict["local_sol2"] = local_solution
            local_solution = localizer.to_space(local_solution,
                                                omega_star_space, range_space)
            local_solution_pou = pou[range_space](local_solution)
            ldict["local_solution_robin"] = local_solution_pou

            if calT:
                # Transfer-Matrix:
                ldict["transfer_matrix_robin"] = transop_robin.as_range_array(
                ).data.T

            if calTd:
                # Transfer-Matrix:
                ldict[
                    "transfer_matrix_dirichlet"] = transop_dirichlet.as_range_array(
                    ).data.T

            # Konstruktion der Produkte:
            range_k = localizer.localize_operator(k_product, range_space,
                                                  range_space)
            omstar_k = LincombOperator((NumpyMatrixOperator(
                ld.products["h1_semi"].assemble().matrix[:,
                                                         lvecext][lvecext, :]),
                                        NumpyMatrixOperator(
                                            ld.products["l2"].assemble(
                                            ).matrix[:, lvecext][lvecext, :])),
                                       (1, mus["k"]**2)).assemble()

            ldict["omega_star_product"] = omstar_k
            ldict["range_product"] = range_k

            if calQ:
                # Loesungs-Matrix:
                solution_op_robin = create_robin_solop(localizer, bilifo,
                                                       source_space,
                                                       omega_star_space)
                Q_r = solution_op_robin.as_range_array()
                ldict["solution_matrix_robin"] = Q_r.data.T
                source_Q_r_product = NumpyMatrixOperator(
                    omstar_k.apply(Q_r).data.T)
                ldict["source_product"] = source_Q_r_product

            lproduct = localizer.localize_operator(full_l2_product,
                                                   source_space, source_space)
            lmat = lproduct.matrix.tocoo()
            lmat.data = np.array([
                4. / 6. * diameter if (row == col) else diameter / 6.
                for row, col in zip(lmat.row, lmat.col)
            ])
            ldict["source_product"] = NumpyMatrixOperator(lmat.tocsc().astype(
                np.cfloat))

    return global_quantities, local_quantities
Exemplo n.º 38
0
 def localize_vector_array(self, va, ind):
     assert isinstance(va, NumpyVectorArray)
     subspace = self.join_spaces(ind)
     a = va._array[:, subspace]
     return NumpyVectorSpace.make_array(a)
Exemplo n.º 39
0
class NumpyGenericOperator(OperatorBase):
    """Wraps an arbitrary Python function between |NumPy arrays| as an |Operator|.

    Parameters
    ----------
    mapping
        The function to wrap. If `parameter_type` is `None`, the function is of
        the form `mapping(U)` and is expected to be vectorized. In particular::

            mapping(U).shape == U.shape[:-1] + (dim_range,).

        If `parameter_type` is not `None`, the function has to have the signature
        `mapping(U, mu)`.
    adjoint_mapping
        The adjoint function to wrap. If `parameter_type` is `None`, the function is of
        the form `adjoint_mapping(U)` and is expected to be vectorized. In particular::

            adjoint_mapping(U).shape == U.shape[:-1] + (dim_source,).

        If `parameter_type` is not `None`, the function has to have the signature
        `adjoint_mapping(U, mu)`.
    dim_source
        Dimension of the operator's source.
    dim_range
        Dimension of the operator's range.
    linear
        Set to `True` if the provided `mapping` and `adjoint_mapping` are linear.
    parameter_type
        The |ParameterType| of the |Parameters| the mapping accepts.
    solver_options
        The |solver_options| for the operator.
    name
        Name of the operator.
    """
    def __init__(self,
                 mapping,
                 adjoint_mapping=None,
                 dim_source=1,
                 dim_range=1,
                 linear=False,
                 parameter_type=None,
                 source_id=None,
                 range_id=None,
                 solver_options=None,
                 name=None):
        self.source = NumpyVectorSpace(dim_source, source_id)
        self.range = NumpyVectorSpace(dim_range, range_id)
        self.solver_options = solver_options
        self.name = name
        self._mapping = mapping
        self._adjoint_mapping = adjoint_mapping
        self.linear = linear
        if parameter_type is not None:
            self.build_parameter_type(parameter_type)
        self.source_id = source_id  # needed for with_
        self.range_id = range_id

    def apply(self, U, mu=None):
        assert U in self.source
        if self.parametric:
            mu = self.parse_parameter(mu)
            return self.range.make_array(self._mapping(U.to_numpy(), mu=mu))
        else:
            return self.range.make_array(self._mapping(U.to_numpy()))

    def apply_adjoint(self, V, mu=None):
        if self._adjoint_mapping is None:
            raise ValueError(
                'NumpyGenericOperator: adjoint mapping was not defined.')
        assert V in self.range
        V = V.to_numpy()
        if self.parametric:
            mu = self.parse_parameter(mu)
            return self.source.make_array(self._adjoint_mapping(V, mu=mu))
        else:
            return self.source.make_array(self._adjoint_mapping(V))
Exemplo n.º 40
0
    class NeuralNetworkInstationaryModel(Model):
        """Class for models of instationary problems that use artificial neural networks.

        This class implements a |Model| that uses a neural network for solving.

        Parameters
        ----------
        T
            The final time T.
        nt
            The number of time steps.
        neural_network
            The neural network that approximates the mapping from parameter space
            to solution space. Should be an instance of
            :class:`~pymor.models.neural_network.FullyConnectedNN` with input size that
            matches the (total) number of parameters and output size equal to the
            dimension of the reduced space.
        parameters
            |Parameters| of the reduced order model (the same as used in the full-order
            model).
        output_functional
            |Operator| mapping a given solution to the model output. In many applications,
            this will be a |Functional|, i.e. an |Operator| mapping to scalars.
            This is not required, however.
        products
            A dict of inner product |Operators| defined on the discrete space the
            problem is posed on. For each product with key `'x'` a corresponding
            attribute `x_product`, as well as a norm method `x_norm` is added to
            the model.
        error_estimator
            An error estimator for the problem. This can be any object with
            an `estimate_error(U, mu, m)` method. If `error_estimator` is
            not `None`, an `estimate_error(U, mu)` method is added to the
            model which will call `error_estimator.estimate_error(U, mu, self)`.
        visualizer
            A visualizer for the problem. This can be any object with
            a `visualize(U, m, ...)` method. If `visualizer`
            is not `None`, a `visualize(U, *args, **kwargs)` method is added
            to the model which forwards its arguments to the
            visualizer's `visualize` method.
        name
            Name of the model.
        """
        def __init__(self,
                     T,
                     nt,
                     neural_network,
                     parameters={},
                     output_functional=None,
                     products=None,
                     error_estimator=None,
                     visualizer=None,
                     name=None):

            super().__init__(products=products,
                             error_estimator=error_estimator,
                             visualizer=visualizer,
                             name=name)

            self.__auto_init(locals())
            self.solution_space = NumpyVectorSpace(
                neural_network.output_dimension)
            if output_functional is not None:
                self.dim_output = output_functional.range.dim

        def _compute_solution(self, mu=None, **kwargs):

            U = self.solution_space.empty(reserve=self.nt)
            dt = self.T / (self.nt - 1)
            t = 0.

            # iterate over time steps
            for i in range(self.nt):
                mu = mu.with_(t=t)
                # convert the parameter `mu` into a form that is usable in PyTorch
                converted_input = torch.from_numpy(mu.to_numpy()).double()
                # obtain (reduced) coordinates by forward pass of the parameter values through the neural network
                result_neural_network = self.neural_network(
                    converted_input).data.numpy()
                # convert plain numpy array to element of the actual solution space
                U.append(self.solution_space.make_array(result_neural_network))
                t += dt

            return U
Exemplo n.º 41
0
    def localize_operator(self, op, range_ind, source_ind):
        if isinstance(op, VectorArrayOperator):
            assert range_ind is None
            source_ind = tuple(source_ind)
            source_subspace = self.join_spaces(source_ind)
            a = NumpyVectorSpace.make_array(op._array.data[:, source_subspace])
            result = VectorArrayOperator(a,
                                         transposed=op.transposed,
                                         name='{}-{}-{}'.format(
                                             op.name, source_ind, range_ind))
            return result

        if isinstance(op, NumpyMatrixOperator):
            # special case for functionals
            if range_ind is None:
                source_ind = tuple(source_ind)
                source_subspace = self.join_spaces(source_ind)
                m = op.matrix[source_subspace]
                result = NumpyMatrixOperator(m,
                                             name='{}-{}-{}'.format(
                                                 op.name, source_ind,
                                                 range_ind))
                return result

            source_ind = tuple(source_ind)
            range_ind = tuple(range_ind)
            op_id = getattr(op, 'sid', op.uid)
            identification_key = (op_id, source_ind, range_ind)
            if identification_key in self._resultcache:
                return self._resultcache[identification_key]

            if op_id not in self._non_zeros:
                # FIXME also handle dense case
                M = op.matrix.tocoo()
                incidences = np.empty(len(M.col),
                                      dtype=[('row', np.int32),
                                             ('col', np.int32)])
                incidences['row'] = self.subspace_map[M.row]
                incidences['col'] = self.subspace_map[M.col]
                incidences = np.unique(incidences)
                self._non_zeros[op_id] = set(incidences.tolist())

            non_zeros = self._non_zeros[op_id]
            if all((ri, si) not in non_zeros
                   for si, ri in product(source_ind, range_ind)):
                return None

            source_subspace = self.join_spaces(source_ind)
            range_subspace = self.join_spaces(range_ind)
            if issparse(op.matrix):
                m = op.matrix.tocsc()[:, source_subspace][range_subspace, :]
            else:
                m = op.matrix[:, source_subspace][range_subspace, :]

            result = NumpyMatrixOperator(m,
                                         name='{}-{}-{}'.format(
                                             op.name, source_ind, range_ind))
            self._resultcache[identification_key] = result
            return result

        elif isinstance(op, LincombOperator):
            ops = [
                self.localize_operator(o, range_ind, source_ind)
                for o in op.operators
            ]
            return LincombOperator(ops, op.coefficients)
        else:
            print("op is ", op)
            raise NotImplementedError
Exemplo n.º 42
0
class ProjectedOperator(OperatorBase):
    """Generic |Operator| representing the projection of an |Operator| to a subspace.

    This operator is implemented as the concatenation of the linear combination with
    `source_basis`, application of the original `operator` and projection onto
    `range_basis`. As such, this operator can be used to obtain a reduced basis
    projection of any given |Operator|. However, no offline/online decomposition is
    performed, so this operator is mainly useful for testing before implementing
    offline/online decomposition for a specific application.

    This operator is instantiated in the default implementation of
    :meth:`~pymor.operators.interfaces.OperatorInterface.projected` in
    :class:`OperatorBase` for parametric or nonlinear operators.

    Parameters
    ----------
    operator
        The |Operator| to project.
    source_basis
        See :meth:`~pymor.operators.interfaces.OperatorInterface.projected`.
    range_basis
        See :meth:`~pymor.operators.interfaces.OperatorInterface.projected`.
    product
        See :meth:`~pymor.operators.interfaces.OperatorInterface.projected`.
    name
        Name of the projected operator.
    """

    linear = False

    def __init__(self, operator, range_basis, source_basis, product=None, solver_options=None, name=None):
        assert isinstance(operator, OperatorInterface)
        assert source_basis is None or source_basis in operator.source
        assert range_basis is None or range_basis in operator.range
        assert product is None \
            or (isinstance(product, OperatorInterface)
                and range_basis is not None
                and operator.range == product.source
                and product.range == product.source)
        self.build_parameter_type(operator)
        self.source = NumpyVectorSpace(len(source_basis), operator.source.id) if source_basis is not None else operator.source
        self.range = NumpyVectorSpace(len(range_basis), operator.range.id) if range_basis is not None else operator.range
        self.solver_options = solver_options
        self.name = name
        self.operator = operator
        self.source_basis = source_basis.copy() if source_basis is not None else None
        self.range_basis = range_basis.copy() if range_basis is not None else None
        self.linear = operator.linear
        self.product = product

    @property
    def T(self):
        if self.product:
            return super().T
        else:
            options = {'inverse': self.solver_options.get('inverse_transpose'),
                       'inverse_transpose': self.solver_options.get('inverse')} if self.solver_options else None
            return ProjectedOperator(self.operator.T, self.source_basis, self.range_basis,
                                     solver_options=options, name=self.name + '_transposed')

    def apply(self, U, mu=None):
        mu = self.parse_parameter(mu)
        if self.source_basis is None:
            if self.range_basis is None:
                return self.operator.apply(U, mu=mu)
            elif self.product is None:
                return self.range.make_array(self.operator.apply2(self.range_basis, U, mu=mu).T)
            else:
                V = self.operator.apply(U, mu=mu)
                return self.range.make_array(self.product.apply2(V, self.range_basis))
        else:
            UU = self.source_basis.lincomb(U.data)
            if self.range_basis is None:
                return self.operator.apply(UU, mu=mu)
            elif self.product is None:
                return self.range.make_array(self.operator.apply2(self.range_basis, UU, mu=mu).T)
            else:
                V = self.operator.apply(UU, mu=mu)
                return self.range.make_array(self.product.apply2(V, self.range_basis))

    def projected_to_subbasis(self, dim_range=None, dim_source=None, name=None):
        """See :meth:`NumpyMatrixOperator.projected_to_subbasis`."""
        assert dim_source is None or dim_source <= self.source.dim
        assert dim_range is None or dim_range <= self.range.dim
        assert dim_source is None or self.source_basis is not None, 'not implemented'
        assert dim_range is None or self.range_basis is not None, 'not implemented'
        name = name or '{}_projected_to_subbasis'.format(self.name)
        source_basis = self.source_basis if dim_source is None \
            else self.source_basis[:dim_source]
        range_basis = self.range_basis if dim_range is None \
            else self.range_basis[:dim_range]
        return ProjectedOperator(self.operator, range_basis, source_basis, product=None,
                                 solver_options=self.solver_options, name=name)

    def jacobian(self, U, mu=None):
        if self.linear:
            return self.assemble(mu)
        assert len(U) == 1
        mu = self.parse_parameter(mu)
        if self.source_basis is None:
            J = self.operator.jacobian(U, mu=mu)
        else:
            J = self.operator.jacobian(self.source_basis.lincomb(U.data), mu=mu)
        pop = J.projected(range_basis=self.range_basis, source_basis=self.source_basis,
                          product=self.product, name=self.name + '_jacobian')
        if self.solver_options:
            options = self.solver_options.get('jacobian')
            if options:
                pop = pop.with_(solver_options=options)
        return pop

    def assemble(self, mu=None):
        op = self.operator.assemble(mu=mu)
        pop = op.projected(range_basis=self.range_basis, source_basis=self.source_basis,
                           product=self.product, name=self.name + '_assembled')
        if self.solver_options:
            pop = pop.with_(solver_options=self.solver_options)
        return pop
Exemplo n.º 43
0
def discrete_multiply(va, function):
    assert len(function.shape) == 1
    return NumpyVectorSpace.make_array(va.data * function)
Exemplo n.º 44
0
 def restricted(self, dofs):
     assert all(0 <= c < self.range.dim for c in dofs)
     restricted_value = NumpyVectorSpace.make_array(self._value.dofs(dofs))
     return ConstantOperator(restricted_value,
                             NumpyVectorSpace(len(dofs))), dofs
Exemplo n.º 45
0
class NumpyGenericOperator(OperatorBase):
    """Wraps an arbitrary Python function between |NumPy arrays| as a an |Operator|.

    Parameters
    ----------
    mapping
        The function to wrap. If `parameter_type` is `None`, the function is of
        the form `mapping(U)` and is expected to be vectorized. In particular::

            mapping(U).shape == U.shape[:-1] + (dim_range,).

        If `parameter_type` is not `None`, the function has to have the signature
        `mapping(U, mu)`.
    adjoint_mapping
        The adjoint function to wrap. If `parameter_type` is `None`, the function is of
        the form `adjoint_mapping(U)` and is expected to be vectorized. In particular::

            adjoint_mapping(U).shape == U.shape[:-1] + (dim_source,).

        If `parameter_type` is not `None`, the function has to have the signature
        `adjoint_mapping(U, mu)`.
    dim_source
        Dimension of the operator's source.
    dim_range
        Dimension of the operator's range.
    linear
        Set to `True` if the provided `mapping` and `adjoint_mapping` are linear.
    parameter_type
        The |ParameterType| of the |Parameters| the mapping accepts.
    name
        Name of the operator.
    """

    def __init__(self, mapping, adjoint_mapping=None, dim_source=1, dim_range=1, linear=False, parameter_type=None,
                 source_id=None, range_id=None, solver_options=None, name=None):
        self.source = NumpyVectorSpace(dim_source, source_id)
        self.range = NumpyVectorSpace(dim_range, range_id)
        self.solver_options = solver_options
        self.name = name
        self._mapping = mapping
        self._adjoint_mapping = adjoint_mapping
        self.linear = linear
        if parameter_type is not None:
            self.build_parameter_type(parameter_type)
        self.source_id = source_id  # needed for with_
        self.range_id = range_id

    def apply(self, U, mu=None):
        assert U in self.source
        if self.parametric:
            mu = self.parse_parameter(mu)
            return self.range.make_array(self._mapping(U.to_numpy(), mu=mu))
        else:
            return self.range.make_array(self._mapping(U.to_numpy()))

    def apply_adjoint(self, V, mu=None):
        if self._adjoint_mapping is None:
            raise ValueError('NumpyGenericOperator: adjoint mapping was not defined.')
        assert V in self.range
        V = V.to_numpy()
        if self.parametric:
            mu = self.parse_parameter(mu)
            return self.source.make_array(self._adjoint_mapping(V, mu=mu))
        else:
            return self.source.make_array(self._adjoint_mapping(V))
Exemplo n.º 46
0
class VectorArrayOperator(OperatorBase):
    """Wraps a |VectorArray| as an |Operator|.

    If `adjoint` is `False`, the operator maps from `NumpyVectorSpace(len(array))`
    to `array.space` by forming linear combinations of the vectors in the array
    with given coefficient arrays.

    If `adjoint == True`, the operator maps from `array.space` to
    `NumpyVectorSpace(len(array))` by forming the inner products of the argument
    with the vectors in the given array.

    Parameters
    ----------
    array
        The |VectorArray| which is to be treated as an operator.
    adjoint
        See description above.
    space_id
        Id of the `source` (`range`) |VectorSpace| in case `adjoint` is
        `False` (`True`).
    name
        The name of the operator.
    """

    linear = True

    def __init__(self, array, adjoint=False, space_id=None, name=None):
        self._array = array.copy()
        if adjoint:
            self.source = array.space
            self.range = NumpyVectorSpace(len(array), space_id)
        else:
            self.source = NumpyVectorSpace(len(array), space_id)
            self.range = array.space
        self.adjoint = adjoint
        self.space_id = space_id
        self.name = name

    @property
    def H(self):
        return VectorArrayOperator(self._array, not self.adjoint,
                                   self.space_id, self.name + '_adjoint')

    def apply(self, U, mu=None):
        assert U in self.source
        if not self.adjoint:
            return self._array.lincomb(U.to_numpy())
        else:
            return self.range.make_array(self._array.dot(U).T)

    def apply_adjoint(self, V, mu=None):
        assert V in self.range
        if not self.adjoint:
            return self.source.make_array(self._array.dot(V).T)
        else:
            return self._array.lincomb(V.to_numpy())

    def apply_inverse_adjoint(self, U, mu=None, least_squares=False):
        adjoint_op = VectorArrayOperator(self._array, adjoint=not self.adjoint)
        return adjoint_op.apply_inverse(U, mu=mu, least_squares=least_squares)

    def as_range_array(self, mu=None):
        if not self.adjoint:
            return self._array.copy()
        else:
            return super().as_range_array(mu)

    def as_source_array(self, mu=None):
        if self.adjoint:
            return self._array.copy()
        else:
            return super().as_source_array(mu)

    def restricted(self, dofs):
        assert all(0 <= c < self.range.dim for c in dofs)
        if not self.adjoint:
            restricted_value = NumpyVectorSpace.make_array(
                self._array.dofs(dofs))
            return VectorArrayOperator(restricted_value,
                                       False), np.arange(self.source.dim,
                                                         dtype=np.int32)
        else:
            raise NotImplementedError
Exemplo n.º 47
0
    p = EllipticProblem(domain=RectDomain([[0, 0], [1, 1]]),
                        diffusion_functions=(ConstantFunction(1.,
                                                              dim_domain=2), ),
                        diffusion_functionals=(1., ),
                        rhs=ConstantFunction(1., dim_domain=2))
    d, data = discretize_elliptic_cg(p, diameter=0.01)
    grid = data["grid"]

    subspaces, subspaces_per_codim = build_subspaces(*partition_any_grid(
        grid, num_intervals=(coarse_grid_resolution, coarse_grid_resolution)))

    localizer = NumpyLocalizer(d.solution_space, subspaces['dofs'])

    images = d.solution_space.empty()
    fdict = localized_pou(subspaces, subspaces_per_codim, localizer,
                          coarse_grid_resolution, grid)
    for space in sorted(fdict):
        lvec = localizer.localize_vector_array(
            NumpyVectorSpace.make_array(np.ones(d.solution_space.dim)), space)
        lvec = fdict[space](lvec)
        gvec = localizer.globalize_vector_array(lvec, space)
        images.append(gvec)

    sum = d.solution_space.zeros()
    for i in range(len(images)):
        sum += images.copy(ind=i)

    d.visualize(images)
    assert np.abs(np.max(sum.data) - 1) < 1e-12
    assert np.abs(np.min(sum.data) - 1) < 1e-12
Exemplo n.º 48
0
 def as_vector(self, copy=True):
     vec = self.matrix.AsVector().FV().NumPy()
     return NumpyVectorSpace.make_array(vec.copy() if copy else vec)
Exemplo n.º 49
0
def _random_array(dims, length, seed):
    np.random.seed(seed + mpi.rank)
    dim = dims[mpi.rank] if len(dims) > 1 else dims[0]
    array = NumpyVectorSpace.make_array(np.random.random((length, dim)))
    obj_id = mpi.manage_object(array)
    return obj_id
Exemplo n.º 50
0
class VectorArrayOperator(OperatorBase):
    """Wraps a |VectorArray| as an |Operator|.

    If `transposed` is `False`, the operator maps from `NumpyVectorSpace(len(array))`
    to `array.space` by forming linear combinations of the vectors in the array
    with given coefficient arrays.

    If `transposed == True`, the operator maps from `array.space` to
    `NumpyVectorSpace(len(array))` by forming the inner products of the argument
    with the vectors in the given array.

    Parameters
    ----------
    array
        The |VectorArray| which is to be treated as an operator.
    transposed
        See description above.
    name
        The name of the operator.
    """

    linear = True

    def __init__(self, array, transposed=False, space_id=None, name=None):
        self._array = array.copy()
        if transposed:
            self.source = array.space
            self.range = NumpyVectorSpace(len(array), space_id)
        else:
            self.source = NumpyVectorSpace(len(array), space_id)
            self.range = array.space
        self.transposed = transposed
        self.space_id = space_id
        self.name = name

    @property
    def T(self):
        return VectorArrayOperator(self._array, not self.transposed,
                                   self.space_id, self.name + '_transposed')

    def apply(self, U, mu=None):
        assert U in self.source
        if not self.transposed:
            return self._array.lincomb(U.data)
        else:
            return self.range.make_array(U.dot(self._array))

    def apply_transpose(self, V, mu=None):
        assert V in self.range
        if not self.transposed:
            return self.source.make_array(self._array.dot(V).T)
        else:
            return self._array.lincomb(V.data)

    def apply_inverse_transpose(self, U, mu=None, least_squares=False):
        transpose_op = VectorArrayOperator(self._array,
                                           transposed=not self.transposed)
        return transpose_op.apply_inverse(U,
                                          mu=mu,
                                          least_squares=least_squares)

    def assemble_lincomb(self,
                         operators,
                         coefficients,
                         solver_options=None,
                         name=None):

        transposed = operators[0].transposed
        if not all(
                isinstance(op, VectorArrayOperator)
                and op.transposed == transposed for op in operators):
            return None
        assert not solver_options

        if coefficients[0] == 1:
            array = operators[0]._array.copy()
        else:
            array = operators[0]._array * coefficients[0]
        for op, c in zip(operators[1:], coefficients[1:]):
            array.axpy(c, op._array)
        return VectorArrayOperator(array, transposed=transposed, name=name)

    def as_range_array(self, mu=None):
        if not self.transposed:
            return self._array.copy()
        else:
            super().as_range_array(mu)

    def as_source_array(self, mu=None):
        if self.transposed:
            return self._array.copy()
        else:
            super().as_source_array(mu)

    def restricted(self, dofs):
        assert all(0 <= c < self.range.dim for c in dofs)
        if not self.transposed:
            restricted_value = NumpyVectorSpace.make_array(
                self._array.dofs(dofs))
            return VectorArrayOperator(restricted_value,
                                       False), np.arange(self.source.dim,
                                                         dtype=np.int32)
        else:
            raise NotImplementedError
Exemplo n.º 51
0
class NumpyGenericOperator(Operator):
    """Wraps an arbitrary Python function between |NumPy arrays| as an |Operator|.

    Parameters
    ----------
    mapping
        The function to wrap. If `parameters` is `None`, the function is of
        the form `mapping(U)` and is expected to be vectorized. In particular::

            mapping(U).shape == U.shape[:-1] + (dim_range,).

        If `parameters` is not `None`, the function has to have the signature
        `mapping(U, mu)`.
    adjoint_mapping
        The adjoint function to wrap. If `parameters` is `None`, the function is of
        the form `adjoint_mapping(U)` and is expected to be vectorized. In particular::

            adjoint_mapping(U).shape == U.shape[:-1] + (dim_source,).

        If `parameters` is not `None`, the function has to have the signature
        `adjoint_mapping(U, mu)`.
    dim_source
        Dimension of the operator's source.
    dim_range
        Dimension of the operator's range.
    linear
        Set to `True` if the provided `mapping` and `adjoint_mapping` are linear.
    parameters
        The |Parameters| the depends on.
    solver_options
        The |solver_options| for the operator.
    name
        Name of the operator.
    """
    def __init__(self,
                 mapping,
                 adjoint_mapping=None,
                 dim_source=1,
                 dim_range=1,
                 linear=False,
                 parameters={},
                 source_id=None,
                 range_id=None,
                 solver_options=None,
                 name=None):
        self.__auto_init(locals())
        self.source = NumpyVectorSpace(dim_source, source_id)
        self.range = NumpyVectorSpace(dim_range, range_id)
        self.parameters_own = parameters

    def apply(self, U, mu=None):
        assert U in self.source
        assert self.parameters.assert_compatible(mu)
        if self.parametric:
            return self.range.make_array(self.mapping(U.to_numpy(), mu=mu))
        else:
            return self.range.make_array(self.mapping(U.to_numpy()))

    def apply_adjoint(self, V, mu=None):
        if self.adjoint_mapping is None:
            raise ValueError(
                'NumpyGenericOperator: adjoint mapping was not defined.')
        assert V in self.range
        assert self.parameters.assert_compatible(mu)
        V = V.to_numpy()
        if self.parametric:
            return self.source.make_array(self.adjoint_mapping(V, mu=mu))
        else:
            return self.source.make_array(self.adjoint_mapping(V))