예제 #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)
예제 #2
0
def test_block_matrix_representation_of_identity():
    # 2x2 block representation of the identity matrix
    A = BlockMatrix([[np.eye(2, 2), np.zeros((2, 2))],
                     [np.zeros((2, 2)), np.eye(2, 2)]])
    assert A.shape == (4, 4)
    assert A.nb_blocks == (2, 2)
    assert A.block_shapes == ([2, 2], [2, 2])
    assert list(A._stored_block_positions()) == [[(0, 0)], [(0, 2)], [(2, 0)],
                                                 [(2, 2)]]
    assert A.str_shape == "2×2×[2×2]"
    assert A.density == 1.0

    assert ((A + A) / 2 == A).all()
    assert (-A).min() == -1
    assert (2 * A).max() == 2
    assert (A * A == A).all()
    b = np.random.rand(4)
    assert (A @ b == b).all()
    assert (A @ A == A).all()

    patches = A._patches(global_frame=(10, 10))
    assert {rectangle.get_xy()
            for rectangle in patches} == {(10, 10), (12, 10), (10, 12),
                                          (12, 12)}

    assert (A == identity_like(A)).all()
    assert (A.full_matrix() == np.eye(4, 4)).all()

    # 2x2 block matrix containing one another block matrix and three regular matrices of different shapes
    B = BlockMatrix([[A, np.zeros((4, 1))],
                     [np.zeros((1, 4)), np.ones((1, 1))]])
    assert B.shape == (5, 5)
    assert B.block_shapes == ([4, 1], [4, 1])
    assert (B.full_matrix() == np.eye(5, 5)).all()
    assert B.str_shape == "[[2×2×[2×2], 4×1], [1×4, 1×1]]"
    assert B.block_shapes == ([4, 1], [4, 1])
    assert list(B._stored_block_positions()) == [[(0, 0)], [(0, 4)], [(4, 0)],
                                                 [(4, 4)]]

    patches = B._patches(global_frame=(10, 10))
    assert {rectangle.get_xy()
            for rectangle in patches} == {(10, 10), (12, 10), (10, 12),
                                          (12, 12), (14, 10), (10, 14),
                                          (14, 14)}

    # 3x3 block matrix with blocks of different shapes
    C = random_block_matrix([1, 2, 4], [1, 2, 4])
    assert C.nb_blocks == (3, 3)
    assert C.block_shapes == ([1, 2, 4], [1, 2, 4])
    assert (ones_like(C).full_matrix() == np.ones(C.shape)).all()

    assert (cut_matrix(C.full_matrix(), *C.block_shapes) == C).all()

    assert (C @ random_block_matrix(C.block_shapes[1], [6])).block_shapes == ([
        1, 2, 4
    ], [6])

    b = np.random.rand(7)
    assert np.allclose(C @ b, C.full_matrix() @ b)
예제 #3
0
def test_block_circulant_matrix():
    # 5x5 block symmetric circulant matrix reprensentation of the identity matrix
    A = BlockCirculantMatrix(
        [[np.eye(2, 2), np.zeros((2, 2)),
          np.zeros((2, 2))]])
    assert A.nb_blocks == (3, 3)
    assert A.shape == (6, 6)
    assert A.block_shapes == ([2, 2, 2], [2, 2, 2])
    assert (A.full_matrix() == np.eye(*A.shape)).all()

    assert ((A + A) / 2 == A).all()
    assert (-A).min() == -1
    assert (2 * A).max() == 2
    assert (A * A == A).all()

    b = np.random.rand(A.shape[0])
    assert np.allclose(A @ b, A.full_matrix() @ b)
    assert np.allclose(A.rmatvec(b), b @ A.full_matrix())

    # Nested matrix
    B = BlockCirculantMatrix([[A, 2 * A, 3 * A]])
    assert B.nb_blocks == (3, 3)
    assert B.shape == (18, 18)
    assert (B.all_blocks[0, 0] == A).all()

    C = BlockCirculantMatrix([[np.array([[i]]) for i in range(5)]])
    b = np.random.rand(C.shape[0])
    assert np.allclose(BlockMatrix.matvec(C, b), C.full_matrix() @ b)
    assert np.allclose(C @ b, C.full_matrix() @ b)
    assert np.allclose(C.rmatvec(b), b @ C.full_matrix())
