Source code for semilattices._sortedcoordinatesemilatticebase

#
# This file is part of semilattices.
#
# semilattices is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# semilattices is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with semilattices.  If not, see <http://www.gnu.org/licenses/>.
#
# semilattices
# Copyright (C) 2018-2019 
# Massachusetts Institute of Technology                    The University of Texas at Austin
# Uncertainty Quantification group                and      Center for Computational Geosciences and Optimization
# Department of Aeronautics and Astronautics               The Oden Institute for Computational Engineering and Sciences
# 
# Authors: Daniele Bigoni and Joshua Chen
# Contact: dabi@mit.edu / joshuawchen@utexas.edu
# Website: 
# Support:
#

import copy
from collections import namedtuple

from semilattices._coordinatesemilatticebase import \
    CoordinateSemilattice
from semilattices._datastructures import \
    MixedSortedContainer
from semilattices._exceptions import *
from semilattices._iterables import \
    BreadthFirstCoupledIntersectionSemilatticeIterable
from semilattices._semilatticebase import \
    Semilattice
from semilattices._vertices import \
    LabeledCoordinateVertex

__all__ = [
    'SortedCoordinateSemilattice',
]
    
[docs]class SortedCoordinateSemilattice( CoordinateSemilattice ): r""" A SortedCoordinateSemilattice is a semilattice with Labeled vertices. The elements of the SortedCoordinateSemilattice are sorted by the labels in a heap. One can add and remove labeled vertices in the lattice, and the ordering is maintained by the heap. SortedCoordinateSemilattice relies on the LabeledVertex and MixedSortedContainer data structures. """ ################### # CLASS VARIABLES # ################### _DefaultVertexConstructor = LabeledCoordinateVertex _DefaultVertexSetConstructor = MixedSortedContainer _DefaultVertexSetConstructorKwargs = { 'label_keys': ('blank_label', ), 'default_label_key' : 'blank_label' } _l1_vertices_partition_flag = True _l1_frontier_partition_flag = True _l1_childless_partition_flag = True Properties = namedtuple( 'Properties', CoordinateSemilattice.Properties._fields + \ ('label_keys', 'default_label_key', 'data_keys') ) ########################### # INITIALIZATION ROUTINES # ########################### def __init__(self, *args, **kwargs): r""" Optional Args: semilattice (Semilattice): a semilattice to cast from Keyword Args: dims (int): semilattice dimension (maximum number of children per vertex) VertexConstructor (class): a subclass of :class:`LabeledCoordinateVertex` (default: :class:`LabeledCoordinateVertex`) VertexSetConstructor (class): a container class defining the data structure containing vertices must be a subclass of :class:`MixedSortedContainer` (default: :class:`MixedSortedContainer`) l1_vertices_partition (bool): whether to keep track of the :math:`\ell^1` partition of vertices (default: ``True``) l1_frontier_partition (bool): whether to keep track of the :math:`\ell^1` partition of the frontier (default: ``True``) label_keys (iterable of strings): the label keys that will be used to label vertices default_label_key (string): label to be used as default sorting label. If not provided, the first key of ``label_keys`` will be used for sorting. data_keys (iterable of strings): the keys for the data contained in each vertex. """ super().__init__(*args, **kwargs) def _prepare_properties_from_object(self, obj, properties=None, **kwargs): #inherit other properties from super class properties = super()._prepare_properties_from_object(obj, properties=properties, **kwargs) # Either use properties of object passed into the initializer # or get default and warn if the user tries to use keyword arguments # to set these attributes for attr in ['label_keys','default_label_key','data_keys']: try: properties[attr] = getattr(obj.properties, attr) passed_int_attr = kwargs.get(attr) if passed_int_attr is not None: if issubclass(type(obj), type(self)): self.logger.warning( 'The provided kwarg `'+attr+'` will not be used, since an object ' \ + 'was passed into the initializer') else: #Using object's kwargs self.logger.warning( 'The provided kwarg `'+attr+'` will be used/override the object`s version') properties[attr] = passed_int_attr except AttributeError: properties[attr] = getattr(self, '_' + attr) # raise ArgumentsException('The obj should have the property `'+ attr + '`') return properties def _prepare_properties(self, properties=None, **kwargs): #inherit other properties from super class properties = super()._prepare_properties(properties=properties, **kwargs) if kwargs.get('label_keys'): label_keys = tuple( kwargs.get('label_keys') ) else: label_keys = tuple( self._DefaultVertexSetConstructorKwargs['label_keys'] ) default_label_key = kwargs.get('default_label_key') if default_label_key is None: default_label_key = label_keys[0] data_keys = kwargs.get('data_keys') data_keys = tuple(data_keys) if data_keys is not None else () properties.update({ 'label_keys': label_keys, 'default_label_key': default_label_key, 'data_keys': data_keys }) properties['VertexSetConstructorKwargs'] = { 'label_keys': label_keys, 'default_label_key': default_label_key } return properties ############## # PROPERTIES # ############## @staticmethod def __name__(self): return "SortedCoordinateSemilattice" @property def sorted(self): r""" Returns the sorted labeled vertices based on the default label .. warning:: It will iterate ONLY over the vertices that have been assigned a label. A warning will be raised if some of the vertices have not been labeled. """ return self._vertices.sorted @property def sorted_frontier(self): r""" Returns the sorted frontier based on the default label .. warning:: It will iterate ONLY over the vertices that have been assigned a label. A warning will be raised if some of the vertices have not been labeled. """ return self._frontier.sorted @property def default_label_key(self): return self.properties.default_label_key @property def label_keys(self): return self.properties.label_keys @property def data_keys(self): return self.properties.data_keys ################# # SERIALIZATION # ################# def _setstate_inner(self, dd, tmp_vertices): super()._setstate_inner(dd, tmp_vertices) # self._properties = SortedCoordinateSemilattice.Properties( # **self._properties._asdict(), # **{ k: dd['properties'][k] for k in ['label_keys', 'data_keys','default_label_key'] }) ############## # COMPARISON # ############## def _inner_eq(self,other): if not super()._inner_eq(other): return False for nvertices, (v1, v2) in enumerate( BreadthFirstCoupledIntersectionSemilatticeIterable( self, other)): if v1 != v2: return False if nvertices + 1 != len(self) or nvertices + 1 != len(other): return False return True ############## # NEW VERTEX # ############## def _new_vertex_sans_check(self, **kwargs): r""" This function should be called to determine and create the the relationships (edges) between a new vertex and other existing vertices in the semilattice, including `edge' edge with `parent' """ kwargs['default_label_key'] = self.default_label_key return super()._new_vertex_sans_check(**kwargs) ################### # UPDATING VERTEX # ################### def _remove_from_sorted_data_structures(self, vertex, label): self._vertices._label_sorted_lists[label].remove( vertex ) if self._l1_vertices_partition_flag: lvl = self._l1_vertices_partition.get_level(vertex) lvl._label_sorted_lists[label].remove( vertex ) if vertex in self._frontier: self._frontier._label_sorted_lists[label].remove( vertex ) if self._l1_frontier_partition_flag: lvl = self._l1_frontier_partition.get_level(vertex) lvl._label_sorted_lists[label].remove( vertex ) def _add_to_sorted_data_structures(self, vertex, label): self._vertices._label_sorted_lists[label].add( vertex ) if self._l1_vertices_partition_flag: lvl = self._l1_vertices_partition.get_level(vertex) lvl._label_sorted_lists[label].add( vertex ) if vertex in self._frontier: self._frontier._label_sorted_lists[label].add( vertex ) if self._l1_frontier_partition_flag: lvl = self._l1_frontier_partition.get_level(vertex) lvl._label_sorted_lists[label].add( vertex )
[docs] def update_labels(self, vertex, **kwargs): r""" Update label(s) of a vertex Args: vertex (LabeledVertex): vertex to label Keyword args: each keyword arg should be a label of the vertex """ for label, value in kwargs.items(): if label not in self.label_keys: raise Exception ('Invalid key for this sorted coordinate semilattice.' + \ 'The valid keys are' + str(self.label_keys)) # 1) Check whether vertex has already a value for this label if label in vertex.labels: # 2) Remove the vertex from the sorted data structures self._remove_from_sorted_data_structures( vertex, label ) # 3) Update vertex label vertex._update_labels( **{label: value} ) # 4) If value is not None, add the vertex back if value is not None: self._add_to_sorted_data_structures( vertex, label )
[docs] def update_data(self, vertex, **kwargs): r""" Update metadata of a vertex Args: vertex (LabeledVertex): vertex to label Keyword args: each keyword arg should be metadata of the vertex """ for key, value in kwargs.items(): if key not in self.data_keys: raise Exception ('Invalid data key for this sorted coordinate semilattice.' + \ 'The valid data keys are' + str(self.data_keys)) vertex.update_data( **{key: value} )
################# # VISUALIZATION # ################# def _mpl_draw_vertex_color( self, v, opts ): if opts.get('color') == 'label': c = v.labels.get(opts.get('color_label')) if c is None: c = opts.get('color_default', 'k') else: c = super()._mpl_draw_vertex_color(v, opts) return c def _cart_draw_init_opts( self, max_nmax_vtx, opts, nopts, eopts ): super()._cart_draw_init_opts( max_nmax_vtx, opts, nopts, eopts) if opts.get('color') == 'label': vmin = float('inf') vmax = - float('inf') for v in self.sorted: vmin = min(vmin, v.labels.get(opts.get('color_label'))) vmax = max(vmax, v.labels.get(opts.get('color_label'))) if vmin != float('inf'): nopts['vmin'] = vmin nopts['vmax'] = vmax
[docs] def draw( self, ax=None, **kwargs ): r""" Draw the semilattice. For additional keyword arguments see :func:`CoordinateSemialattice.draw`. Args: ax: matplotlib axes Keyword Args: color (bool): if set to ``labels`` colors with respect to the ``label`` of the vertex """ super().draw(ax, **kwargs)