Esempio n. 1
0
def test_2_in_1_ACA_with_different_matrices():
    n = 5
    A = np.arange(1, 1 + n**2).reshape((n, n)) + np.random.rand(n, n)
    B = A.T

    def get_row_func(i):
        return A[i, :], B[i, :]

    def get_col_func(j):
        return A[:, j], B[:, j]

    lrA, lrB = LowRankMatrix.from_rows_and_cols_functions_with_multi_ACA(
        get_row_func, get_col_func, n, n, nb_matrices=2, max_rank=3)

    assert matrix_rank(lrA.full_matrix()) == matrix_rank(
        lrB.full_matrix()) == 3
Esempio n. 2
0
def test_2_in_1_ACA_with_identical_matrices():
    n = 5
    A = np.arange(1, 1 + n**2).reshape((n, n)) + np.random.rand(n, n)
    B = A.copy()

    def get_row_func(i):
        return A[i, :], B[i, :]

    def get_col_func(j):
        return A[:, j], B[:, j]

    lrA, lrB = LowRankMatrix.from_rows_and_cols_functions_with_multi_ACA(
        get_row_func, get_col_func, n, n, nb_matrices=2, tol=1e-3)

    assert (lrA.full_matrix() == lrB.full_matrix()).all()
    assert norm(lrA.full_matrix() - A, 'fro') / norm(A, 'fro') < 3e-2
Esempio n. 3
0
def test_2_in_1_ACA_with_identical_matrices():
    n = 5
    A = np.fromfunction(lambda i, j: 1 / abs(i - (100 + j)), (n, n))
    B = A.copy()

    def get_row_func(i):
        return A[i, :], B[i, :]

    def get_col_func(j):
        return A[:, j], B[:, j]

    lrA, lrB = LowRankMatrix.from_rows_and_cols_functions_with_multi_ACA(
        get_row_func, get_col_func, n, n, nb_matrices=2, tol=1e-3)

    assert (lrA.full_matrix() == lrB.full_matrix()).all()
    assert norm(lrA.full_matrix() - A, 'fro') / norm(A, 'fro') < 3e-2
