Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | List of all members
CellMLToNektar.pycml.mathml_piecewise Class Reference
Inheritance diagram for CellMLToNektar.pycml.mathml_piecewise:
Inheritance graph
[legend]
Collaboration diagram for CellMLToNektar.pycml.mathml_piecewise:
Collaboration graph
[legend]

Public Member Functions

def __init__
 
def tree_complexity
 
def get_units
 
def classify_variables
 
def evaluate
 
- Public Member Functions inherited from CellMLToNektar.pycml.mathml_constructor
def __init__
 
- Public Member Functions inherited from CellMLToNektar.pycml.mathml
def __init__
 
def __repr__
 
def __deepcopy__
 
def clone_self
 
def get_original_of_clone
 
def get_component
 
def model
 
def eval
 
- Public Member Functions inherited from CellMLToNektar.pycml.element_base
def __init__
 
def __delattr__
 
def __setattr__
 
def rootNode
 
def cmeta_id
 
def xml_remove_child_at
 
def xml_doc
 
def xml_properties
 

Static Public Member Functions

def create_new
 
- Static Public Member Functions inherited from CellMLToNektar.pycml.mathml
def clone
 

Private Member Functions

def _set_in_units
 
def _get_binding_time
 
def _reduce
 

Private Attributes

 _cml_units
 
 _cml_binding_time
 

Additional Inherited Members

- Public Attributes inherited from CellMLToNektar.pycml.element_base
 xml_attributes
 
- Properties inherited from CellMLToNektar.pycml.mathml
 component = property(get_component)
 

Detailed Description

Definition at line 5032 of file pycml.py.

Constructor & Destructor Documentation

def CellMLToNektar.pycml.mathml_piecewise.__init__ (   self)

Definition at line 5033 of file pycml.py.

5034  def __init__(self):
5035  super(mathml_piecewise, self).__init__()
5036  self._cml_units = None
5037  self._cml_binding_time = None

Member Function Documentation

def CellMLToNektar.pycml.mathml_piecewise._get_binding_time (   self)
private
Return the binding time of this expression.

The binding time will be computed recursively and cached.
It will also be made available as an attribute in the XML.

It is computed by taking the least upper bound of the binding
times of (some of) the conditions and cases.

Condition & case binding times are computed in the order given
in the file.  If a condition is static with value False, its
associated case is not considered.  If a condition is static
with value True, subsequent conditions & cases and the
otherwise (if present) will not be considered.

Definition at line 5228 of file pycml.py.

References CellMLToNektar.pycml.cellml_variable._cml_binding_time, CellMLToNektar.pycml.mathml_apply._cml_binding_time, CellMLToNektar.pycml.mathml_piecewise._cml_binding_time, CellMLToNektar.pycml.mathml_constructor._get_element_binding_time(), CellMLToNektar.pycml.child_i(), and CellMLToNektar.pycml.mathml.eval().

Referenced by CellMLToNektar.pycml.mathml_piecewise._reduce().

