Source code for HierMat.cluster_tree
"""cluster_tree.py: :class:`ClusterTree`, :func:`build_cluster_tree`, :func:`recursion_build_cluster_tree`
"""
[docs]class ClusterTree(object):
"""Tree structure built according to the :class:`Splitable` in use
Splits are done until max_leaf_size is reached
"""
def __init__(self, content, sons=None, max_leaf_size=1, level=0):
""""""
self.level = level
self.content = content
self.sons = sons if sons else []
self.max_leaf_size = max_leaf_size
def __repr__(self):
optional_string = " with children {0}".format(self.sons) if self.sons else " without children"
return "<ClusterTree at level {0}{1}>".format(str(self.level), optional_string)
def __len__(self):
return len(self.content)
def __str__(self):
"""give str representation of self."""
cont_str = ",".join([str(p) for p in self.content])
out_str = "ClusterTree at level {0} with content:\n{1}".format(self.level, cont_str)
return out_str
def _plot_str(self):
"""string for plots"""
cont_str = ''
for p in self.content:
cont_str += '['
cont_str += ",".join(["{0:.2f}".format(i) for i in p])
cont_str += "] "
cont_str.rstrip()
return cont_str
def __eq__(self, other):
"""Test for equality
:param other: other cluster tree
:type other: ClusterTree
"""
return (self.level == other.level and self.content == other.content
and self.sons == other.sons and self.max_leaf_size == other.max_leaf_size)
def __ne__(self, other):
"""Test for inequality
:param other: other cluster tree
:type other: ClusterTree
"""
return not self == other
def __getitem__(self, item):
return self.content[item]
[docs] def get_index(self, item):
"""Get index from content
:param item: index to get
:type item: int
:return: index
:rtype: int
"""
return self.content.get_index(item)
[docs] def get_grid_item(self, item):
"""Get grid item from content
:param item: index of item to get
:type item: int
"""
return self.content.get_grid_item(item)
[docs] def get_grid_item_support_by_index(self, item):
"""Return supports of i-th item from grid
:param item: index
:type item: int
"""
return self.content.get_grid_item_support_by_index(item)
[docs] def get_grid_item_support(self, item):
"""Return supports ofitem from grid
:param item: point
:type item: tuple(float)
"""
return self.content.get_grid_item_support(item)
[docs] def get_patch_coordinates(self):
"""Return min and max out of indices
:return: min and max
:rtype: tuple(int, int)
"""
return self.content.get_patch_coordinates()
[docs] def to_list(self):
"""Give list representation for export
Return a list containing the object and a list with its sons
.. hint::
**recursive function**
"""
if self.sons:
return [self, [son.to_list() for son in self.sons]]
else:
return [self, []]
[docs] def to_xml(self):
"""Return a string for xml representation"""
out_list = self.to_list()
return self._to_xml(out_list)
@staticmethod
def _to_xml(lst, out_string=''):
if len(lst[1]):
value_string = str(lst[0])
display_string = str(len(lst[1]))
else:
value_string = str(lst[0])
display_string = str(lst[0])
out_string += '<node value="{0}">{1}\n'.format(value_string, display_string)
if len(lst) > 1 and type(lst[1]) is list:
for item in lst[1]:
out_string = ClusterTree._to_xml(item, out_string)
out_string += "</node>\n"
return out_string
[docs] def to_dot(self):
"""Return a string for .dot representation"""
out_list = self.to_list()
return self._to_dot(out_list)
@staticmethod
def _to_dot(lst, out_string=''):
value_string = str(lst[0])
label_string = len(lst[0])
for item in lst[1]:
item_string = str(item[0])
out_string += '''"{0}" -- "{1}";
"{0}"[label="{2}",color="#cccccc",style="filled",shape="box"];\n'''.format(
value_string, item_string, label_string)
out_string = ClusterTree._to_dot(item, out_string)
if not lst[1]:
try:
content_list = ['{:.2f}'.format(p) for p in lst[0].content]
except ValueError:
content_list = [str(['{:.2f}'.format(p) for p in item]) for item in lst[0].content]
label_string = ', '.join(content_list)
out_string += '"{0}"[label="{1}",color="#cccccc",style="filled",shape="box"];\n'.format(value_string,
label_string)
return out_string
[docs] def depth(self, root_level=None):
"""Get depth of the tree
:param root_level: internal use.
:type root_level: int
:return: depth
:rtype: int
.. hint::
**recursive function**
"""
if root_level is None:
root_level = self.level
if not self.sons:
return self.level - root_level
else:
return max([son.depth(root_level) for son in self.sons])
[docs] def diameter(self):
"""Return the diameter of content
:return: diameter
:rtype: float
"""
return self.content.diameter()
[docs] def distance(self, other):
"""Return distance to other
:param other: other cluster tree
:type other: ClusterTree
:return: distance
:rtype: float
"""
return self.content.distance(other.content)
[docs]def build_cluster_tree(splitable, max_leaf_size=1, start_level=0):
"""Factory for building a cluster tree
:param splitable: strategy that decides the structure
:type splitable: Splitable
:param max_leaf_size: cluster size at which recursion stops
:type max_leaf_size: int
:param start_level: counter to identify levels
:type start_level: int
:return: cluster tree
:rtype: ClusterTree
"""
root = ClusterTree(splitable, max_leaf_size=max_leaf_size, level=start_level)
recursion_build_cluster_tree(root)
return root
[docs]def recursion_build_cluster_tree(current_tree):
"""Recursion to :func:`build_cluster_tree`
"""
if len(current_tree.content) > current_tree.max_leaf_size:
splits = current_tree.content.split()
for split in splits:
new_tree = ClusterTree(content=split,
max_leaf_size=current_tree.max_leaf_size,
level=current_tree.level + 1)
current_tree.sons.append(new_tree)
recursion_build_cluster_tree(new_tree)
[docs]def admissible(left_clustertree, right_clustertree):
"""Default admissible condition for BlockClusterTree
True if the smaller diameter of the input is smaller or equal to the distance between the two ClusterTrees
:param left_clustertree: "Left-side" ClusterTree
:param right_clustertree: "Right-side" ClusterTree
:type left_clustertree: ClusterTree
:type right_clustertree: ClusterTree
:return: admissible
:rtype: bool
"""
diam_min = min(left_clustertree.diameter(), right_clustertree.diameter())
distance = left_clustertree.distance(right_clustertree)
return diam_min <= distance