Source code for cellsystem.logging.geometric

"""
Geometric Logging
=================

This module defines functionality for following the geometric
evolution of the cell blob through time.

The classes GeometricLog and WorldLines are defined here.
"""

from collections import defaultdict

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from .core import WeakLog


[docs]class GeometricLog(WeakLog): """Registers the geometric positions of the cells in time. One may view the state of a GeometricLog object 'glog' by 'glog.worldlines().show()' """ def __init__(self, *args, **kwargs): """Maintains a changes record.""" # Init as the superclass (Weak) super().__init__() self.initial_state = dict() self.changes = [] # --- def _unpack_info(self, cell): 'Unpack cell information.' index = cell.index site = cell.coordinates return index, site # --- def _register(self, cell): "Add the cell's current information to the changes." index, site = self._unpack_info(cell) self.changes.append( (index, site) ) # --- def _which_action(self, change): "Decode the event into a concrete action." # There are 3 possible actions: # division: ( father, ((d1,site), (d2,site)) ) # death: (cell, None) # and migration: (cell, site) cell, other = change if other is None: return "death" else: # Check division try: (d1, site), (d2, site) = other return "division" # Check migration except TypeError: return "migration" # ---
[docs] def iter_changes(self): "Iterate through every action" for change in self.changes: # Decode change change_type = self._which_action(change) yield change_type, change
# ---
[docs] def iter_states(self): "Generate the intermediate states." current_state = self.initial_state.copy() for change_type, change in self.iter_changes(): # Perform change if change_type == "migration": cell, site = change current_state[cell] = site elif change_type == "division": # Unpack cell, daughters = change del current_state[cell] (d1, s1),(d2, s2) = daughters current_state[d1] = s1 current_state[d2] = s2 elif change_type == "death": cell, _ = change del current_state[cell] yield current_state.copy()
# ---
[docs] def worldlines(self, prune_death=False): "Get the geometric evolution of individual cells in space and time." return WorldLines.from_log(self, prune_death)
# ---
[docs] def log_newcell(self, cell): self._register(cell)
# ---
[docs] def preparefor_division(self, cell): # The index of the dividing cell self.tmp = cell.index
# ---
[docs] def log_division(self, daughters): # Register the disappeareance of the father father = self.tmp # Register the sites of the daughters d1, d2 = daughters d1_info = self._unpack_info(d1) d2_info = self._unpack_info(d2) self.changes.append( (father, (d1_info, d2_info)) )
# ---
[docs] def log_migration(self, cell): self._register(cell)
# ---
[docs] def log_death(self, cell): "Register the disappeareance of the cell." self.changes.append( (cell.index, None) )
# ---
[docs]class WorldLines: """A class that represents the worldlines of a set of cells. A worldline is the place in time and space that a cell occupies throughout it's existence. In other words, it stores for each cell 'c', a list of space-time points in which the cell has been (a spacetime point is a pair t, x where t is time and x is the geometric position of the cell at that time). A world line may store a state similar to: # wl is an initialized Worldline object >>> wl.worldlines {0: [(0, 50, 50), (1, 51, 51), (2, 52, 50)], 1: [(3, 51, 50), (2, 52, 50), (4, 51, 50), (5, 51, 50), (6, 51, 50)], 2: [(3, 52, 49), (2, 52, 50), (4, 52, 49), (5, 52, 48), (6, 52, 48)] } """ def __init__(self, initial_state=None, time=0): "Initialize from a state." if initial_state is None: initial_state = dict() self.worldlines = defaultdict(list) # Assemble initial state for cell,site in initial_state.items(): # A point in spacetime event = self._event(time, site) self.worldlines[cell].append(event) # --- @staticmethod def _event(time, site): "Assemble a point in spacetime." return tuple( [time]+list(site) ) # ---
[docs] @classmethod def from_log(cls, geometric_log, prune_death=False): "Initialize from a geometric log." changes = geometric_log.iter_changes() states = geometric_log.iter_states() # Intialize timelines object current_state = next(states) t = 0 worldlines = cls(current_state, t) # Assemble timelines for i,(state,change) in enumerate(zip(states, changes)): worldlines.update(state, transition=change, time=i+1, prune_death=prune_death) return worldlines
# ---
[docs] def update(self, state, transition, time, prune_death=False): "Add the event described by the transition, time and final state." change_type, change = transition if prune_death and (change_type == "death"): # Cell is dead, remove it's timeline cell, _ = change self.remove(cell) elif change_type == "division": cell, daughters = change (d1, s1),(d2, s2) = daughters # Find the time and site of the division division = self.last_state_of(cell) # The daughters' timeline starts at the # site of the division and to their current site self.add_event(d1, division)\ .add_event(d2, division) for cell, site in state.items(): self.add_event(cell, self._event(time, site)) return self
# ---
[docs] def remove(self, cell): "Remove the given cell's timeline." del self.worldlines[cell]
# ---
[docs] def last_state_of(self, cell): "Return the last recorded event of the given cell." return self.worldlines[cell][-1]
# ---
[docs] def add_event(self, cell, event): "Add an event (a spacetime coordinate) for the cell." self.worldlines[cell].append(event) return self
# --- def __getitem__(self, cell): "Get the timeline of a cell." return self.worldlines[cell] # --- def __iter__(self): "Iterate through the cell worldlines." yield from self.worldlines.items() # ---
[docs] def show(self, div_marker='o', end_marker='', savefig=None): "Render the 3D worldlines as a plot." fig = plt.figure() ax = fig.gca(projection='3d') for cell,timeline in self: t, x, y = zip(*timeline) ax.plot(t, x, y) # Mark the beginning of a worldline ax.scatter(t[0], x[0], y[0], marker=div_marker) # Mark the end of a worldline ax.scatter(t[-1], x[-1], y[-1], marker=end_marker) ax.set_xlabel('t') ax.set_ylabel('x') ax.set_zlabel('y') # Save to a file if savefig: return plt.savefig(savefig) plt.show()
# --- # --- WorldLines