Nektar++
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:
[legend]

Classes

class  OPS
 

Public Member Functions

def __init__ (self)
 
def clear_dependency_info (self)
 
def get_dependencies (self)
 
def operator (self)
 
def qualifiers (self)
 
def operands (self)
 
def evaluate (self)
 
def tree_complexity (self, **kw)
 
def get_units (self)
 
def check_assigned_var (self)
 
def classify_variables (self, root=False, dependencies_only=False, needs_special_treatment=lambda n:None)
 
def is_top_level (self)
 
def is_ode (self)
 
def is_assignment (self)
 
def assigned_variable (self)
 
- Public Member Functions inherited from CellMLToNektar.utilities.Colourable
def __init__ (self, *args, **kwargs)
 
def set_colour (self, colour)
 
def get_colour (self)
 
def clear_colour (self)
 
def __init__ (self)
 
- Public Member Functions inherited from CellMLToNektar.pycml.mathml
def __init__ (self)
 
def __repr__ (self)
 
def __deepcopy__ (self, memo)
 
def clone_self (self, register=False)
 
def get_original_of_clone (self)
 
def get_component (self)
 
def model (self)
 
def eval (self, elt)
 
- Public Member Functions inherited from CellMLToNektar.pycml.element_base
def __init__ (self)
 
def __delattr__ (self, key)
 
def __setattr__ (self, key, value)
 
def rootNode (self)
 
def cmeta_id (self)
 
def xml_remove_child_at (self, index=-1)
 
def xml_doc (self)
 
def xml_properties (self)
 

Static Public Member Functions

def create_new (elt, operator, operands=[], qualifiers=[])
 
- Static Public Member Functions inherited from CellMLToNektar.pycml.mathml
def clone (expr)
 

Static Public Attributes

 QUALIFIERS
 

Private Member Functions

def _is_qualifier (self, element)
 
def _get_operand_units (self)
 
def _set_in_units (self, units, no_act=False)
 
def _get_binding_time (self, check_operator=True)
 
def _reduce (self, check_operator=True)
 

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

◆ __init__()

def CellMLToNektar.pycml.mathml_apply.__init__ (   self)

Reimplemented from CellMLToNektar.pycml.mathml_constructor.

Definition at line 4393 of file pycml.py.

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

References CellMLToNektar.pycml.mathml_apply.__init__().

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

Member Function Documentation

◆ _get_binding_time()

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.

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

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.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_ci._reduce(), CellMLToNektar.pycml.mathml_apply._reduce(), CellMLToNektar.pycml.mathml_piecewise._reduce(), CellMLToNektar.pycml.cellml_variable.get_value(), and CellMLToNektar.pycml.cellml_variable.is_statically_const().

◆ _get_operand_units()

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.

4439 def _get_operand_units(self):
4440 """
4441 Return an iterable containing a <units> element for each operand
4442 of this expression.
4443 """
4444 for o in self.operands():
4445 yield self._get_element_units(o)
4446

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().

◆ _is_qualifier()

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.

4416 def _is_qualifier(self, element):
4417 """Return True iff element is a qualifier element."""
4418 return element.localName in self.QUALIFIERS
4419

References CellMLToNektar.pycml.mathml_apply.QUALIFIERS.

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

◆ _reduce()

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.

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

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_piecewise._get_binding_time(), CellMLToNektar.pycml.mathml_diff._get_binding_time(), CellMLToNektar.pycml.mathml_and._get_binding_time(), CellMLToNektar.pycml.mathml_or._get_binding_time(), CellMLToNektar.pycml.mathml_eq._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().

◆ _set_in_units()

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.

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

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(), CellMLToNektar.pycml.cellml_variable.get_units(), CellMLToNektar.pycml.mathml_apply.get_units(), CellMLToNektar.pycml.mathml_piecewise.get_units(), CellMLToNektar.pycml.mathml_cn.get_units(), CellMLToNektar.pycml.mathml_ci.get_units(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

◆ assigned_variable()

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.

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

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

◆ check_assigned_var()

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.

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

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

◆ classify_variables()

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

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

References CellMLToNektar.pycml.mathml_apply._cml_assigns_to, CellMLToNektar.processors.ModelModifier.model, CellMLToNektar.pycml.cellml_variable.model(), CellMLToNektar.pycml.mathml.model(), CellMLToNektar.translators.CellMLTranslator.model, CellMLToNektar.optimize.ExpressionMatcher.A.operands, CellMLToNektar.pycml.mathml_apply.operands(), CellMLToNektar.optimize.ExpressionMatcher.A.operator, and CellMLToNektar.pycml.mathml_apply.operator().

◆ clear_dependency_info()

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.

4398 def clear_dependency_info(self):
4399 """Clear the type, dependency, etc. information for this equation.
4400
4401 This allows us to re-run the type & dependency analysis for the model."""
4402 self._cml_binding_time = None
4403 # Dependency graph edges
4404 self._cml_depends_on = []
4405 self._cml_assigns_to = None
4406 self.clear_colour()
4407

Referenced by CellMLToNektar.pycml.cellml_variable.__init__().

◆ create_new()

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.

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

References CellMLToNektar.pycml.check_append_safety().

◆ evaluate()

def CellMLToNektar.pycml.mathml_apply.evaluate (   self)
Evaluate this expression, and return its value, if possible.

Definition at line 4447 of file pycml.py.

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

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

Referenced by CellMLToNektar.pycml.mathml_constructor._eval_self(), and CellMLToNektar.pycml.mathml_ci._reduce().

◆ get_dependencies()

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.

4408 def get_dependencies(self):
4409 """Return the list of variables this expression depends on."""
4410 return self._cml_depends_on
4411

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

◆ get_units()

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.

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

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_piecewise._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.pycml.get_units_by_name(), CellMLToNektar.processors.ModelModifier.model, CellMLToNektar.pycml.cellml_variable.model(), CellMLToNektar.pycml.mathml.model(), CellMLToNektar.translators.CellMLTranslator.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().

◆ is_assignment()

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.

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

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

◆ is_ode()

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.

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

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

◆ is_top_level()

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.

4892 def is_top_level(self):
4893 """Test whether this is a top-level assignment expression."""
4894 return self._cml_assigns_to is not None
4895

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().

◆ operands()

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.

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

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(), CellMLToNektar.optimize.ExpressionMatcher.A.match(), and CellMLToNektar.pycml.mathml_apply.tree_complexity().

◆ operator()

def CellMLToNektar.pycml.mathml_apply.operator (   self)

◆ qualifiers()

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.

4420 def qualifiers(self):
4421 """
4422 Return an iterable over the elements representing the qualifiers for
4423 this application.
4424 """
4425 quals = self.xml_element_children()
4426 return filter(self._is_qualifier, quals)
4427

References CellMLToNektar.pycml.mathml_apply._is_qualifier().

◆ tree_complexity()

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.

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

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().

Member Data Documentation

◆ _cml_assigns_to

CellMLToNektar.pycml.mathml_apply._cml_assigns_to
private

◆ _cml_binding_time

CellMLToNektar.pycml.mathml_apply._cml_binding_time
private

◆ _cml_depends_on

CellMLToNektar.pycml.mathml_apply._cml_depends_on
private

◆ _cml_ode_has_free_var_on_rhs

CellMLToNektar.pycml.mathml_apply._cml_ode_has_free_var_on_rhs
private

Definition at line 4882 of file pycml.py.

◆ _cml_units

CellMLToNektar.pycml.mathml_apply._cml_units
private

◆ QUALIFIERS

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

Definition at line 4374 of file pycml.py.

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