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

Classes

class  OPS
 

Public Member Functions

def __init__
 
def clear_dependency_info
 
def get_dependencies
 
def operator
 
def qualifiers
 
def operands
 
def evaluate
 
def tree_complexity
 
def get_units
 
def check_assigned_var
 
def classify_variables
 
def is_top_level
 
def is_ode
 
def is_assignment
 
def assigned_variable
 
- Public Member Functions inherited from CellMLToNektar.utilities.Colourable
def __init__
 
def set_colour
 
def get_colour
 
def clear_colour
 
- 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
 

Static Public Attributes

tuple QUALIFIERS
 

Private Member Functions

def _is_qualifier
 
def _get_operand_units
 
def _set_in_units
 
def _get_binding_time
 
def _reduce
 

Private Attributes

 _cml_units
 
 _cml_binding_time
 
 _cml_depends_on
 
 _cml_assigns_to
 
 _cml_ode_has_free_var_on_rhs
 

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 4372 of file pycml.py.

Constructor & Destructor Documentation

def CellMLToNektar.pycml.mathml_apply.__init__ (   self)

Definition at line 4393 of file pycml.py.

4394  def __init__(self):
4395  super(mathml_apply, self).__init__()
4396  self._cml_units = None
4397  self.clear_dependency_info()

Member Function Documentation

def CellMLToNektar.pycml.mathml_apply._get_binding_time (   self,
  check_operator = True 
)
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 our operands, unless the operator possesses an
alternative method.

Definition at line 4934 of file pycml.py.