5229  def _get_binding_time(self):
5230  """Return the binding time of this expression.
5231 
5232  The binding time will be computed recursively and cached.
5233  It will also be made available as an attribute in the XML.
5234 
5235  It is computed by taking the least upper bound of the binding
5236  times of (some of) the conditions and cases.
5237 
5238  Condition & case binding times are computed in the order given
5239  in the file. If a condition is static with value False, its
5240  associated case is not considered. If a condition is static
5241  with value True, subsequent conditions & cases and the
5242  otherwise (if present) will not be considered.
5243  """
5244  if self._cml_binding_time is not None:
5245  return self._cml_binding_time
5246 
5247  # Do we have an annotation?
5248  if hasattr(self, u'binding_time'):
5249  self._cml_binding_time = getattr(BINDING_TIMES,
5250  self.binding_time)
5251  return self._cml_binding_time
5252 
5253  # Compute condition binding times
5254  bts = [BINDING_TIMES.static]
5255  for piece in getattr(self, u'piece', []):
5256  condition = child_i(piece, 2)
5257  bt = self._get_element_binding_time(condition)
5258  if bt is BINDING_TIMES.static:
5259  cond_value = self.eval(condition)
5260  if cond_value is True:
5261  # Compute BT for associated case
5262  bts.append(self._get_element_binding_time(
5263  child_i(piece, 1)))
5264  # Skip remaining conditions & otherwise
5265  break
5266  else:
5267  # Don't need to append extra statics, since bts
5268  # contains at least one member that is static
5269  bts.append(bt)
5270  # Compute BT for associated case
5271  bts.append(self._get_element_binding_time(
5272  child_i(piece, 1)))
5273  else:
5274  # Consider the <otherwise> element
5275  if hasattr(self, u'otherwise'):
5276  bts.append(self._get_element_binding_time(
5277  child_i(self.otherwise, 1)))
5278  # Take least upper bound of appropriate binding times
5279  self._cml_binding_time = max(bts)
5280 
5281  # Annotate the element with the binding time
5282  self.xml_set_attribute((u'pe:binding_time', NSS[u'pe']),
5283  unicode(self._cml_binding_time))
5284  return self._cml_binding_time
def CellMLToNektar.pycml.mathml_piecewise._reduce (   self,
  check_operator = True 
)
private
Reduce this expression by evaluating its static parts.

Even in a dynamic conditional, where a condition is static and
evaluates to False, the associated case is discarded.

Definition at line 5285 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._eval_self(), CellMLToNektar.pycml.cellml_variable._get_binding_time(), CellMLToNektar.pycml.mathml_cn._get_binding_time(), CellMLToNektar.pycml.mathml_ci._get_binding_time(), CellMLToNektar.pycml.mathml_apply._get_binding_time(), CellMLToNektar.pycml.mathml_piecewise._get_binding_time(), CellMLToNektar.pycml.mathml_constructor._get_element_binding_time(), CellMLToNektar.pycml.mathml_constructor._reduce_elt(), CellMLToNektar.pycml.mathml_constructor._update_usage_counts(), CellMLToNektar.pycml.child_i(), and CellMLToNektar.pycml.mathml.eval().

5286  def _reduce(self, check_operator=True):
5287  """Reduce this expression by evaluating its static parts.
5288 
5289  Even in a dynamic conditional, where a condition is static and
5290  evaluates to False, the associated case is discarded.
5291  """
5292  # Element to replace this <piecewise> with, if any
5293  new_elt = None
5294  if self._get_binding_time() == BINDING_TIMES.static:
5295  # Evaluate self and replace by a <cn>, <true> or <false>
5296  new_elt = self._eval_self()
5297  elif self._get_binding_time() == BINDING_TIMES.dynamic:
5298  # Go through pieces and reduce where appropriate
5299  deletable_pieces = []
5300  found_dynamic_piece = False
5301  for piece in getattr(self, u'piece', []):
5302  condition = child_i(piece, 2)
5303  bt = self._get_element_binding_time(condition)
5304  if bt is BINDING_TIMES.static:
5305  cond_value = self.eval(condition)
5306  if cond_value is True:
5307  if not found_dynamic_piece:
5308  # Replace the entire piecewise element by our case
5309  # We don't replace if a previous piece had a
5310  # dynamic condition, since this would change
5311  # execution order, which could alter the semantics.
5312  new_elt = child_i(piece, 1)
5313  break
5314  else:
5315  # Discard this condition & case
5316  deletable_pieces.append(piece)
5317  elif bt is BINDING_TIMES.dynamic:
5318  found_dynamic_piece = True
5319  # Reduce the condition & case
5320  self._reduce_elt(condition)
5321  self._reduce_elt(child_i(piece, 1))
5322  else:
5323  # Didn't replace entire conditional
5324  # Remove pieces with False conditions
5325  for piece in deletable_pieces:
5326  self._update_usage_counts(piece, remove=True)
5327  self.xml_remove_child(piece)
5328  # Consider the <otherwise> element
5329  if hasattr(self, u'otherwise'):
5330  if not found_dynamic_piece:
5331  # All the <piece> elements were removed, so replace
5332  # the entire conditional by this <otherwise>
5333  new_elt = child_i(self.otherwise, 1)
5334  else:
5335  # Just reduce the <otherwise>
5336  self._reduce_elt(child_i(self.otherwise, 1))
5337  # Replace this element, if required
5338  if new_elt is not None:
5339  # Update usage counts for removed expressions
5340  for piece in getattr(self, u'piece', []):
5341  if not new_elt is child_i(piece, 1):
5342  self._update_usage_counts(piece, remove=True)
5343  else:
5344  # Condition is being removed
5345  self._update_usage_counts(child_i(piece, 2), remove=True)
5346  piece.xml_remove_child(child_i(piece, 2))
5347  piece.xml_remove_child(new_elt)
5348  if hasattr(self, u'otherwise') and \
5349  not new_elt is child_i(self.otherwise, 1):
5350  self._update_usage_counts(child_i(self.otherwise, 1),
5351  remove=True)
5352  # Do the replace
5353  self._xfer_complexity(new_elt)
5354  self.replace_child(self, new_elt, self.xml_parent)
5355  # May need to reduce our replacement
5356  self._reduce_elt(new_elt)
def CellMLToNektar.pycml.mathml_piecewise._set_in_units (   self,
  units,
  no_act = False 
)
private
Set the units this expression should be given in.