Esempio n. 4
0
    def build_matrices(self, mesh1, mesh2, *args, _rec_depth=1, **kwargs):
        """Recursively builds a hierarchical matrix between mesh1 and mesh2.
        
        Same arguments as :func:`BasicMatrixEngine.build_matrices`.

        :code:`_rec_depth` keeps track of the recursion depth only for pretty log printing.
        """

        if logging.getLogger().isEnabledFor(logging.DEBUG):
            log_entry = (
                "\t" * (_rec_depth + 1) +
                "Build the S and K influence matrices between {mesh1} and {mesh2}"
                .format(
                    mesh1=mesh1.name,
                    mesh2=(mesh2.name if mesh2 is not mesh1 else 'itself')))
        else:
            log_entry = ""  # will not be used

        green_function = args[-1]

        # Distance between the meshes (for ACA).
        distance = np.linalg.norm(mesh1.center_of_mass_of_nodes -
                                  mesh2.center_of_mass_of_nodes)

        # I) SPARSE COMPUTATION
        # I-i) BLOCK TOEPLITZ MATRIX

        if (isinstance(mesh1, ReflectionSymmetricMesh)
                and isinstance(mesh2, ReflectionSymmetricMesh)
                and mesh1.plane == mesh2.plane):

            LOG.debug(log_entry + " using mirror symmetry.")

            S_a, V_a = self.build_matrices(mesh1[0],
                                           mesh2[0],
                                           *args,
                                           **kwargs,
                                           _rec_depth=_rec_depth + 1)
            S_b, V_b = self.build_matrices(mesh1[0],
                                           mesh2[1],
                                           *args,
                                           **kwargs,
                                           _rec_depth=_rec_depth + 1)

            return BlockSymmetricToeplitzMatrix(
                [[S_a, S_b]]), BlockSymmetricToeplitzMatrix([[V_a, V_b]])

        elif (isinstance(mesh1, TranslationalSymmetricMesh)
              and isinstance(mesh2, TranslationalSymmetricMesh)
              and np.allclose(mesh1.translation, mesh2.translation)
              and mesh1.nb_submeshes == mesh2.nb_submeshes):

            LOG.debug(log_entry + " using translational symmetry.")

            S_list, V_list = [], []
            for submesh in mesh2:
                S, V = self.build_matrices(mesh1[0],
                                           submesh,
                                           *args,
                                           **kwargs,
                                           _rec_depth=_rec_depth + 1)
                S_list.append(S)
                V_list.append(V)
            for submesh in mesh1[1:][::-1]:
                S, V = self.build_matrices(submesh,
                                           mesh2[0],
                                           *args,
                                           **kwargs,
                                           _rec_depth=_rec_depth + 1)
                S_list.append(S)
                V_list.append(V)

            return BlockToeplitzMatrix([S_list]), BlockToeplitzMatrix([V_list])

        elif (isinstance(mesh1, AxialSymmetricMesh)
              and isinstance(mesh2, AxialSymmetricMesh)
              and mesh1.axis == mesh2.axis
              and mesh1.nb_submeshes == mesh2.nb_submeshes):

            LOG.debug(log_entry + " using rotation symmetry.")

            S_line, V_line = [], []
            for submesh in mesh2[:mesh2.nb_submeshes]:
                S, V = self.build_matrices(mesh1[0],
                                           submesh,
                                           *args,
                                           **kwargs,
                                           _rec_depth=_rec_depth + 1)
                S_line.append(S)
                V_line.append(V)

            return BlockCirculantMatrix([S_line
                                         ]), BlockCirculantMatrix([V_line])

        # I-ii) LOW-RANK MATRIX WITH ACA

        elif distance > self.ACA_distance * mesh1.diameter_of_nodes or distance > self.ACA_distance * mesh2.diameter_of_nodes:

            LOG.debug(log_entry + " using ACA.")

            def get_row_func(i):
                s, v = green_function.evaluate(mesh1.extract_one_face(i),
                                               mesh2, *args[:-1], **kwargs)
                return s.flatten(), v.flatten()

            def get_col_func(j):
                s, v = green_function.evaluate(mesh1,
                                               mesh2.extract_one_face(j),
                                               *args[:-1], **kwargs)
                return s.flatten(), v.flatten()

            return LowRankMatrix.from_rows_and_cols_functions_with_multi_ACA(
                get_row_func,
                get_col_func,
                mesh1.nb_faces,
                mesh2.nb_faces,
                nb_matrices=2,
                id_main=
                1,  # Approximate V and get an approximation of S at the same time
                tol=self.ACA_tol,
                dtype=np.complex128)

        # II) NON-SPARSE COMPUTATIONS
        # II-i) BLOCK MATRIX

        elif (isinstance(mesh1, CollectionOfMeshes)
              and isinstance(mesh2, CollectionOfMeshes)):

            LOG.debug(log_entry + " using block matrix structure.")

            S_matrix, V_matrix = [], []
            for submesh1 in mesh1:
                S_line, V_line = [], []
                for submesh2 in mesh2:
                    S, V = self.build_matrices(submesh1,
                                               submesh2,
                                               *args,
                                               **kwargs,
                                               _rec_depth=_rec_depth + 1)

                    S_line.append(S)
                    V_line.append(V)
                S_matrix.append(S_line)
                V_matrix.append(V_line)

            return BlockMatrix(S_matrix), BlockMatrix(V_matrix)

        # II-ii) PLAIN NUMPY ARRAY

        else:
            LOG.debug(log_entry)

            S, V = green_function.evaluate(mesh1, mesh2, *args[:-1], **kwargs)
            return S, V
