Beispiel #1
0
def test_hierarchical_matrix():
    n = 30
    X = np.linspace(0, 1, n)
    Y = np.linspace(10, 11, n)

    def f(i, j):
        return 1 / abs(X[i] - Y[j])

    S = np.array([[f(i, j) for j in range(n)] for i in range(n)])

    HS = BlockMatrix([
        [
            S[:n // 2, :n // 2],
            LowRankMatrix.from_full_matrix_with_ACA(S[n // 2:, :n // 2],
                                                    tol=1e-5)
        ],
        [
            LowRankMatrix.from_full_matrix_with_ACA(S[:n // 2, n // 2:],
                                                    tol=1e-5), S[n // 2:,
                                                                 n // 2:]
        ],
    ])

    assert np.allclose(HS.full_matrix(), S, rtol=2e-1)

    doubled = HS + HS
    assert np.allclose(2 * S, doubled.full_matrix(), rtol=2e-1)
Beispiel #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
Beispiel #3
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
Beispiel #4
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
Beispiel #5
0
def full_like(A, value, dtype=np.float64):
    """A matrix of the same kind and shape as A but filled with a single value."""
    if isinstance(A, BlockMatrix):
        new_matrix = []
        for i in range(A._stored_nb_blocks[0]):
            line = []
            for j in range(A._stored_nb_blocks[1]):
                line.append(
                    full_like(A._stored_blocks[i, j], value, dtype=dtype))
            new_matrix.append(line)
        return A.__class__(new_matrix)
    elif isinstance(A, LowRankMatrix):
        return LowRankMatrix(np.ones((A.shape[0], 1)),
                             np.full((1, A.shape[1]), value))
    elif isinstance(A, np.ndarray):
        return np.full_like(A, value, dtype=dtype)
Beispiel #6
0
def test_low_rank_blocks():
    n = 10

    # Test initialization
    a, b = np.random.rand(n, 1), np.random.rand(1, n)
    LR = LowRankMatrix(a, b)
    assert LR.shape == LR.full_matrix().shape
    assert matrix_rank(LR.full_matrix()) == LR.rank == 1
    assert LR.density == 2 * n / n**2

    a, b = np.random.rand(n, 2), np.random.rand(2, n)
    LR = LowRankMatrix(a, b)
    assert LR.shape == LR.full_matrix().shape
    assert matrix_rank(LR.full_matrix()) == LR.rank == 2

    # Test creation from SVD
    A = np.arange(n**2).reshape((n, n)) + 1.0
    dumb_low_rank = LowRankMatrix.from_full_matrix_with_SVD(A, n)
    assert np.allclose(dumb_low_rank.full_matrix() - A, 0.0)

    A_rank_1 = LowRankMatrix.from_full_matrix_with_SVD(A, 1)
    assert matrix_rank(A_rank_1.full_matrix()) == A_rank_1.rank == 1

    # Test recompression
    recompressed = dumb_low_rank.recompress(new_rank=2)
    assert recompressed.rank == matrix_rank(recompressed.full_matrix()) == 2

    recompressed = dumb_low_rank.recompress(tol=1e-1)
    assert recompressed.rank <= dumb_low_rank.rank

    # Test multiplication with vector
    b = np.random.rand(n)
    assert np.allclose(A_rank_1 @ b, A_rank_1.full_matrix() @ b)

    # Test creation with ACA
    full_A_rank_1 = A_rank_1.full_matrix()
    A_rank_1_again = LowRankMatrix.from_full_matrix_with_ACA(full_A_rank_1,
                                                             max_rank=5)
    assert matrix_rank(A_rank_1_again.full_matrix()) == 1
    assert np.allclose(A_rank_1_again.full_matrix(), full_A_rank_1)

    # Test creation from function with ACA
    X = np.linspace(0, 1, n)
    Y = np.linspace(5, 6, n)

    def f(i, j):
        return 1 / abs(X[i] - Y[j])

    S = np.array([[f(i, j) for j in range(n)] for i in range(n)])
    SLR = LowRankMatrix.from_function_with_ACA(f, n, n, max_rank=2, tol=1e-5)
    assert SLR.shape == (n, n)
    assert np.allclose(SLR.full_matrix(), S, atol=1e-4)

    summed = SLR + A_rank_1
    assert summed.rank == 1
Beispiel #7
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
Beispiel #8
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)