This is done recursively by setting the units for each option.

We also set the units on each condition to be boolean, since
subexpressions of the conditions may need units conversions added.

Definition at line 5106 of file pycml.py.

References CellMLToNektar.pycml.cellml_model._cml_units, CellMLToNektar.pycml.cellml_component._cml_units, CellMLToNektar.pycml.mathml_units_mixin_tokens._cml_units, CellMLToNektar.pycml.mathml_units_mixin_container._cml_units, CellMLToNektar.pycml.mathml_cn._cml_units, CellMLToNektar.pycml.mathml_ci._cml_units, CellMLToNektar.pycml.mathml_apply._cml_units, CellMLToNektar.pycml.mathml_piecewise._cml_units, CellMLToNektar.pycml.mathml_units_mixin._set_element_in_units(), and CellMLToNektar.pycml.child_i().

5107  def _set_in_units(self, units, no_act=False):
5108  """Set the units this expression should be given in.
5109 
5110  This is done recursively by setting the units for each option.
5111 
5112  We also set the units on each condition to be boolean, since
5113  subexpressions of the conditions may need units conversions added.
5114  """
5115  # First, record our units
5116  if not no_act:
5117  self._cml_units = units
5118  # Now process our children
5119  boolean = self.model.get_units_by_name(u'cellml:boolean')
5120  for piece in getattr(self, u'piece', []):
5121  self._set_element_in_units(child_i(piece, 1), units, no_act)
5122  self._set_element_in_units(child_i(piece, 2), boolean, no_act)
5123  if hasattr(self, u'otherwise'):
5124  self._set_element_in_units(child_i(self.otherwise, 1), units, no_act)
def CellMLToNektar.pycml.mathml_piecewise.classify_variables (   self,
  dependencies_only = False,
  needs_special_treatment = lambda n: None 
)
Classify variables in this expression according to how they are used.

In the process, compute and return a set of variables on which
this expression depends.

If dependencies_only then the variable classification will not be
done, only dependencies will be analysed.  This is useful for doing
a 'light' re-analysis if the dependency set has been reduced; if the
set has increased then the topological sort of equations may need to
be redone.

The function needs_special_treatment may be supplied to override the
default recursion into sub-trees.  It takes a single sub-tree as
argument, and should either return the dependency set for that
sub-tree, or None to use the default recursion.  This is used when
re-analysing dependencies after applying lookup tables, since table
lookups only depend on the keying variable.

Definition at line 5177 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_piecewise.get_units().