Esempio n. 5
0
    def build_hierarchical_toeplitz_matrix(mesh1,
                                           mesh2,
                                           *args,
                                           _rec_depth=1,
                                           **kwargs):
        """Assemble hierarchical Toeplitz matrices.

        The method is basically an ugly multiple dispatch on the kind of mesh.
        For hierarchical structures, the method is called recursively on all of the sub-bodies.

        Parameters
        ----------
        mesh1: Mesh or CollectionOfMeshes
            mesh of the receiving body (where the potential is measured)
        mesh2: Mesh or CollectionOfMeshes
            mesh of the source body (over which the source distribution is integrated)
        *args, **kwargs
            Other arguments, passed to the actual evaluation of the coefficients
        _rec_depth: int, optional
            internal parameter: recursion accumulator, used only for pretty logging

        Returns
        -------
        pair of matrix-like objects
            influence matrices
        """

        if logging.getLogger().isEnabledFor(logging.DEBUG):
            log_entry = "\t" * (
                _rec_depth + 1) + function_description_for_logging.format(
                    mesh1=mesh1.name,
                    mesh2=(mesh2.name if mesh2 is not mesh1 else 'itself'))
        else:
            log_entry = ""  # irrelevant

        # Distance between the meshes (for ACA).
        distance = np.linalg.norm(mesh1.center_of_mass_of_nodes -
                                  mesh2.center_of_mass_of_nodes)

        # I) SPARSE COMPUTATION

        if (isinstance(mesh1, ReflectionSymmetricMesh)
                and isinstance(mesh2, ReflectionSymmetricMesh)
                and mesh1.plane == mesh2.plane):

            LOG.debug(log_entry + " using mirror symmetry.")

            S_a, V_a = build_hierarchical_toeplitz_matrix(
                mesh1[0], mesh2[0], *args, **kwargs, _rec_depth=_rec_depth + 1)
            S_b, V_b = build_hierarchical_toeplitz_matrix(
                mesh1[0], mesh2[1], *args, **kwargs, _rec_depth=_rec_depth + 1)

            return BlockSymmetricToeplitzMatrix(
                [[S_a, S_b]]), BlockSymmetricToeplitzMatrix([[V_a, V_b]])

        elif (isinstance(mesh1, TranslationalSymmetricMesh)
              and isinstance(mesh2, TranslationalSymmetricMesh)
              and np.allclose(mesh1.translation, mesh2.translation)
              and mesh1.nb_submeshes == mesh2.nb_submeshes):

            LOG.debug(log_entry + " using translational symmetry.")

            S_list, V_list = [], []
            for submesh in mesh2:
                S, V = build_hierarchical_toeplitz_matrix(
                    mesh1[0],
                    submesh,
                    *args,
                    **kwargs,
                    _rec_depth=_rec_depth + 1)
                S_list.append(S)
                V_list.append(V)
            for submesh in mesh1[1:][::-1]:
                S, V = build_hierarchical_toeplitz_matrix(
                    submesh,
                    mesh2[0],
                    *args,
                    **kwargs,
                    _rec_depth=_rec_depth + 1)
                S_list.append(S)
                V_list.append(V)

            return BlockToeplitzMatrix([S_list]), BlockToeplitzMatrix([V_list])

        elif (isinstance(mesh1, AxialSymmetricMesh)
              and isinstance(mesh2, AxialSymmetricMesh)
              and mesh1.axis == mesh2.axis
              and mesh1.nb_submeshes == mesh2.nb_submeshes):

            LOG.debug(log_entry + " using rotation symmetry.")

            S_line, V_line = [], []
            for submesh in mesh2[:mesh2.nb_submeshes]:
                S, V = build_hierarchical_toeplitz_matrix(
                    mesh1[0],
                    submesh,
                    *args,
                    **kwargs,
                    _rec_depth=_rec_depth + 1)
                S_line.append(S)
                V_line.append(V)

            return BlockCirculantMatrix([S_line
                                         ]), BlockCirculantMatrix([V_line])

        elif distance > ACA_distance * mesh1.diameter_of_nodes or distance > ACA_distance * mesh2.diameter_of_nodes:
            # Low-rank matrix computed with Adaptive Cross Approximation.

            LOG.debug(log_entry + " using ACA.")

            def get_row_func(i):
                s, v = build_matrices(mesh1.extract_one_face(i), mesh2, *args,
                                      **kwargs)
                return s.flatten(), v.flatten()

            def get_col_func(j):
                s, v = build_matrices(mesh1, mesh2.extract_one_face(j), *args,
                                      **kwargs)
                return s.flatten(), v.flatten()

            return LowRankMatrix.from_rows_and_cols_functions_with_multi_ACA(
                get_row_func,
                get_col_func,
                mesh1.nb_faces,
                mesh2.nb_faces,
                nb_matrices=2,
                id_main=
                1,  # Approximate V and get an approximation of S at the same time
                tol=ACA_tol,
                dtype=dtype)

        # II) NON-SPARSE COMPUTATIONS

        elif (isinstance(mesh1, CollectionOfMeshes)
              and isinstance(mesh2, CollectionOfMeshes)):
            # Recursively build a block matrix

            LOG.debug(log_entry + " using block matrix structure.")

            S_matrix, V_matrix = [], []
            for submesh1 in mesh1:
                S_line, V_line = [], []
                for submesh2 in mesh2:
                    S, V = build_hierarchical_toeplitz_matrix(
                        submesh1,
                        submesh2,
                        *args,
                        **kwargs,
                        _rec_depth=_rec_depth + 1)

                    S_line.append(S)
                    V_line.append(V)
                S_matrix.append(S_line)
                V_matrix.append(V_line)

            return BlockMatrix(S_matrix), BlockMatrix(V_matrix)

        else:
            # Actual evaluation of coefficients using the Green function.

            LOG.debug(log_entry)

            return build_matrices(mesh1, mesh2, *args, **kwargs)