예제 #4
0
def cut_matrix(full_matrix, x_shapes, y_shapes, check=False):
    """Transform a numpy array into a block matrix of numpy arrays.

    Parameters
    ----------
    full_matrix: numpy array
        The matrix to split into blocks.
    x_shapes: sequence of int
        The columns at which to split the blocks.
    y_shapes: sequence of int
        The lines at which to split the blocks.
    check: bool, optional
        Check to dimensions and type of the matrix after creation (default: False).

    Return
    ------
    BlockMatrix
        The same matrix as the input one but in block form.
    """
    new_block_matrix = []
    for i, di in zip(accumulate([0] + x_shapes[:-1]), x_shapes):
        line = []
        for j, dj in zip(accumulate([0] + x_shapes[:-1]), y_shapes):
            line.append(full_matrix[i:i + di, j:j + dj])
        new_block_matrix.append(line)
    return BlockMatrix(new_block_matrix, check=check)
예제 #5
0
 def block_diagonalize(self):
     """Returns an array of matrices"""
     if not hasattr(self, 'block_diagonalization'):
         if all(isinstance(matrix, BlockMatrix) for matrix in self._stored_blocks[0, :]):
             self.block_diagonalization = BlockMatrix.fft_of_list(*self.all_blocks[:, 0])
         else:
             stacked_blocks = np.empty((self.nb_blocks[1],) + self.block_shape, dtype=self.dtype)
             for i, block in enumerate(self.all_blocks[:, 0]):
                 stacked_blocks[i] = block.full_matrix() if not isinstance(block, np.ndarray) else block
             self.block_diagonalization =  np.fft.fft(stacked_blocks, axis=0)
     return self.block_diagonalization
예제 #6
0
def test_solve_block_toeplitz():
    A = BlockToeplitzMatrix([[(lambda: np.random.rand(1, 1))()
                              for _ in range(7)]])
    b = np.random.rand(A.shape[0])

    assert np.allclose(BlockMatrix.matvec(A, b), A.full_matrix() @ b)
    assert np.allclose(A @ b, A.full_matrix() @ b)

    x_gmres = solve_gmres(A, b)
    x_dumb_gmres = solve_gmres(A.full_matrix(), b)

    assert np.allclose(x_gmres, x_dumb_gmres, rtol=1e-6)
예제 #7
0
def test_block_symmetric_toeplitz_matrices():
    # 2x2 block symmetric Toeplitz representation of the identity matrix
    A = BlockSymmetricToeplitzMatrix([[np.eye(2, 2), np.zeros((2, 2))]])
    assert A.nb_blocks == (2, 2)
    assert A.block_shapes == ([2, 2], [2, 2])
    assert A.shape == (4, 4)
    assert A.block_shape == (2, 2)
    assert list(A._stored_block_positions()) == [[(0, 0), (2, 2)],
                                                 [(0, 2), (2, 0)]]
    assert (A.full_matrix() == np.eye(*A.shape)).all()

    assert ((A + A) / 2 == A).all()
    assert (-A).min() == -1
    assert (2 * A).max() == 2
    assert (A * A == A).all()

    b = np.random.rand(4)
    assert np.allclose(A @ b, b)
    assert np.allclose(A.rmatvec(b), b)

    # The same as a simpler block matrix
    A2 = BlockMatrix(A.all_blocks)
    assert (A2.full_matrix() == A.full_matrix()).all()

    # 2x2 blocks symmetric Toeplitz matrix of non-squared matrix
    A3 = BlockSymmetricToeplitzMatrix([[np.ones((3, 1)), np.zeros((3, 1))]])
    assert A3.nb_blocks == (2, 2)
    assert A3.block_shapes == ([3, 3], [1, 1])
    assert A3.shape == (6, 2)
    assert A3.block_shape == (3, 1)

    with pytest.raises(AssertionError):
        BlockSymmetricToeplitzMatrix([[np.ones((1, 1)), np.zeros((2, 2))]])

    # 2x2 block symmetrix Toeplitz matrix composed of block matrices
    B = BlockSymmetricToeplitzMatrix([[
        random_block_matrix([1, 1], [1, 1]),
        random_block_matrix([1, 1], [1, 1])
    ]])
    assert B.nb_blocks == (2, 2)
    assert B._stored_nb_blocks == (1, 2)
    assert B.block_shapes == ([2, 2], [2, 2])
    assert B.block_shape == (2, 2)
    assert B.shape == (4, 4)

    assert isinstance(B.all_blocks[0, 0], BlockMatrix)
    assert (B.all_blocks[0, 0] == B.all_blocks[1, 1]).all()

    list_of_matrices = [B, B + ones_like(B), B, B - ones_like(B)]
    BB_fft = BlockSymmetricToeplitzMatrix.fft_of_list(*list_of_matrices)
    full_BB_fft = np.fft.fft(np.array(
        [A.full_matrix() for A in list_of_matrices]),
                             axis=0)
    assert np.allclose(full_BB_fft,
                       np.array([A.full_matrix() for A in BB_fft]))

    # 2x2 block symmetric Toeplitz matrix composed of the previous block symmetric Toeplitz matrices
    C = BlockSymmetricToeplitzMatrix([[A, B]])
    assert C.shape == (8, 8)

    b = np.random.rand(8)
    assert np.allclose(C @ b, C.full_matrix() @ b)
    assert np.allclose(C.rmatvec(b), b @ C.full_matrix())

    # We need to go deeper
    D = BlockMatrix([[C, np.zeros(C.shape)]])
    assert D.shape == (8, 16)

    b = np.random.rand(16)
    assert np.allclose(D @ b, D.full_matrix() @ b)