5178  needs_special_treatment=lambda n: None):
5179  """Classify variables in this expression according to how they are used.
5180 
5181  In the process, compute and return a set of variables on which
5182  this expression depends.
5183 
5184  If dependencies_only then the variable classification will not be
5185  done, only dependencies will be analysed. This is useful for doing
5186  a 'light' re-analysis if the dependency set has been reduced; if the
5187  set has increased then the topological sort of equations may need to
5188  be redone.
5189 
5190  The function needs_special_treatment may be supplied to override the
5191  default recursion into sub-trees. It takes a single sub-tree as
5192  argument, and should either return the dependency set for that
5193  sub-tree, or None to use the default recursion. This is used when
5194  re-analysing dependencies after applying lookup tables, since table
5195  lookups only depend on the keying variable.
5196  """
5197  dependencies = set()
5198  pieces = list(getattr(self, u'piece', []))
5199  if hasattr(self, u'otherwise'):
5200  pieces.append(self.otherwise)
5201  for piece in pieces:
5202  dependencies.update(self.classify_child_variables(piece, dependencies_only=dependencies_only,
5203  needs_special_treatment=needs_special_treatment))
5204  return dependencies
def CellMLToNektar.pycml.mathml_piecewise.create_new (   elt,
  pieces,
  otherwise = None 
)
static
Create a new piecewise element.

elt is any element in the current document.

pieces is a list of pairs of expressions: (case, condition).

otherwise, if given, is the default case.

Definition at line 5358 of file pycml.py.

References CellMLToNektar.pycml.check_append_safety().

5359  def create_new(elt, pieces, otherwise=None):
5360  """Create a new piecewise element.
5361 
5362  elt is any element in the current document.
5363 
5364  pieces is a list of pairs of expressions: (case, condition).
5365 
5366  otherwise, if given, is the default case.
5367  """
5368  piecewise = elt.xml_create_element(u'piecewise', NSS[u'm'])
5369  for piece in pieces:
5370  case, cond = piece
5371  check_append_safety(case)
5372  check_append_safety(cond)
5373  piece_elt = elt.xml_create_element(u'piece', NSS[u'm'])
5374  piece_elt.xml_append(case)
5375  piece_elt.xml_append(cond)
5376  piecewise.xml_append(piece_elt)
5377  if otherwise:
5378  check_append_safety(otherwise)
5379  otherwise_elt = elt.xml_create_element(u'otherwise', NSS[u'm'])
5380  otherwise_elt.xml_append(otherwise)
5381  piecewise.xml_append(otherwise_elt)
5382  return piecewise
5383 
def check_append_safety
Definition: pycml.py:197
def CellMLToNektar.pycml.mathml_piecewise.evaluate (   self)
Evaluate this piecewise expression.

Tests choices in the order they occur in the file.
Only evaluates a choice if its condition evaluates to True.

Definition at line 5205 of file pycml.py.

References CellMLToNektar.pycml._child1(), CellMLToNektar.pycml.child_i(), and CellMLToNektar.pycml.mathml.eval().

Referenced by CellMLToNektar.pycml.mathml_constructor._eval_self().

5206  def evaluate(self):
5207  """Evaluate this piecewise expression.
5208 
5209  Tests choices in the order they occur in the file.
5210  Only evaluates a choice if its condition evaluates to True.
5211  """
5212  for piece in getattr(self, u'piece', []):
5213  condition = child_i(piece, 2)
5214  cond_value = self.eval(condition)
5215  if cond_value is True:
5216  # This is the option to take
5217  value = self.eval(child_i(piece, 1))
5218  break
5219  else:
5220  # Evaluate the <otherwise>
5221  if hasattr(self, u'otherwise'):
5222  value = self.eval(_child1(self.otherwise))
5223  else:
5224  raise EvaluationError(u' '.join([
5225  "A piecewise element where the pieces aren't mutually",
5226  "exhaustive requires an otherwise element."]))
5227  return value
def CellMLToNektar.pycml.mathml_piecewise.get_units (   self)
Recursively check this expression for dimensional consistency.