References CellMLToNektar.pycml.cellml_variable._cml_binding_time, CellMLToNektar.pycml.mathml_apply._cml_binding_time, CellMLToNektar.pycml.mathml_constructor._get_element_binding_time(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

Referenced by CellMLToNektar.pycml.mathml_apply._reduce(), and CellMLToNektar.pycml.mathml_piecewise._reduce().

4935  def _get_binding_time(self, check_operator=True):
4936  """Return the binding time of this expression.
4937 
4938  The binding time will be computed recursively and cached.
4939  It will also be made available as an attribute in the XML.
4940 
4941  It is computed by taking the least upper bound of the binding
4942  times of our operands, unless the operator possesses an
4943  alternative method.
4944  """
4945  if self._cml_binding_time is not None:
4946  return self._cml_binding_time
4947 
4948  # Do we have an annotation?
4949  if hasattr(self, u'binding_time'):
4950  self._cml_binding_time = getattr(BINDING_TIMES, self.binding_time)
4951  return self._cml_binding_time
4952 
4953  # Does operator have a specialised method for this?
4954  op = self.operator()
4955  if check_operator and hasattr(op, '_get_binding_time'):
4956  self._cml_binding_time = op._get_binding_time()
4957  else:
4958  # Compute operand binding times
4959  bts = [BINDING_TIMES.static]
4960  for operand in self.operands():
4961  bts.append(self._get_element_binding_time(operand))
4962  # Take l.u.b.
4963  self._cml_binding_time = max(bts)
4964 
4965  # Annotate the element with the binding time
4966  self.xml_set_attribute((u'pe:binding_time', NSS[u'pe']), unicode(self._cml_binding_time))
4967  return self._cml_binding_time
def CellMLToNektar.pycml.mathml_apply._get_operand_units (   self)
private
Return an iterable containing a <units> element for each operand
of this expression.

Definition at line 4439 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._get_element_units(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, and CellMLToNektar.pycml.mathml_apply.operands().

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

4440  def _get_operand_units(self):
4441  """
4442  Return an iterable containing a <units> element for each operand
4443  of this expression.
4444  """
4445  for o in self.operands():
4446  yield self._get_element_units(o)
def CellMLToNektar.pycml.mathml_apply._is_qualifier (   self,
  element 
)
private
Return True iff element is a qualifier element.

Definition at line 4416 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply.QUALIFIERS.

Referenced by CellMLToNektar.pycml.mathml_apply.operands(), and CellMLToNektar.pycml.mathml_apply.qualifiers().

4417  def _is_qualifier(self, element):
4418  """Return True iff element is a qualifier element."""
4419  return element.localName in self.QUALIFIERS
def CellMLToNektar.pycml.mathml_apply._reduce (   self,
  check_operator = True 
)
private
Reduce this expression by evaluating its static parts.

Definition at line 4968 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_constructor._reduce_elt(), CellMLToNektar.pycml.mathml_constructor._update_usage_counts(), CellMLToNektar.utilities.DEBUG(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4969  def _reduce(self, check_operator=True):
4970  """Reduce this expression by evaluating its static parts."""
4971  # Check to see if this operator requires a special
4972  # reduction strategy
4973  op = self.operator()
4974  DEBUG('partial-evaluator', "Reducing", op.localName, getattr(self, u'id', ''))
4975  if check_operator and hasattr(op, '_reduce'):
4976  op._reduce()
4977  else:
4978  bt = self._get_binding_time()
4979  if bt == BINDING_TIMES.static:
4980  # Evaluate self and replace by a <cn>, <true> or <false>
4981  new_elt = self._eval_self()
4982  self._xfer_complexity(new_elt)
4983  self.replace_child(self, new_elt, self.xml_parent)
4984  # Update usage counts
4985  self._update_usage_counts(self, remove=True)
4986  elif bt == BINDING_TIMES.dynamic:
4987  # Recurse into operands and reduce those
4988  for op in self.operands():
4989  self._reduce_elt(op)
4990  return
def CellMLToNektar.pycml.mathml_apply._set_in_units (   self,
  units,
  no_act = False 
)
private
Set the units this expression should be given in.

If these aren't our natural units (as given by an initial
get_units) then we need to add units conversion code.

Definition at line 4523 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_units_mixin._set_element_in_units(), CellMLToNektar.pycml.cellml_variable.get_units(), CellMLToNektar.pycml.mathml_cn.get_units(), CellMLToNektar.pycml.mathml_ci.get_units(), CellMLToNektar.pycml.mathml_apply.get_units(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4524  def _set_in_units(self, units, no_act=False):
4525  """Set the units this expression should be given in.
4526 
4527  If these aren't our natural units (as given by an initial
4528  get_units) then we need to add units conversion code.
4529  """
4530  # First check we have something to do
4531  current_units = self.get_units()
4532  if units is current_units:
4533  return
4534  # Next, check if the required units can be achieved by suitable choices for operand units
4535  done = False
4536  if units in current_units:
4537  # They can!
4538  if not no_act:
4539  self._cml_units = units
4540  for src_units_set, src_units in current_units._get_sources(units):
4541  expr = src_units_set.get_expression()
4542  self._set_element_in_units(expr, src_units, no_act)
4543  done = True
4544  if not done and not no_act:
4545  # Some operators need this to be a UnitsSet
4546  self._cml_units = UnitsSet([units], self)
4547  if not done:
4548  # The behaviour now depends on the operator
4549  op = self.operator()
4550  if hasattr(op, '_set_in_units') and callable(op._set_in_units):
4551  op._set_in_units(units, no_act)
4552  else:
4553  raise UnitsError(self, u' '.join([
4554  "Don't know how to select units for operands of operator",
4555  op.localName, "when its units are", units.description()]))
def CellMLToNektar.pycml.mathml_apply.assigned_variable (   self)
Return the variable assigned to by this assignment.

Should only be called on a top-level assignment expression.

If it's a straightforward assignment (so self.is_assignment()
returns True) then return the cellml_variable object
representing the variable assigned to.

If it's an ODE, return a pair
(dependent variable, independent variable).

Definition at line 4917 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, and CellMLToNektar.pycml.mathml_apply.is_top_level().

4918  def assigned_variable(self):
4919  """Return the variable assigned to by this assignment.
4920 
4921  Should only be called on a top-level assignment expression.
4922 
4923  If it's a straightforward assignment (so self.is_assignment()
4924  returns True) then return the cellml_variable object
4925  representing the variable assigned to.
4926 
4927  If it's an ODE, return a pair
4928  (dependent variable, independent variable).
4929  """
4930  if not self.is_top_level():
4931  raise TypeError("not a top-level apply element")
4932  else:
4933  return self._cml_assigns_to
def CellMLToNektar.pycml.mathml_apply.check_assigned_var (   self)
Check the current component owns the variable being assigned to.

Should only be called if this object represents an assignment
expression.  Checks that the variable being assigned to doesn't
have an interface value of 'in'.  If this isn't a simple assignment
(i.e. the LHS isn't a plain ci element) then the check succeeds
automatically.

Adds to the model's error list if the check fails.  This method
always returns None.

Definition at line 4758 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply.classify_variables(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4759  def check_assigned_var(self):
4760  """Check the current component owns the variable being assigned to.
4761 
4762  Should only be called if this object represents an assignment
4763  expression. Checks that the variable being assigned to doesn't
4764  have an interface value of 'in'. If this isn't a simple assignment
4765  (i.e. the LHS isn't a plain ci element) then the check succeeds
4766  automatically.
4767 
4768  Adds to the model's error list if the check fails. This method
4769  always returns None.
4770  """
4771  # Check this is an application of eq
4772  if self.operator().localName != u'eq':
4773  raise MathsError(self, u'Top-level mathematics expressions should be assigment expressions.')
4774  first_operand = self.operands().next()
4775  if first_operand.localName == u'ci':
4776  # We've already checked that the variable exists
4777  var = first_operand.variable
4778  for iface in [u'public_interface', u'private_interface']:
4779  if getattr(var, iface, u'none') == u'in':
4780  raise MathsError(self, u' '.join([
4781  u'Variable', var.fullname(),
4782  u'is assigned to in a math element, but has its',
4783  iface, u'set to "in".']))
def CellMLToNektar.pycml.mathml_apply.classify_variables (   self,
  root = False,
  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 root is True, store this set as a
list, to represent edges of a dependency graph.
Also, if root is True then this node is the root of an expression
(so it will be an application of eq); treat the LHS differently.

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 4786 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

Referenced by CellMLToNektar.pycml.mathml_apply.check_assigned_var().

4787  needs_special_treatment=lambda n: None):
4788  """
4789  Classify variables in this expression according to how they are
4790  used.
4791 
4792  In the process, compute and return a set of variables on which
4793  this expression depends. If root is True, store this set as a
4794  list, to represent edges of a dependency graph.
4795  Also, if root is True then this node is the root of an expression
4796  (so it will be an application of eq); treat the LHS differently.
4797 
4798  If dependencies_only then the variable classification will not be
4799  done, only dependencies will be analysed. This is useful for doing
4800  a 'light' re-analysis if the dependency set has been reduced; if the
4801  set has increased then the topological sort of equations may need to
4802  be redone.
4803 
4804  The function needs_special_treatment may be supplied to override the
4805  default recursion into sub-trees. It takes a single sub-tree as
4806  argument, and should either return the dependency set for that
4807  sub-tree, or None to use the default recursion. This is used when
4808  re-analysing dependencies after applying lookup tables, since table
4809  lookups only depend on the keying variable.
4810  """
4811  dependencies = set()
4812  ode_indep_var = None
4813  op = self.operator()
4814  if op.localName == u'diff':
4815  # This is a derivative dy/dx on the RHS of an assignment.
4816  # Store the dependency as a pair (y,x)
4817  dependencies.add((op.dependent_variable, op.independent_variable))
4818  if not dependencies_only:
4819  # Set variable types
4820  op._set_var_types()
4821  else:
4822  opers = self.operands()
4823  if root:
4824  # Treat the LHS of the assignment
4825  lhs = opers.next()
4826  if lhs.localName == u'ci':
4827  # Direct assignment to variable
4828  var = lhs.variable
4829  var._add_dependency(self)
4830  self._cml_assigns_to = var
4831  if not dependencies_only:
4832  # Check for possibly conflicting types
4833  t = var.get_type()
4834  if t == VarTypes.Constant or t == VarTypes.MaybeConstant:
4835  self.model.validation_warning(
4836  u' '.join([
4837  u'Variable',var.fullname(),u'is assigned to',
4838  u'and has an initial value set.']),
4839  level=logging.WARNING_TRANSLATE_ERROR)
4840  elif t == VarTypes.State or t == VarTypes.Free:
4841  self.model.validation_warning(
4842  u' '.join([
4843  u'Variable',var.fullname(),u'is assigned to',
4844  u'and appears on the LHS of an ODE.']),
4845  level=logging.WARNING_TRANSLATE_ERROR)
4846  var._set_type(VarTypes.Computed)
4847  elif lhs.localName == u'apply':
4848  # This could be an ODE
4849  diff = lhs.operator()
4850  if diff.localName == u'diff':
4851  # It is an ODE. TODO: Record it somewhere?
4852  if not dependencies_only:
4853  diff._set_var_types()
4854  dep = diff.dependent_variable
4855  indep = diff.independent_variable
4856  dep._add_ode_dependency(indep, self)
4857  # An ODE should depend on its independent variable
4858  ode_indep_var = indep
4859  if not dependencies_only:
4860  indep._used()
4861  # TODO: Hack; may remove.
4862  self._cml_assigns_to = (dep, indep)
4863  else:
4864  raise MathsError(self, u'Assignment statements are expected to be an ODE or assign to a variable.',
4865  warn=True,
4866  level=logging.WARNING_TRANSLATE_ERROR)
4867  else:
4868  raise MathsError(self, u'Assignment statements are expected to be an ODE or assign to a variable.',
4869  warn=True,
4870  level=logging.WARNING_TRANSLATE_ERROR)
4871 
4872  # Consider operands other than the LHS of an assignment
4873  for oper in opers:
4874  dependencies.update(self.classify_child_variables(oper, dependencies_only=dependencies_only,
4875  needs_special_treatment=needs_special_treatment))
4876 
4877  if ode_indep_var:
4878  # ODEs should depend on their independent variable.
4879  # However, for code generation we wish to distinguish
4880  # whether the independent variable appears on the RHS or
4881  # not.
4882  if ode_indep_var in dependencies:
4883  self._cml_ode_has_free_var_on_rhs = True
4884  else:
4885  self._cml_ode_has_free_var_on_rhs = False
4886  dependencies.add(ode_indep_var)
4887 
4888  if root:
4889  # Store dependencies
4890  self._cml_depends_on = list(dependencies)
4891  return dependencies
def CellMLToNektar.pycml.mathml_apply.clear_dependency_info (   self)
Clear the type, dependency, etc. information for this equation.

This allows us to re-run the type & dependency analysis for the model.

Definition at line 4398 of file pycml.py.

4399  def clear_dependency_info(self):
4400  """Clear the type, dependency, etc. information for this equation.
4401 
4402  This allows us to re-run the type & dependency analysis for the model."""
4403  self._cml_binding_time = None
4404  # Dependency graph edges
4406  self._cml_assigns_to = None
4407  self.clear_colour()
def CellMLToNektar.pycml.mathml_apply.create_new (   elt,
  operator,
  operands = [],
  qualifiers = [] 
)
static
Create a new MathML apply element, with given content.

elt should be any element in the document.

operator is used as the name of the first, empty, child.

operands is a list, possibly empty, of operand elements.  If
any member is a unicode object, it is considered to be the
name of a variable.  If a tuple, then it should be a pair of
unicode objects: (number, units).  (Although units can be an
attribute dictionary.)

qualifiers specifies a list of qualifier elements.

Definition at line 4992 of file pycml.py.

References CellMLToNektar.pycml.check_append_safety().

4993  def create_new(elt, operator, operands=[], qualifiers=[]):
4994  """Create a new MathML apply element, with given content.
4995 
4996  elt should be any element in the document.
4997 
4998  operator is used as the name of the first, empty, child.
4999 
5000  operands is a list, possibly empty, of operand elements. If
5001  any member is a unicode object, it is considered to be the
5002  name of a variable. If a tuple, then it should be a pair of
5003  unicode objects: (number, units). (Although units can be an
5004  attribute dictionary.)
5005 
5006  qualifiers specifies a list of qualifier elements.
5007  """
5008  app = elt.xml_create_element(u'apply', NSS[u'm'])
5009  app.xml_append(app.xml_create_element(unicode(operator), NSS[u'm']))
5010  for qual in qualifiers:
5011  check_append_safety(qual)
5012  app.xml_append(qual)
5013  for op in operands:
5014  if isinstance(op, basestring):
5015  # Variable name
5016  op = app.xml_create_element(u'ci', NSS[u'm'],
5017  content=unicode(op))
5018  elif isinstance(op, tuple):
5019  # Constant with units
5020  if isinstance(op[1], dict):
5021  attrs = op[1]
5022  else:
5023  attrs = {(u'cml:units', NSS[u'cml']): unicode(op[1])}
5024  op = app.xml_create_element(u'cn', NSS[u'm'],
5025  attributes=attrs,
5026  content=unicode(op[0]))
5027  else:
5028  # Should already be an element
5030  app.xml_append(op)
5031  return app
def check_append_safety
Definition: pycml.py:197
def CellMLToNektar.pycml.mathml_apply.evaluate (   self)
Evaluate this expression, and return its value, if possible.

Definition at line 4447 of file pycml.py.

References CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

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

4448  def evaluate(self):
4449  """
4450  Evaluate this expression, and return its value, if possible.
4451  """
4452  # Result depends on the operator
4453  op = self.operator()
4454  if hasattr(op, 'evaluate') and callable(op.evaluate):
4455  return op.evaluate()
4456  else:
4457  raise EvaluationError("Don't know how to evaluate the operator " + op.localName)
def CellMLToNektar.pycml.mathml_apply.get_dependencies (   self)
Return the list of variables this expression depends on.

Definition at line 4408 of file pycml.py.

References CellMLToNektar.pycml.cellml_variable._cml_depends_on, and CellMLToNektar.pycml.mathml_apply._cml_depends_on.

4409  def get_dependencies(self):
4410  """Return the list of variables this expression depends on."""
4411  return self._cml_depends_on
def CellMLToNektar.pycml.mathml_apply.get_units (   self)
Recursively check this expression for dimensional consistency.

Checks that the operands have suitable units.
What constitutes 'suitable' depends on the operator; see appendix
C.3.2 of the CellML 1.0 spec.

If yes, returns a <units> element for the whole expression, based
on the rules in appendix C.3.3.

Throws a UnitsError if the units are inconsistent.

Definition at line 4556 of file pycml.py.

References CellMLToNektar.pycml._child1(), 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_constructor._get_element_binding_time(), CellMLToNektar.pycml.mathml_constructor._get_element_units(), CellMLToNektar.pycml.mathml_apply._get_operand_units(), CellMLToNektar.pycml.copy(), CellMLToNektar.pycml.mathml.eval(), CellMLToNektar.processors.ModelModifier.model, CellMLToNektar.translators.CellMLTranslator.model, CellMLToNektar.pycml.cellml_variable.model(), CellMLToNektar.pycml.mathml.model(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

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

4557  def get_units(self):
4558  """Recursively check this expression for dimensional consistency.
4559 
4560  Checks that the operands have suitable units.
4561  What constitutes 'suitable' depends on the operator; see appendix
4562  C.3.2 of the CellML 1.0 spec.
4563 
4564  If yes, returns a <units> element for the whole expression, based
4565  on the rules in appendix C.3.3.
4566 
4567  Throws a UnitsError if the units are inconsistent.
4568  """
4569  if self._cml_units:
4570  return self._cml_units
4571  our_units = None
4572  op = self.operator().localName
4573  operand_units = self._get_operand_units()
4574  # Tuples where second item is an index
4575  operand_units_idx = itertools.izip(operand_units, itertools.count(1))
4576  # Standard units objects
4577  dimensionless = self.model.get_units_by_name(u'dimensionless')
4578  boolean = self.model.get_units_by_name(u'cellml:boolean')
4579 
4580  if op in self.OPS.relations | self.OPS.plusMinus:
4581  our_units = operand_units.next().copy()
4582  # Operands mustn't be booleans
4583  if boolean in our_units:
4584  raise UnitsError(self, u' '.join([
4585  u'Operator',op,u'has boolean operands, which does not make sense.']))
4586  # Operand units must be 'equivalent' (perhaps dimensionally)
4587  for u in operand_units:
4588  if not hasattr(self.model, '_cml_special_units_converter') and not our_units.dimensionally_equivalent(u):
4589  raise UnitsError(self, u' '.join([
4590  u'Operator',op,u'requires its operands to have',
4591  u'dimensionally equivalent units;',u.description(),
4592  u'and',our_units.description(),u'differ']))
4593  our_units.update(u)
4594  if op in self.OPS.relations:
4595  # Result has cellml:boolean units
4596  our_units = UnitsSet([boolean])
4597  elif op in self.OPS.logical:
4598  # Operand units must be cellml:boolean
4599  for u, i in operand_units_idx:
4600  if not boolean in u:
4601  raise UnitsError(self, u' '.join([
4602  u'Operator',op,u'requires operands to be booleans;',
4603  u'operand',str(i),u'has units',u.description()]))
4604  # Result has cellml:boolean units
4605  our_units = UnitsSet([boolean])
4606  elif op in self.OPS.elementary:
4607  # Operand units must be dimensionless
4608  for u, i in operand_units_idx:
4609  if not u.dimensionally_equivalent(dimensionless):
4610  raise UnitsError(self, u' '.join([
4611  u'Operator',op,u'requires operands to be dimensionless;',
4612  u'operand',str(i),u'has units',u.description()]))
4613  if op == 'log':
4614  # <logbase> qualifier must have units dimensionless
4615  if hasattr(self, u'logbase'):
4616  base = _child1(self.logbase)
4617  u = self._get_element_units(base)
4618  if not u.dimensionally_equivalent(dimensionless):
4619  raise UnitsError(self, u' '.join([u'The logbase qualifier must have dimensionless',
4620  u'units, not',u.description()]))
4621  # Result has units of dimensionless
4622  our_units = UnitsSet([dimensionless])
4623  elif op == 'power':
4624  # Arg1 : any, Arg2 : dimensionless
4625  arg_units = operand_units.next()
4626  if boolean in arg_units:
4627  raise UnitsError(self, u'The argument of <power> should not be boolean')
4628  exponent_units = operand_units.next()
4629  if not exponent_units.dimensionally_equivalent(dimensionless):
4630  raise UnitsError(self, u' '.join([u'The second operand to power must have dimensionless',
4631  u'units, not',exponent_units.description()]))
4632  # Result has units that are the units on the (first)
4633  # operand raised to the power of the second operand. If
4634  # units on the first operand are dimensionless, then so is
4635  # the result.
4636  # TODO: Check how we could allow equiv. to d'less, instead of equal.
4637  # Need to consider any multiplicative factor...
4638  if arg_units.equals(dimensionless):
4639  our_units = UnitsSet([dimensionless])
4640  else:
4641  opers = self.operands()
4642  opers.next()
4643  # Make sure exponent is static
4644  expt = opers.next()
4645  if self._get_element_binding_time(expt) != BINDING_TIMES.static:
4646  raise UnitsError(self, 'Unable to units check power with an exponent that can vary at run-time',
4647  warn=True,
4648  level=logging.WARNING_TRANSLATE_ERROR)
4649  # Try to evaluate the exponent
4650  try:
4651  expt = self.eval(expt)
4652  except EvaluationError, e:
4653  raise UnitsError(self, u' '.join([u'Unable to evaluate the exponent of a power element:', unicode(e)]),
4654  warn=True,
4655  level=logging.WARNING_TRANSLATE_ERROR)
4656  our_units = dimensionless.simplify(arg_units, expt)
4657  elif op == 'root':
4658  # Arg : any, <degree> : dimensionless
4659  arg_units = operand_units.next()
4660  if boolean in arg_units:
4661  raise UnitsError(self, u'The argument of <root> should not be boolean')
4662  if hasattr(self, u'degree'):
4663  degree = _child1(self.degree)
4664  u = self._get_element_units(degree)
4665  if not u.dimensionally_equivalent(dimensionless):
4666  raise UnitsError(self, u' '.join([
4667  u'The degree qualifier must have dimensionless units, not',u.description()]))
4668  else:
4669  degree = 2.0 # Default is square root
4670  # Result has units that are the units on the (first) operand
4671  # raised to the power of the reciprocal of the value of the
4672  # degree qualifier.
4673  # TODO: If units on the first operand are dimensionless,
4674  # then so is the result.
4675  if not type(degree) is float:
4676  # Make sure degree is static
4677  if self._get_element_binding_time(degree) != BINDING_TIMES.static:
4678  raise UnitsError(self, 'Unable to units check root with a degree that can vary at run-time',
4679  warn=True,
4680  level=logging.WARNING_TRANSLATE_ERROR)
4681  try:
4682  degree = self.eval(degree)
4683  except EvaluationError, e:
4684  raise UnitsError(self, u' '.join([u'Unable to evaluate the degree of a root element:', unicode(e)]),
4685  warn=True,
4686  level=logging.WARNING_TRANSLATE_ERROR)
4687  our_units = dimensionless.simplify(arg_units, 1/degree)
4688  elif op == 'diff':
4689  # Arg : any, <bvar> : any, <degree> : dimensionless
4690  arg_units = operand_units.next()
4691  if boolean in arg_units:
4692  raise UnitsError(self, u'The argument of <diff> should not be boolean')
4693  if hasattr(self, u'bvar'):
4694  if hasattr(self.bvar, u'degree'):
4695  degree = _child1(self.bvar.degree)
4696  u = self._get_element_units(degree)
4697  if not u.dimensionally_equivalent(dimensionless):
4698  raise UnitsError(self, u' '.join([
4699  u'The degree qualifier must have dimensionless units, not',u.description()]))
4700  else:
4701  degree = 1.0 # Default is first derivative
4702  else:
4703  raise UnitsError(self, u'A diff operator must have a bvar qualifier')
4704  # Result has units that are the quotient of the units of the
4705  # operand, over the units of the term in the bvar qualifier
4706  # raised to the value of the degree qualifier
4707  if not type(degree) is float:
4708  # Make sure exponent is static
4709  if self._get_element_binding_time(degree) != BINDING_TIMES.static:
4710  raise UnitsError(self, 'Unable to units check derivative with a degree that can vary at run-time',
4711  warn=True,
4712  level=logging.WARNING_TRANSLATE_ERROR)
4713  try:
4714  degree = self.eval(degree)
4715  except EvaluationError, e:
4716  raise UnitsError(self, u' '.join([u'Unable to evaluate the degree of a diff element:', unicode(e)]),
4717  warn=True,
4718  level=logging.WARNING_TRANSLATE_ERROR)
4719  for e in self.xml_element_children(self.bvar):
4720  if not e.localName == u'degree':
4721  bvar_units = self._get_element_units(e)
4722  break
4723  else:
4724  raise UnitsError(self, u'diff element does not have a valid bvar')
4725  our_units = arg_units.simplify(bvar_units, -degree)
4726  elif op in self.OPS.absRound | self.OPS.timesDivide:
4727  # No restrictions on operand units, except that they shouldn't be boolean
4728  for u in self._get_operand_units():
4729  if boolean in u:
4730  raise UnitsError(self, u' '.join([
4731  u'Operator',op,u'has boolean operands, which does not make sense.']))
4732  if op == 'times':
4733  # Result has units that are the product of the operand units
4734  our_units = operand_units.next().copy()
4735  for u in operand_units:
4736  our_units = our_units.simplify(other_units=u)
4737  elif op == 'divide':
4738  # Result has units that are the quotient of the units
4739  # on the first and second operands
4740  our_units = operand_units.next()
4741  our_units = our_units.simplify(other_units=operand_units.next(), other_exponent=-1)
4742  else:
4743  # Result has same units as operands
4744  our_units = operand_units.next().copy()
4745  else:
4746  # Warning: unsupported operator!
4747  raise UnitsError(self, u' '.join([u'Unsupported operator for units checking:', op]),
4748  warn=True,
4749  level=logging.WARNING_TRANSLATE_ERROR)
4750 
4751  # Cache & return result
4752  if isinstance(our_units, cellml_units):
4753  # Units conversion has been done, then PE
4754  our_units = UnitsSet([our_units])
4755  self._cml_units = our_units
4756  our_units.set_expression(self)
4757  return self._cml_units
def CellMLToNektar.pycml.mathml_apply.is_assignment (   self)
Return True iff this is a straightforward assignment expression.

Only makes sense if called on a top-level assignment expression.
Checks that this is *not* an ODE, but assigns to a single variable.

Definition at line 4907 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, and CellMLToNektar.pycml.mathml_apply.is_top_level().

4908  def is_assignment(self):
4909  """Return True iff this is a straightforward assignment expression.
4910 
4911  Only makes sense if called on a top-level assignment expression.
4912  Checks that this is *not* an ODE, but assigns to a single variable.
4913  """
4914  if not self.is_top_level():
4915  return False
4916  return isinstance(self._cml_assigns_to, cellml_variable)
def CellMLToNektar.pycml.mathml_apply.is_ode (   self)
Return True iff this is the assignment of an ODE.

Only makes sense if called on a top-level assignment
expression, and checks if it represents an ODE, i.e. if the
LHS is a derivative.

Definition at line 4896 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, and CellMLToNektar.pycml.mathml_apply.is_top_level().

4897  def is_ode(self):
4898  """Return True iff this is the assignment of an ODE.
4899 
4900  Only makes sense if called on a top-level assignment
4901  expression, and checks if it represents an ODE, i.e. if the
4902  LHS is a derivative.
4903  """
4904  if not self.is_top_level():
4905  return False
4906  return type(self._cml_assigns_to) == types.TupleType
def CellMLToNektar.pycml.mathml_apply.is_top_level (   self)
Test whether this is a top-level assignment expression.

Definition at line 4892 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to.

Referenced by CellMLToNektar.pycml.mathml_apply.assigned_variable(), CellMLToNektar.pycml.mathml_apply.is_assignment(), and CellMLToNektar.pycml.mathml_apply.is_ode().

4893  def is_top_level(self):
4894  """Test whether this is a top-level assignment expression."""
4895  return self._cml_assigns_to is not None
def CellMLToNektar.pycml.mathml_apply.operands (   self)
Return an iterable over the elements representing the operands for
this application.

Definition at line 4428 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._is_qualifier().

Referenced by CellMLToNektar.pycml.mathml_apply._get_binding_time(), CellMLToNektar.pycml.mathml_apply._get_operand_units(), CellMLToNektar.pycml.mathml_apply._reduce(), CellMLToNektar.pycml.mathml_apply.check_assigned_var(), CellMLToNektar.pycml.mathml_apply.classify_variables(), CellMLToNektar.pycml.mathml_apply.get_units(), and CellMLToNektar.pycml.mathml_apply.tree_complexity().

4429  def operands(self):
4430  """
4431  Return an iterable over the elements representing the operands for
4432  this application.
4433  """
4434  # Get all element children and strip the first (the operator)
4435  operands = self.xml_element_children()
4436  operands.next()
4437  # Now strip qualifiers from the front
4438  return itertools.dropwhile(self._is_qualifier, operands)
def CellMLToNektar.pycml.mathml_apply.operator (   self)
Return the element representing the operator being applied.

Definition at line 4412 of file pycml.py.

References CellMLToNektar.pycml._child1().

Referenced by CellMLToNektar.pycml.mathml_apply._get_binding_time(), CellMLToNektar.pycml.mathml_apply._reduce(), CellMLToNektar.pycml.mathml_apply._set_in_units(), CellMLToNektar.pycml.mathml_apply.check_assigned_var(), CellMLToNektar.pycml.mathml_apply.classify_variables(), CellMLToNektar.pycml.mathml_apply.evaluate(), CellMLToNektar.pycml.mathml_apply.get_units(), and CellMLToNektar.pycml.mathml_apply.tree_complexity().

4413  def operator(self):
4414  """Return the element representing the operator being applied."""
4415  return _child1(self)
def CellMLToNektar.pycml.mathml_apply.qualifiers (   self)
Return an iterable over the elements representing the qualifiers for
this application.

Definition at line 4420 of file pycml.py.

References CellMLToNektar.pycml.mathml_apply._is_qualifier().

4421  def qualifiers(self):
4422  """
4423  Return an iterable over the elements representing the qualifiers for
4424  this application.
4425  """
4426  quals = self.xml_element_children()
4427  return filter(self._is_qualifier, quals)
def CellMLToNektar.pycml.mathml_apply.tree_complexity (   self,
  kw 
)
Calculate a rough estimate of the computation time for
evaluating this <apply> element.

Operates recursively, so the complexity of a function call is
given by summing the complexity of the arguments and the time
for evaluating the function itself.

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.

Definition at line 4458 of file pycml.py.

References CellMLToNektar.pycml.mathml_constructor._tree_complexity(), CellMLToNektar.utilities.add_dicts(), CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

4459  def tree_complexity(self, **kw):
4460  """
4461  Calculate a rough estimate of the computation time for
4462  evaluating this <apply> element.
4463 
4464  Operates recursively, so the complexity of a function call is
4465  given by summing the complexity of the arguments and the time
4466  for evaluating the function itself.
4467 
4468  If lookup_tables is True, then assume we're using lookup tables
4469  where possible.
4470  If algebraic is True, the complexity is calculated as a dictionary,
4471  mapping node types to the number of occurences of that type.
4472  """
4473  kw['algebraic'] = kw.get('algebraic', False)
4474  if kw['algebraic']: ac = {}
4475  else: ac = 0
4476 
4477  # Complexity of this function
4478  op_name = self.operator().localName
4479  OPS = self.OPS
4480  if op_name in OPS.plusMinus or op_name in OPS.logical or op_name in OPS.relations:
4481  if kw['algebraic']: ac['op'] = (len(list(self.operands())) - 1)
4482  else: ac += 1 * (len(list(self.operands())) - 1)
4483  elif op_name in OPS.absRound:
4484  if op_name == 'abs':
4485  if kw['algebraic']: ac['abs'] = 1
4486  else: ac += 5
4487  else:
4488  if kw['algebraic']: ac['round'] = 1
4489  else: ac += 20
4490  elif op_name in OPS.elementary:
4491  if kw['algebraic']: ac['elementary'] = 1
4492  else: ac += 70
4493  elif op_name == 'times':
4494  if kw['algebraic']: ac['times'] = (len(list(self.operands())) - 1)
4495  else: ac += 1 * (len(list(self.operands())) - 1)
4496  elif op_name == 'divide':
4497  if kw['algebraic']: ac['divide'] = 1
4498  else: ac += 15
4499  elif op_name == 'power':
4500  # This will vary depending on the exponent - gcc can optimise for 2 and 3, it seems.
4501  exponent = list(self.operands())[1]
4502  if exponent.localName == u'cn' and unicode(exponent).strip() in [u'2', u'3']:
4503  if kw['algebraic']: ac['power2'] = 1
4504  else: ac += 5
4505  else:
4506  if kw['algebraic']: ac['power'] = 1
4507  else: ac += 30
4508  elif op_name == 'root':
4509  if kw['algebraic']: ac['root'] = 1
4510  else: ac += 30
4511  elif op_name == 'diff':
4512  if kw['algebraic']: ac['variable'] = 1
4513  else: ac += 0.7
4514  else:
4515  raise EvaluationError("Don't know complexity of operator " + op_name)
4516 
4517  # Complexity of operands
4518  for elt in self.operands():
4519  if kw['algebraic']:
4520  add_dicts(ac, self._tree_complexity(elt, **kw))
4521  else: ac += self._tree_complexity(elt, **kw)
4522  return ac

Member Data Documentation

CellMLToNektar.pycml.mathml_apply._cml_assigns_to
private

Definition at line 4405 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_apply.assigned_variable(), CellMLToNektar.pycml.mathml_apply.classify_variables(), CellMLToNektar.pycml.mathml_apply.is_assignment(), CellMLToNektar.pycml.mathml_apply.is_ode(), and CellMLToNektar.pycml.mathml_apply.is_top_level().

CellMLToNektar.pycml.mathml_apply._cml_binding_time
private

Definition at line 4402 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_apply._get_binding_time(), and CellMLToNektar.pycml.mathml_piecewise._get_binding_time().

CellMLToNektar.pycml.mathml_apply._cml_depends_on
private

Definition at line 4404 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_apply.get_dependencies().

CellMLToNektar.pycml.mathml_apply._cml_ode_has_free_var_on_rhs
private

Definition at line 4882 of file pycml.py.

CellMLToNektar.pycml.mathml_apply._cml_units
private

Definition at line 4395 of file pycml.py.

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

tuple CellMLToNektar.pycml.mathml_apply.QUALIFIERS
static
Initial value:
1 = frozenset(('degree', 'bvar', 'logbase',
2  'lowlimit', 'uplimit', 'interval', 'condition',
3  'domainofapplication', 'momentabout'))

Definition at line 4374 of file pycml.py.

Referenced by CellMLToNektar.pycml.mathml_apply._is_qualifier().