예제 #8
0
def test_block_toeplitz_matrices():
    # 2x2 block Toeplitz representation of the identity matrix
    A = BlockToeplitzMatrix(
        [[np.eye(2, 2), np.zeros((2, 2)),
          np.zeros((2, 2))]])
    assert A.block_shapes == ([2, 2], [2, 2])
    assert A.block_shape == (2, 2)
    assert A.shape == (4, 4)
    assert list(A._stored_block_positions()) == [[(0, 0), (2, 2)], [(0, 2)],
                                                 [(2, 0)]]
    assert (A.full_matrix() == np.eye(*A.shape)).all()

    # Removing the Toeplitz structure.
    Ant = A.no_toeplitz()
    assert not isinstance(Ant, BlockToeplitzMatrix)
    assert Ant.all_blocks[0, 0] is Ant.all_blocks[1, 1]
    # When using no_toeplitz, the resulting BlockMatrix still contains several references to the same array object.

    from copy import deepcopy
    Ant_cp = deepcopy(Ant)
    assert Ant_cp.all_blocks[0, 0] is not Ant_cp.all_blocks[1, 1]
    # The deepcopy has made different copies of the blocks in the block matrix without explicit Toeplitz structure.

    # However,
    A_cp = deepcopy(A)
    assert A_cp.all_blocks[0, 0] is A_cp.all_blocks[1, 1]
    # The matrix with explicit Toeplitz structure still keep the structure when deepcopied.

    assert ((A + A) / 2 == A).all()
    assert (-A).min() == -1
    assert (2 * A).max() == 2
    assert (A * A == A).all()

    assert isinstance(A.circulant_super_matrix, BlockCirculantMatrix)
    assert A.circulant_super_matrix.shape == (6, 6)

    b = np.random.rand(A.shape[0])
    assert np.allclose(A @ b, b)
    assert np.allclose(A.rmatvec(b), b)

    # 3x3 block random Toeplitz matrix
    B = BlockToeplitzMatrix([[
        random_block_matrix([1], [1]),
        random_block_matrix([1], [1]),
        random_block_matrix([1], [1]),
        random_block_matrix([1], [1]),
        random_block_matrix([1], [1]),
    ]])
    assert B.shape == (3, 3)
    assert B.nb_blocks == (3, 3)
    assert B.block_shape == (1, 1)
    assert list(B._stored_block_positions()) == [[(0, 0), (1, 1), (2, 2)],
                                                 [(0, 1), (1, 2)], [(0, 2)],
                                                 [(2, 0)], [(1, 0), (2, 1)]]

    assert isinstance(B.circulant_super_matrix, BlockCirculantMatrix)
    assert B.circulant_super_matrix.shape == (5, 5)

    b = np.random.rand(B.shape[0])
    assert np.allclose(BlockMatrix.matvec(B, b), B.full_matrix() @ b)
    assert np.allclose(B @ b, B.full_matrix() @ b)
    assert np.allclose(B.rmatvec(b), b @ B.full_matrix())
예제 #9
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
예제 #10
0
def block_three_rect(two_by_two_block_identity):
    A = two_by_two_block_identity
    B = BlockMatrix([[A, np.zeros((4, 1))],
                     [np.zeros((1, 4)), np.ones((1, 1))]])
    return B
예제 #11
0
def two_by_two_block_identity():
    A = BlockMatrix([[np.eye(2, 2), np.zeros((2, 2))],
                     [np.zeros((2, 2)), np.eye(2, 2)]])
    return A
예제 #12
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)