The first child elements of each <piece> and <otherwise> element
should have dimensionally equivalent units (the resulting <units>
element will be dimensionally equivalent to these).  The second child
elements of each <piece> should have units of cellml:boolean.

If consistent, returns a <units> element for the whole expression.
Throws a UnitsError if the units are inconsistent.

Definition at line 5125 of file pycml.py.

References CellMLToNektar.pycml.cellml_model._cml_units, CellMLToNektar.pycml.cellml_component._cml_units, CellMLToNektar.pycml.mathml_units_mixin_tokens._cml_units, CellMLToNektar.pycml.mathml_units_mixin_container._cml_units, CellMLToNektar.pycml.mathml_cn._cml_units, CellMLToNektar.pycml.mathml_ci._cml_units, CellMLToNektar.pycml.mathml_apply._cml_units, CellMLToNektar.pycml.mathml_piecewise._cml_units, CellMLToNektar.pycml.mathml_constructor._get_element_units(), CellMLToNektar.pycml.child_i(), CellMLToNektar.pycml.mathml_piecewise.classify_variables(), and CellMLToNektar.pycml.copy().

Referenced by CellMLToNektar.pycml.mathml._ensure_units_exist(), and CellMLToNektar.pycml.mathml_units_mixin_tokens._set_in_units().

5126  def get_units(self):
5127  """Recursively check this expression for dimensional consistency.
5128 
5129  The first child elements of each <piece> and <otherwise> element
5130  should have dimensionally equivalent units (the resulting <units>
5131  element will be dimensionally equivalent to these). The second child
5132  elements of each <piece> should have units of cellml:boolean.
5133 
5134  If consistent, returns a <units> element for the whole expression.
5135  Throws a UnitsError if the units are inconsistent.
5136  """
5137  if self._cml_units:
5138  return self._cml_units
5139  # Check the second child of each <piece> element
5140  boolean = self.model.get_units_by_name(u'cellml:boolean')
5141  for piece in getattr(self, u'piece', []):
5142  cond_elt = child_i(piece, 2)
5143  units = self._get_element_units(cond_elt)
5144  if not boolean in units:
5145  raise UnitsError(self, u' '.join([
5146  u'The second child element of a <piece> element must have'
5147  u'units of cellml:boolean, not',units.description()]))
5148  # Compare the first child element of each <piece> and the <otherwise>,
5149  # if present.
5150  our_units = None
5151  if hasattr(self, u'otherwise'):
5152  value_elt = child_i(self.otherwise, 1)
5153  our_units = self._get_element_units(value_elt).copy()
5154  for piece in getattr(self, u'piece', []):
5155  value_elt = child_i(piece, 1)
5156  if our_units is None:
5157  our_units = self._get_element_units(value_elt).copy()
5158  else:
5159  units = self._get_element_units(value_elt)
5160  if not our_units.dimensionally_equivalent(units):
5161  raise UnitsError(self, u' '.join([
5162  u'The first child elements of children of a piecewise',
5163  u'element must have dimensionally equivalent units;',
5164  units.description(),'and',our_units.description(),
5165  u'differ']))
5166  our_units.update(units)
5167  # Check that we have some units for this element
5168  if our_units is None:
5169  raise UnitsError(self, u' '.join([
5170  u'A piecewise element must have at least one piece or',
5171  u'otherwise child in order to have defined units.']))
5172  # Cache & return units
5173  self._cml_units = our_units
5174  our_units.set_expression(self)
5175  return self._cml_units
def CellMLToNektar.pycml.mathml_piecewise.tree_complexity (   self,
  kw 
)
Calculate a rough estimate of the computation time for
evaluating this <piecewise> element.

The real evaluation time will generally depend on run time
data, which makes things tricky.  Here we estimate by taking
the sum of the complexities of the conditions and the maximum
of the complexity of the cases, in order to obtain an upper
bound.

