Source code for HierMat.hmat

"""hmat.py: :class:`HMat`
"""
import numpy

from rmat import RMat


[docs]class HMat(object): """Implement a hierarchical Matrix """ def __init__(self, blocks=(), content=None, shape=(), root_index=()): self.blocks = blocks # This list contains the lower level HMat self.content = content # If not empty, this is either a full matrix or a RMat self.shape = shape # Tuple of dimensions, i.e. size of index sets self.root_index = root_index # Tuple of coordinates for the top-left corner in the root matrix def __repr__(self): return '<HMat with {content}>'.format(content=self.blocks if self.blocks else self.content) def __eq__(self, other): """Test for equality :param other: other HMat :type other: HMat :return: true on equal :rtype: bool """ length = len(self.blocks) if len(other.blocks) != length: return False block_checks = [self.blocks[i] == other.blocks[i] for i in xrange(length)] if not all(block_checks): return False if type(self.content) is not type(other.content): return False if type(self.content) is RMat: if self.content != other.content: return False if type(self.content) is numpy.matrix: if not numpy.array_equal(self.content, other.content): return False if self.shape != other.shape: return False if self.root_index != other.root_index: return False return True def __ne__(self, other): """Test for inequality :param other: other HMat :type other: HMat :return: true on not equal :rtype: bool """ length = len(self.blocks) if len(other.blocks) != length: return True block_checks = [self.blocks[i] == other.blocks[i] for i in xrange(length)] if not all(block_checks): return True if type(self.content) != type(other.content): return True if type(self.content) is RMat: if self.content != other.content: return True if type(self.content) is numpy.matrix: if not numpy.array_equal(self.content, other.content): return True if self.shape != other.shape: return True if self.root_index != other.root_index: return True return False def __add__(self, other): """Addiion with several types""" if type(other) is HMat: return self._add_hmat(other) elif type(other) is RMat: return self._add_rmat(other) elif type(other) is numpy.matrix: return self._add_matrix(other) else: raise NotImplementedError('unsupported operand type(s) for +: {0} and {1}'.format(type(self), type(other))) def _add_hmat(self, other): """ :param other: HMat to add :type other: HMat :return: sum :rtype: HMat """ # check inputs if self.shape != other.shape: raise ValueError('shapes {0.shape} and {1.shape} not aligned: ' '{0.shape[1]} (dim 1) != {1.shape[0]} (dim 0)'.format(self, other)) if self.root_index != other.root_index: raise ValueError('root indices {0.root_index} and {1.root_index} not the same'.format(self, other)) if (self.content is None and other.blocks is None) or (self.blocks is None and other.content is None): raise ValueError('block structure is not the same for {0} and {1}'.format(self, other)) if self.content is not None: # both have content return HMat(content=self.content + other.content, shape=self.shape, root_index=self.root_index) if self.blocks is not None: # both have children blocks = len(self.blocks) return HMat(blocks=[self.blocks[i] + other.blocks[i] for i in xrange(blocks)], shape=self.shape, root_index=self.root_index) return None def _add_rmat(self, other): pass def _add_matrix(self, other): pass def __mul__(self, other): if type(other) is numpy.ndarray: return self._mul_with_vector(other) elif type(other) is numpy.matrix: return self._mul_with_matrix(other) elif type(other) is int: return self._mul_with_int(other) else: raise NotImplementedError('unsupported operand type(s) for *: {0} and {1}'.format(type(self), type(other))) def _mul_with_vector(self, other): """Multiply with a vector :param other: vector :type other: numpy.array :return: result vector :rtype: numpy.array """ if self.content is not None: # We have content. Multiply and return return self.content * other else: if self.shape[1] != other.shape[0]: raise ValueError('shapes {0.shape} and {1.shape} not aligned: ' '{0.shape[1]} (dim 1) != {1.shape[0]} (dim 0)'.format(self, other)) x_start, y_start = self.root_index res_length = self.shape[0] res = numpy.zeros((res_length, 1)) for block in self.blocks: x_current, y_current = block.root_index x_length, y_length = block.shape in_index_start = x_current - x_start in_index_end = in_index_start + x_length res_index_start = y_current - y_start res_index_end = res_index_start + y_length res[res_index_start: res_index_end] += block * other[in_index_start: in_index_end] return res def _mul_with_matrix(self, other): """Multiply with a matrix :param other: matrix :type other: numpy.matrix :return: result matrix :rtype: numpy.matrix """ if self.content is not None: return self.content * other else: if self.shape[1] != other.shape[0]: raise ValueError('shapes {0.shape} and {1.shape} not aligned: ' '{0.shape[1]} (dim 1) != {1.shape[0]} (dim 0)'.format(self, other)) res_shape = (self.shape[0], other.shape[1]) res = numpy.zeros(res_shape) row_base, col_base = self.root_index for block in self.blocks: row_count, col_count = block.shape row_current, col_current = block.root_index row_start = row_current - row_base row_end = row_start + row_count res[row_start:row_end, :] += block * other[row_start:row_end, :] return res def _mul_with_rmat(self, other): """Multiplication with an RMat :param other: rmatrix.RMat to multiply :type other: RMat :return: """ out = HMat(shape=self.shape, root_index=self.root_index) if type(self.content) == RMat: out.content = self.content * other elif self.content is not None: return other.__rmul__(self.content) else: raise TypeError("Encountered HMat * RMat! What should I do?!") def _mul_with_int(self, other): """Multiplication with integer""" out = HMat(shape=self.shape, root_index=self.root_index) if self.content is not None: out.content = self.content * other return out else: out.blocks = [block * other for block in self.blocks] return out
[docs] def to_matrix(self): """Full matrix representation :return: full matrix :rtype: numpy.matrix """ if self.blocks: # The matrix has children so fill recursive out_mat = numpy.empty(self.shape) for block in self.blocks: # determine the position of the current block vertical_start = block.root_index[0] vertical_end = vertical_start + block.shape[0] horizontal_start = block.root_index[1] horizontal_end = horizontal_start + block.shape[1] # fill the block with recursive call out_mat[vertical_start:vertical_end, horizontal_start:horizontal_end] = block.to_matrix() return out_mat elif type(self.content) == RMat: # We have an RMat in content, so return its full representation return self.content.to_matrix() else: # We have regular content, so we return it return self.content
[docs]def build_hmatrix(block_cluster_tree=None, generate_rmat_function=None, generate_full_matrix_function=None): """Factory to build an HMat instance :param block_cluster_tree: block cluster tree giving the structure :type block_cluster_tree: BlockClusterTree :param generate_rmat_function: function taking an admissible block cluster tree and returning a rank-k matrix :param generate_full_matrix_function: function taking an inadmissible block cluster tree and returning a numpy.matrix :return: hmatrix :rtype: RMat :raises: ValueError if root of BlockClusterTree is admissible """ if block_cluster_tree.admissible: raise ValueError("Root of the block cluster tree is admissible, can't generate HMat from that.") root = HMat(blocks=[], shape=block_cluster_tree.shape(), root_index=(0, 0)) recursion_build_hmatrix(root, block_cluster_tree, generate_rmat_function, generate_full_matrix_function) return root
[docs]def recursion_build_hmatrix(current_hmat, block_cluster_tree, generate_rmat, generate_full_mat): """Recursion to :func:`build_hmatrix` """ if block_cluster_tree.admissible: # admissible level found, so fill content with rank-k matrix and stop current_hmat.content = generate_rmat(block_cluster_tree) elif not block_cluster_tree.sons: # no sons and not admissible, so fill content with full matrix and stop current_hmat.content = generate_full_mat(block_cluster_tree) else: # recursion: generate new hmatrix for every son in block cluster tree for son in block_cluster_tree.sons: new_hmat = HMat(blocks=[], shape=son.shape(), root_index=son.plot_info) current_hmat.blocks.append(new_hmat) recursion_build_hmatrix(new_hmat, son, generate_rmat, generate_full_mat)