If lookup_tables is True, then assume we're using lookup tables
where possible.
If algebraic is True, the complexity is calculated as a dictionary,
mapping node types to the number of occurences of that type.

If self.rootNode.num_lookup_tables exists, this method will
update the count of lookup tables based on this expression,
unless the argument 'count_tables' is False or algebraic is True.

Definition at line 5038 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._tree_complexity(), CellMLToNektar.utilities.add_dicts(), CellMLToNektar.pycml.child_i(), CellMLToNektar.utilities.max_i(), and CellMLToNektar.pycml.element_base.rootNode().

5039  def tree_complexity(self, **kw):
5040  """
5041  Calculate a rough estimate of the computation time for
5042  evaluating this <piecewise> element.
5043 
5044  The real evaluation time will generally depend on run time
5045  data, which makes things tricky. Here we estimate by taking
5046  the sum of the complexities of the conditions and the maximum
5047  of the complexity of the cases, in order to obtain an upper
5048  bound.
5049 
5050  If lookup_tables is True, then assume we're using lookup tables
5051  where possible.
5052  If algebraic is True, the complexity is calculated as a dictionary,
5053  mapping node types to the number of occurences of that type.
5054 
5055  If self.rootNode.num_lookup_tables exists, this method will
5056  update the count of lookup tables based on this expression,
5057  unless the argument 'count_tables' is False or algebraic is True.
5058  """
5059  kw['algebraic'] = kw.get('algebraic', False)
5060  alg = kw['algebraic']
5061  if alg: ac, piece_dicts = {}, []
5062  else: ac = 0
5063  piece_acs = []
5064  count_lts = hasattr(self.rootNode, 'num_lookup_tables') and kw.get('count_tables', True) and not alg
5065  if count_lts:
5066  # Alternative method of counting number of lookup tables;
5067  # handles the Zhang model better!
5068  num_lts = self.rootNode.num_lookup_tables
5069  piece_num_lts = []
5070 
5071  for piece in getattr(self, u'piece', []):
5072  test_ac = self._tree_complexity(child_i(piece, 2), **kw)
5073  if alg: add_dicts(ac, test_ac)
5074  else: ac += test_ac
5075  if count_lts:
5076  nlts = self.rootNode.num_lookup_tables
5077  piece_ac = self._tree_complexity(child_i(piece, 1), **kw)
5078  if alg:
5079  piece_dicts.append(piece_ac)
5080  piece_acs.append(self._tree_complexity(child_i(piece, 1), count_tables=False))
5081  else:
5082  piece_acs.append(piece_ac)
5083  if count_lts:
5084  piece_num_lts.append(self.rootNode.num_lookup_tables - nlts)
5085  if hasattr(self, u'otherwise'):
5086  if count_lts:
5087  nlts = self.rootNode.num_lookup_tables
5088  ow_ac = self._tree_complexity(child_i(self.otherwise, 1), **kw)
5089  if alg:
5090  piece_dicts.append(ow_ac)
5091  piece_acs.append(
5092  self._tree_complexity(child_i(self.otherwise, 1), count_tables=False))
5093  else:
5094  piece_acs.append(ow_ac)
5095  if count_lts:
5096  piece_num_lts.append(self.rootNode.num_lookup_tables - nlts)
5097  max_idx, max_piece_ac = max_i(piece_acs)
5098  if alg:
5099  add_dicts(ac, piece_dicts[max_idx])
5100  else:
5101  ac += max_piece_ac
5102  if count_lts:
5103  self.rootNode.num_lookup_tables -= sum(piece_num_lts)
5104  self.rootNode.num_lookup_tables += piece_num_lts[max_idx]
5105  return ac

Member Data Documentation

CellMLToNektar.pycml.mathml_piecewise._cml_binding_time
private

Definition at line 5036 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_piecewise._get_binding_time().

CellMLToNektar.pycml.mathml_piecewise._cml_units
private

Definition at line 5035 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_piecewise._set_in_units(), and CellMLToNektar.pycml.mathml_piecewise.get_units().