3 from __future__ 
import division
 
    5 """Copyright (c) 2005-2016, University of Oxford. 
    8 University of Oxford means the Chancellor, Masters and Scholars of the 
    9 University of Oxford, having an administrative office at Wellington 
   10 Square, Oxford OX1 2JD, UK. 
   12 This file is part of Chaste. 
   14 Redistribution and use in source and binary forms, with or without 
   15 modification, are permitted provided that the following conditions are met: 
   16  * Redistributions of source code must retain the above copyright notice, 
   17    this list of conditions and the following disclaimer. 
   18  * Redistributions in binary form must reproduce the above copyright notice, 
   19    this list of conditions and the following disclaimer in the documentation 
   20    and/or other materials provided with the distribution. 
   21  * Neither the name of the University of Oxford nor the names of its 
   22    contributors may be used to endorse or promote products derived from this 
   23    software without specific prior written permission. 
   25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
   26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
   27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
   28 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
   29 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
   30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
   31 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
   32 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
   33 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
   34 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
   61 warnings.simplefilter(
'ignore', DeprecationWarning)
 
   65 from amara 
import binderytools 
as bt
 
   67 from Ft.Xml 
import SplitQName
 
   68 from xml.dom 
import Node 
 
   76 from cStringIO 
import StringIO
 
   78 from utilities 
import *
 
   81 import cellml_metadata 
 
   87     if processors 
is None:
 
   91 __version__ = 
"$Revision: 25949 $"[11:-2]
 
  100 logging.basicConfig(level=logging.CRITICAL,
 
  101                     format=
"%(name)s: %(levelname)s: %(message)s",
 
  103 logging.getLogger().handlers[0].setLevel(logging.CRITICAL)
 
  108 logging.WARNING_TRANSLATE_ERROR = logging.WARNING + 5
 
  109 logging.addLevelName(logging.WARNING_TRANSLATE_ERROR, 
'WARNING')
 
  116 NSS = {
u'm'  : 
u'http://www.w3.org/1998/Math/MathML',
 
  117        u'cml': 
u'http://www.cellml.org/cellml/1.0#',
 
  119        u'pe': 
u'https://chaste.comlab.ox.ac.uk/cellml/ns/partial-evaluation#',
 
  120        u'lut': 
u'https://chaste.comlab.ox.ac.uk/cellml/ns/lookup-tables',
 
  121        u'solver': 
u'https://chaste.comlab.ox.ac.uk/cellml/ns/solver-info',
 
  122        u'oxmeta': 
u'https://chaste.comlab.ox.ac.uk/cellml/ns/oxford-metadata#',
 
  123        u'pycml': 
u'https://chaste.comlab.ox.ac.uk/cellml/ns/pycml#',
 
  124        u'proto': 
u'https://chaste.cs.ox.ac.uk/nss/protocol/0.1#',
 
  126        u'cmeta'  : 
u"http://www.cellml.org/metadata/1.0#",
 
  127        u'rdf'    : 
u"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
 
  128        u'dc'     : 
u"http://purl.org/dc/elements/1.1/",
 
  129        u'dcterms': 
u"http://purl.org/dc/terms/",
 
  130        u'bqs'    : 
u"http://www.cellml.org/bqs/1.0#",
 
  131        u'vCard'  : 
u"http://www.w3.org/2001/vcard-rdf/3.0#",
 
  132        u'cg'     : 
u"http://www.cellml.org/metadata/graphs/1.0#",
 
  133        u'cs'     : 
u"http://www.cellml.org/metadata/simulation/1.0#",
 
  134        u'csub'   : 
u"http://www.cellml.org/metadata/custom_subset/1.0#",
 
  135        u'bqbiol' : 
u"http://biomodels.net/biology-qualifiers/",
 
  137        u'doc' : 
u"http://cellml.org/tmp-documentation" 
  142 VarTypes = 
Enum(
'Unknown', 
'Free', 
'State', 
'MaybeConstant', 
'Constant',
 
  143                 'Computed', 
'Mapped')
 
  146 CELLML_SUBSET_ELTS = frozenset(
 
  147     [
'math', 
'cn', 
'sep', 
'ci', 
'apply', 
'piecewise', 
'piece', 
'otherwise',
 
  148      'eq', 
'neq', 
'gt', 
'lt', 
'geq', 
'leq',
 
  149      'plus', 
'minus', 
'times', 
'divide', 
'power', 
'root', 
'abs',
 
  150      'exp', 
'ln', 
'log', 
'floor', 
'ceiling', 
'factorial',
 
  151      'and', 
'or', 
'not', 
'xor',
 
  152      'diff', 
'degree', 
'bvar', 
'logbase',
 
  153      'sin', 
'cos', 
'tan', 
'sec', 
'csc', 
'cot',
 
  154      'sinh', 
'cosh', 
'tanh', 
'sech', 
'csch', 
'coth',
 
  155      'arcsin', 
'arccos', 
'arctan', 
'arcsec', 
'arccsc', 
'arccot',
 
  156      'arcsinh', 
'arccosh', 
'arctanh', 
'arcsech', 
'arccsch', 
'arccoth',
 
  157      'true', 
'false', 
'notanumber', 
'pi', 
'infinity', 
'exponentiale',
 
  158      'semantics', 
'annotation', 
'annotation-xml'])
 
  161 BINDING_TIMES = 
Enum(
'static', 
'dynamic')
 
  170     Create a specialised binder, given some mappings from element names 
  171     to python classes, and setting namespace prefixes. 
  173     binder = amara.bindery.binder(prefixes=NSS)
 
  174     binder.set_binding_class(NSS[
u'cml'], 
"model", cellml_model)
 
  175     binder.set_binding_class(NSS[
u'cml'], 
"component", cellml_component)
 
  176     binder.set_binding_class(NSS[
u'cml'], 
"variable", cellml_variable)
 
  177     binder.set_binding_class(NSS[
u'cml'], 
"units", cellml_units)
 
  178     binder.set_binding_class(NSS[
u'cml'], 
"unit", cellml_unit)
 
  179     for mathml_elt 
in [
'math', 
'degree', 
'logbase', 
'otherwise',
 
  180                        'diff', 
'plus', 
'minus', 
'times', 
'divide',
 
  181                        'exp', 
'ln', 
'log', 
'abs', 
'power', 
'root',
 
  182                        'leq', 
'geq', 
'lt', 
'gt', 
'eq', 
'neq',
 
  184                        'ci', 
'cn', 
'apply', 
'piecewise', 
'piece',
 
  185                        'sin', 
'cos', 
'tan', 
'arcsin', 
'arccos', 
'arctan']:
 
  186         exec 
"binder.set_binding_class(NSS[u'm'], '%s', mathml_%s)" % (mathml_elt, mathml_elt)
 
  187     binder.set_binding_class(NSS[
u'm'], 
"and_", mathml_and)
 
  188     binder.set_binding_class(NSS[
u'm'], 
"or_", mathml_or)
 
  192     """Parse a CellML source with default rules and bindings.""" 
  194     rules = [bt.ws_strip_element_rule(
u'*')]
 
  195     return amara_parse(source, rules=rules, binderobj=binder)
 
  199     Check whether elt is safe to make a child, i.e. that it isn't 
  200     already a child elsewhere. 
  202     assert getattr(elt, 
'next_elem', 
None) 
is None 
  203     parent = getattr(elt, 
'xml_parent', 
None)
 
  205         assert elt 
not in parent.xml_children
 
  209     Base element class to allow me to set certain attributes on my instances 
  210     that are Python objects rather than unicode strings. 
  214         super(element_base, self).
__init__()
 
  218         Bypass Amara's __delattr__ for attribute names that start with _cml_ 
  220         if key.startswith(
'_cml_'):
 
  221             del self.__dict__[key]
 
  223             amara.bindery.element_base.__delattr__(self, key)
 
  227         Bypass Amara's __setattr__ for attribute names that start with _cml_ 
  229         if key.startswith(
'_cml_'):
 
  230             self.__dict__[key] = value
 
  232             amara.bindery.element_base.__setattr__(self, key, value)
 
  239         elif isinstance(self, mathml):
 
  240             raise ValueError(
'MathML element with no parent!')
 
  245         """Get the value of the cmeta:id attribute, or the empty string if not set.""" 
  246         return self.getAttributeNS(NSS[
'cmeta'], 
u'id')
 
  250         Remove child object at a given index 
  251         index - optional, 0-based index of child to remove (defaults to the last child) 
  253         obj = self.xml_children[index]
 
  254         if isinstance(obj, unicode):
 
  255             del self.xml_children[index]
 
  259             for attr, val 
in self.__dict__.items():
 
  260                 if not (attr.startswith(
'xml') 
or 
  261                         attr.startswith(
'_cml_') 
or 
  262                         attr 
in self.xml_ignore_members):
 
  263                     next = getattr(val, 
'next_elem', 
None)
 
  265                         del self.__dict__[attr]
 
  266                         if next: self.__dict__[attr] = next
 
  268                         prev, val = val, next
 
  269                         next = getattr(val, 
'next_elem', 
None)
 
  271                             prev.next_elem = next
 
  273             del self.xml_children[index]
 
  278         if hasattr(self, 
'xml_attributes'):
 
  279             msg.append(
'Object references based on XML attributes:')
 
  283                     source_phrase = 
" based on '{%s}%s' in XML"%(ns, local)
 
  285                     source_phrase = 
" based on '%s' in XML"%(local)
 
  286                 msg.append(apyname+source_phrase)
 
  287                 xml_attrs.append(apyname)
 
  288         msg.append(
'Object references based on XML child elements:')
 
  289         for attr, val 
in self.__dict__.items():
 
  290             if not (attr.startswith(
'xml') 
or 
  291                     attr.startswith(
'_cml_') 
or 
  292                     attr 
in self.xml_ignore_members):
 
  293                 if attr 
not in xml_attrs:
 
  294                     count = len(list(getattr(self, attr)))
 
  296                         count_phrase = 
" (%s element)"%count
 
  298                         count_phrase = 
" (%s elements)"%count
 
  299                     local, ns = val.localName, val.namespaceURI
 
  301                         source_phrase = 
" based on '{%s}%s' in XML"%(ns, local)
 
  303                         source_phrase = 
" based on '%s' in XML"%(local)
 
  304                     msg.append(attr+count_phrase+source_phrase)
 
  305         return u'\n'.join(msg)
 
  310         Return a dictionary whose keys are Python properties on this 
  311         object that represent XML attributes and elements, and whose vaues 
  312         are the corresponding objects (a subset of __dict__) 
  315         for attr 
in self.__dict__:
 
  316             if (
not (attr.startswith(
'xml')
 
  317                      or attr.startswith(
'_cml_')
 
  318                      or attr 
in self.xml_ignore_members)):
 
  319                 properties[attr] = self.__dict__[attr]
 
  324     def getAttributeNS(self, ns, local, default=u""):
 
  326         Get the value of an attribute specified by namespace and localname. 
  328         Optionally can also pass a default value if the attribute 
  329         doesn't exist (defaults to the empty string). 
  331         attrs = getattr(self, 
'xml_attributes', {})
 
  332         keys = [ (ns_, SplitQName(qname)[1]) 
 
  333                    for _, (qname, ns_) 
in attrs.items() ]
 
  334         values = [ unicode(getattr(self, attr))
 
  335                    for attr, (qname, ns_) 
in attrs.items() ]
 
  336         attr_dict = dict(zip(keys, values))
 
  337         return attr_dict.get((ns, local), default)
 
  339     def xml_element_children(self, elt=None):
 
  340         """Return an iterable over child elements of this element.""" 
  343         for child 
in elt.xml_children:
 
  344             if getattr(child, 
'nodeType', 
None) == Node.ELEMENT_NODE:
 
  347     def safe_remove_child(self, child, parent=None):
 
  348         """Remove a child element from parent in such a way that it can safely be added elsewhere.""" 
  349         if parent 
is None: parent = self
 
  350         parent.xml_remove_child(child)
 
  351         child.next_elem = 
None 
  353     def replace_child(self, old, new, parent=None):
 
  354         """Replace child old of parent with new.""" 
  355         if parent 
is None: parent = self
 
  356         parent.xml_insert_after(old, new)
 
  357         self.safe_remove_child(old, parent)
 
  360     for method 
in [
'getAttributeNS', 
'xml_element_children', 
'safe_remove_child', 
'replace_child']:
 
  361         meth = new.instancemethod(locals()[method], 
None, amara.bindery.element_base)
 
  362         setattr(amara.bindery.element_base, method, meth)
 
  367     """An iterable version of comment nodes.""" 
  369         amara.bindery.comment_base.__init__(self, body)
 
  379     Specialised class for the model element of a CellML document. 
  380     Adds methods for collecting and reporting validation errors, etc. 
  384         element_base.__init__(self)
 
  399         """Try to get the RDF library to clean up nicely.""" 
  400         cellml_metadata.remove_model(self)
 
  403         """Get the configuration store if it exists, or an attribute thereof.""" 
  404         config = getattr(self.xml_parent, 
'_cml_config', 
None)
 
  406             config = getattr(config, config_attr, 
None)
 
  410         """Get the value of a command-line option.""" 
  411         config = getattr(self.xml_parent, 
'_cml_config', 
None)
 
  412         return config 
and getattr(config.options, option_name)
 
  415         """Return the component object that has name `compname'.""" 
  420         Return the variable object with name `varname' in component 
  427                 if self.component.ignore_component_name:
 
  428                     compname = self.component.name
 
  431                         compname, varname = cellml_variable.split_name(varname)
 
  441         Get the unique variable in this model with the given Oxford metadata 
  444         If throw is True, will raise ValueError if there is no such variable, 
  445         or more than 1 match.  If throw is False, returns None in these cases. 
  447         vars = cellml_metadata.find_variables(self,
 
  448                                              (
'bqbiol:is', NSS[
'bqbiol']),
 
  449                                              (
'oxmeta:'+str(name), NSS[
'oxmeta']))
 
  453             raise ValueError(
'"%s" does not name a unique variable (matches: %s)' 
  460         """Return a list of variables annotated with the given ontology term. 
  462         The annotations have the same form as for oxmeta name annotations (see 
  463         get_variable_by_oxmeta_name).  However, here we are not restricted to 
  464         namespace, and no check is done on the number of results returned. 
  466         The given term must be a (prefixed_name, nsuri) tuple. 
  468         assert isinstance(term, tuple)
 
  469         assert len(term) == 2
 
  470         named_vars = cellml_metadata.find_variables(self, (
'bqbiol:is', NSS[
'bqbiol']), term)
 
  471         category_vars = cellml_metadata.find_variables(self, (
'bqbiol:isVersionOf', NSS[
'bqbiol']), term)
 
  472         return named_vars + category_vars
 
  476         Get the unique variable in this model with the given cmeta:id attribute value. 
  478         vars = self.xml_xpath(
u'cml:component/cml:variable[@cmeta:id="%s"]' % cmeta_id)
 
  480             raise ValueError(
'"%s" does not ID a unique variable (matches: %s)' 
  481                              % (cmeta_id, str(vars)))
 
  485         """Return an iterator over the variables in the model.""" 
  486         for comp 
in getattr(self, 
u'component', []):
 
  487             for var 
in getattr(comp, 
u'variable', []):
 
  491         """Add a new variable to the model.""" 
  496         """Remove a variable from the model.""" 
  500         """Add a new component to the model.""" 
  502             comp.xml_parent = self
 
  504             self.xml_append(comp)
 
  508         """Remove the given component from the model.""" 
  509         self.xml_remove_child(comp)
 
  513         """Log a validation error message. 
  515         Message should be a unicode string. 
  517         self._cml_validation_errors.append(errmsg)
 
  518         logging.getLogger(
'validator').log(level, errmsg.encode(
'UTF-8'))
 
  520         """Return the list of all errors found (so far) while validating this model.""" 
  523         """Log a validation warning message. 
  525         Message should be a unicode string. 
  527         self._cml_validation_warnings.append(errmsg)
 
  528         logging.getLogger(
'validator').log(level, errmsg.encode(
'UTF-8'))
 
  530         """Return the list of all warnings found (so far) while validating this model. 
  534         """Report an exception e as a validation error or warning. 
  536         If show_xml_context is True, display the XML of the context 
  537         of the exception as well. 
  539         e.show_xml_context = show_xml_context
 
  545     def validate(self, xml_context=False,
 
  546                  invalid_if_warnings=
False,
 
  547                  warn_on_units_errors=
False,
 
  548                  check_for_units_conversions=
False,
 
  549                  assume_valid=
False, **ignored_kwargs):
 
  550         """Validate this model. 
  552         Assumes that RELAX NG validation has been done.  Checks rules 
  553         3.4.2.2, 3.4.3.2, 3.4.3.3, 3.4.5.2, 3.4.5.3, 3.4.5.4, 3.4.6.2, 3.4.6.3, 3.4.6.4, 
  554         4.4.2.1, 4.4.3.2, 4.4.4, 5.4.1.2, 5.4.2.2, 6.4.2.5, 6.4.3.2, and 6.4.3.3 
  555         in the CellML 1.0 spec, and performs units checking. 
  557         Note that if some checks fail, most of the remaining checks 
  558         will not be performed.  Hence when testing a model validate 
  559         repeatedly until it passes. 
  561         If xml_context is True, then the failing MathML tree will be 
  562         displayed with every units error. 
  564         If check_for_units_conversions is True, then generate a warning if 
  565         units conversions will be needed. 
  567         If assume_valid is True then fewer checks will be done - only 
  568         what is required to set up the data structures needed for model 
  571         Returns True iff the model validates. 
  572         When invalid_if_warnings is True the model will fail to validate 
  573         if there are any warnings, as well as if there are any errors. 
  582             DEBUG(
'validator', 
'Checked for units cycles')
 
  595                 for expr 
in assignment_exprs:
 
  619                                                 warn_on_units_errors,
 
  620                                                 check_for_units_conversions)
 
  623         unknown_nss = set(self.rootNode.xml_namespaces.keys()).difference(set(NSS.values()))
 
  626                                     u'\n  '.join(list(unknown_nss)))
 
  633         """Check Rule 6.4.3.2 (4): hierarchies must not be circular. 
  635         Builds all the hierarchies, and checks for cycles. 
  636         In the process, we also check the other rules in 6.4.3, and 6.4.2.5. 
  641         for group 
in getattr(self, 
u'group', []):
 
  643             for rel 
in getattr(group, 
u'relationship_ref', []):
 
  645                 reln = rel.relationship
 
  646                 ns = rel.xml_attributes[
u'relationship'][1]
 
  647                 name = getattr(rel, 
u'name', 
None)
 
  648                 hier = (reln, ns, name)
 
  649                 if hier 
in local_hiers:
 
  650                     self.
validation_error(
"A group element must not contain two or more relationship_ref" 
  651                                           " elements that define a relationship attribute in a common" 
  652                                           " namespace with the same value and that have the same name" 
  653                                           " attribute value (which may be non-existent) (6.4.2.5)." 
  654                                           " Relationship '%s' name '%s' in namespace '%s' is repeated." 
  655                                           % (reln, name 
or '', ns))
 
  656                 local_hiers.add(hier)
 
  661         DEBUG(
'validator', 
'Checked component hierachies')
 
  664         """Check Rules 3.4.{5,6}: check variable mappings and interfaces are sane.""" 
  667         connected_components = set()
 
  668         for connection 
in getattr(self, 
u'connection', []):
 
  669             comps = frozenset([connection.map_components.component_1, connection.map_components.component_2])
 
  670             if comps 
in connected_components:
 
  671                 self.
validation_error(
"Each map_components element must map a unique pair of components " 
  672                                       "(3.4.5.4). The pair ('%s', '%s') is repeated." % tuple(comps))
 
  673             connected_components.add(comps)
 
  676         for comp 
in getattr(self, 
u'component', []):
 
  677             for var 
in getattr(comp, 
u'variable', []):
 
  678                 for iface 
in [
u'private_interface', 
u'public_interface']:
 
  679                     if getattr(var, iface, 
u'none') == 
u'in':
 
  681                             var.get_source_variable()
 
  685                                                   "but no component exports a value to that variable." 
  686                                                   % (var.fullname(), iface))
 
  687         DEBUG(
'validator', 
'Checked variable mappings')
 
  690         """Validate the given connection element. 
  692         Check that the given connection object defines valid mappings 
  693         between variables, according to rules 3.4.5 and 3.4.6. 
  699             self.
validation_error(
"Connections must be between components defined in the current model " 
  700                                   "(3.4.5.2). There is no component '%s'." % conn.map_components.component_1)
 
  705             self.
validation_error(
"Connections must be between components defined in the current model " 
  706                                   "(3.4.5.3). There is no component '%s'." % conn.map_components.component_2)
 
  709             self.
validation_error(
"A connection must link two different components (3.4.5.4). " 
  710                                   "The component '%s' is being connected to itself." % comp1.name)
 
  713         par1, par2 = comp1.parent(), comp2.parent()
 
  715         if not (par1 == comp2 
or par2 == comp1 
or par1 == par2):
 
  717                 'Connections are only permissible between sibling',
 
  718                 'components, or where one is the parent of the other.\n',
 
  719                 comp1.name,
'and',comp2.name,
'are unrelated.']))
 
  722         for mapping 
in conn.map_variables:
 
  726                 self.
validation_error(
"A variable mapping must be between existing variables (3.4.6.2). " 
  727                                       "Variable '%s' doesn't exist in component '%s'." 
  728                                        % (mapping.variable_1, comp1.name))
 
  733                 self.
validation_error(
"A variable mapping must be between existing variables (3.4.6.2). " 
  734                                       "Variable '%s' doesn't exist in component '%s'." 
  735                                        % (mapping.variable_2, comp2.name))
 
  737             errm, e = [
'Interface mismatch mapping',var1.fullname(),
'and',var2.fullname(),
':\n'], 
None 
  740                 if not hasattr(var1, 
'public_interface'):
 
  741                     e = 
'missing public_interface attribute on ' + \
 
  742                         var1.fullname() + 
'.' 
  743                 elif not hasattr(var2, 
'public_interface'):
 
  744                     e = 
'missing public_interface attribute on ' + \
 
  745                         var2.fullname() + 
'.' 
  746                 elif var1.public_interface == var2.public_interface:
 
  747                     e = 
'public_interface attributes are identical.' 
  749                     if var1.public_interface == 
'in':
 
  750                         var1._set_source_variable(var2)
 
  752                         var2._set_source_variable(var1)
 
  756                     var1, var2 = var2, var1
 
  758                 if not hasattr(var1, 
'public_interface'):
 
  759                     e = var1.fullname()+
' missing public_interface.' 
  760                 elif not hasattr(var2, 
'private_interface'):
 
  761                     e = var2.fullname()+
' missing private_interface.' 
  762                 elif var1.public_interface == var2.private_interface:
 
  763                     e = 
'relevant interfaces have identical values.' 
  765                     if var1.public_interface == 
'in':
 
  766                         var1._set_source_variable(var2)
 
  768                         var2._set_source_variable(var1)
 
  775         """Check rule 3.4.3.3: that the units declared for variables exist.""" 
  780                 self.
validation_error(
"The value of the units attribute on a variable must be either " 
  781                                       "one of the standard units or the name of a unit defined in the " 
  782                                       "current component or model (3.4.3.3). The units '%s' on the " 
  783                                       "variable '%s' in component '%s' do not exist." 
  784                                       % (var.units, var.name, var.component.name))
 
  787         """Check that the units of mapped variables are dimensionally consistent. 
  789         If check_for_units_conversions is True we also warn if they are not equivalent, 
  790         since much processing software may not be able to handle that case. 
  792         for conn 
in getattr(self, 
u'connection', []):
 
  795             for mapping 
in conn.map_variables:
 
  799                 u1 = var1.get_units()
 
  800                 u2 = var2.get_units()
 
  802                     if not u1.dimensionally_equivalent(u2):
 
  804                             var1.fullname(), 
'and', var2.fullname(),
 
  805                             'are mapped, but have dimensionally inconsistent units.']))
 
  806                     elif check_for_units_conversions:
 
  808                             u' '.join([
'Warning: mapping between', var1.fullname(), 
'and',
 
  809                                        var2.fullname(), 
'will require a units conversion.']),
 
  810                             level=logging.WARNING_TRANSLATE_ERROR)
 
  813         """Check rules 4.4.2.1 and 4.4.3.2: name references in mathematics.""" 
  814         if isinstance(expr, mathml_ci):
 
  821                                    "The content of a MathML ci element must match the name of a variable " 
  822                                    "in the enclosing component, once whitespace normalisation has been " 
  823                                    "performed (4.4.2.1). Variable '%s' does not exist in component '%s'." 
  824                                    % (unicode(expr).strip(), expr.component.name)),
 
  826         elif isinstance(expr, mathml_cn):
 
  833                                    "Units on a cn element must be standard or defined in the current " 
  834                                    "component or model (4.4.3.2). Units '%s' are not defined in " 
  835                                    "component '%s'." % (expr.units, expr.component.name)),
 
  839             for child 
in expr.xml_element_children():
 
  843         """Check Rule 4.4.4: mathematical expressions may only modify 
  844         variables belonging to the current component. 
  846         for expr 
in assignments:
 
  848                 expr.check_assigned_var()
 
  849             except MathsError, e:
 
  851         DEBUG(
'validator', 
'Checked variable assignments')
 
  854         """Warn if MathML outside the CellML subset is used.""" 
  855         for elt 
in math_elts:
 
  856             if not elt.localName 
in CELLML_SUBSET_ELTS 
and \
 
  857                    elt.namespaceURI == NSS[
u'm']:
 
  859                     u'MathML element', elt.localName,
 
  860                     u'is not in the CellML subset.',
 
  861                     u'Some tools may not be able to process it.']))
 
  864             DEBUG(
'validator', 
'Checked for CellML subset')
 
  867         """Determine the type of each variable. 
  869         Note that mapped vars must have already been classified by 
  870         self._check_variable_mappings, and the RELAX NG schema ensures 
  871         that a variable cannot be both Mapped and MaybeConstant. 
  873         Builds the equation dependency graph in the process. 
  878             if hasattr(var, 
u'initial_value'):
 
  879                 var._set_type(VarTypes.MaybeConstant)
 
  882         for expr 
in assignment_exprs:
 
  884                 expr.classify_variables(root=
True)
 
  885             except MathsError, e:
 
  889             if var.get_type() == VarTypes.MaybeConstant:
 
  890                 var._set_type(VarTypes.Constant)
 
  891         DEBUG(
'validator', 
'Classified variables')
 
  894         """Topologically sort the equation dependency graph. 
  896         This orders all the assignment expressions in the model, to 
  897         allow procedural code generation.  It also checks that equations 
  898         are not cyclic (we don't support DAEs). 
  904                 if var.get_colour() == DFS.White:
 
  906             for expr 
in assignment_exprs:
 
  907                 if expr.get_colour() == DFS.White:
 
  909         except MathsError, e:
 
  911         DEBUG(
'validator', 
'Topologically sorted variables')
 
  913     math_xpath_1 = 
u'cml:component/m:math' 
  914     math_xpath_2 = 
u'cml:component/cml:reaction/cml:variable_ref/cml:role/m:math' 
  915     apply_xpath_1 = 
u'/m:apply[m:eq]' 
  916     apply_xpath_2 = 
u'/m:semantics/m:apply[m:eq]' 
  919         """Search for assignment expressions in the model's mathematics. 
  921         If comp is supplied, will only return assignments in that component. 
  928             assignments_xpath = assignments_xpath.replace(
u'component',
 
  929                                                           u'component[@name="%s"]' % comp.name)
 
  930         return self.xml_xpath(assignments_xpath)
 
  934         Create dictionaries mapping names of variables and components to 
  935         the objects representing them. 
  936         Dictionary keys for variables will be 
  937           (component_name, variable_name). 
  939         If rebuild is True, clear the dictionaries first. 
  945             for comp 
in getattr(self, 
u'component', []):
 
  947                     self.
validation_error(
"Component names must be unique within a model (3.4.2.2)." 
  948                                           " The name '%s' is repeated." % comp.name)
 
  950                 for var 
in getattr(comp, 
u'variable', []):
 
  951                     key = (comp.name, var.name)
 
  953                         self.
validation_error(
"Variable names must be unique within a component (3.4.3.2)." 
  954                                               " The name '%s' is repeated in component '%s'." 
  955                                               % (var.name, comp.name))
 
  960         Create all the parent-child links for the given component hierarchy. 
  962         relationship gives the type of the hierarchy. If it is not one of the 
  963         CellML types (i.e. encapsulation or containment) then the namespace URI 
  964         must be specified.  Multiple non-encapsulation hierarchies of the same 
  965         type can be specified by giving the name argument. 
  967         key = (relationship, namespace, name)
 
  969         for comp 
in getattr(self, 
u'component', []):
 
  970             comp._clear_hierarchy(key)
 
  974             rels = self.xml_xpath(
u'cml:group/cml:relationship_ref')
 
  980             if hasattr(rel, 
u'relationship_'):
 
  982                     'relationship_ref element has multiple relationship',
 
  983                     'attributes in different namespaces:\n'] + 
 
  984                         map(
lambda qn,ns: 
'('+qn+
','+ns+
')',
 
  985                             rel.xml_attributes.values())))
 
  987             if rel.relationship == relationship 
and \
 
  988                rel.xml_attributes[
u'relationship'][1] == namespace 
and \
 
  989                getattr(rel, 
u'name', 
None) == name:
 
  991                 groups.append(rel.xml_parent)
 
  993         def set_parent(p, crefs):
 
  999                     self.
validation_error(
"A component_ref element must reference a component in the current" 
 1000                                           " model (6.4.3.3). Component '%s' does not exist." % cref.component)
 
 1003                 c._set_parent_component(key, p)
 
 1004                 if hasattr(cref, 
'component_ref'):
 
 1006                     if c._has_child_components(key):
 
 1007                         self.
validation_error(
"In a given hierarchy, only one component_ref element " 
 1008                                               "referencing a given component may contain children (6.4.3.2)." 
 1009                                               " Component '%s' has children in multiple locations." % c.name)
 
 1011                     set_parent(c, cref.component_ref)
 
 1012                 elif p 
is None and namespace 
is None:
 
 1013                     self.
validation_error(
"Containment and encapsulation relationships must be hierarchical" 
 1014                                           " (6.4.3.2). Potentially top-level component '%s' has not been" 
 1015                                           " given children." % cref.component)
 
 1016         for group 
in groups:
 
 1017             set_parent(
None, group.component_ref)
 
 1025         def has_cycle(root_comp, cur_comp):
 
 1026             if cur_comp 
is None:
 
 1028             elif cur_comp 
is root_comp:
 
 1031                 return has_cycle(root_comp, cur_comp.parent(reln_key=key))
 
 1032         for group 
in groups:
 
 1033             for cref 
in group.component_ref:
 
 1036                 if has_cycle(c, c.parent(reln_key = key)):
 
 1037                     n, ns = name 
or "", namespace 
or "" 
 1038                     self.
validation_error(
"The '%s' relationship hierarchy with name '%s' and namespace" 
 1039                                           " '%s' has a cycle" % (relationship, n, ns))
 
 1044         Do a topological sort of all assignment expressions and variables 
 1047         node should be an expression or variable object that inherits from 
 1048         Colourable and has methods get_dependencies, get_component 
 1050         node.set_colour(DFS.Gray)
 
 1052         if isinstance(node, cellml_variable):
 
 1053             self._cml_sorting_variables_stack.append(node.fullname())
 
 1057             n1, n2 = map(
lambda v: v.fullname(), node.assigned_variable())
 
 1058             self._cml_sorting_variables_stack.append(
u'd'+n1+
u'/d'+n2)
 
 1060         for dep 
in node.get_dependencies():
 
 1061             if type(dep) == types.TupleType:
 
 1063                 dep = dep[0].get_ode_dependency(dep[1], node)
 
 1064             if dep.get_colour() == DFS.White:
 
 1066             elif dep.get_colour() == DFS.Gray:
 
 1068                 if isinstance(dep, cellml_variable):
 
 1069                     i = self._cml_sorting_variables_stack.index(dep.fullname())
 
 1071                     n1, n2 = map(
lambda v: v.fullname(),
 
 1072                                  dep.assigned_variable())
 
 1073                     i = self._cml_sorting_variables_stack.index(
 
 1085                     u'There is a cyclic dependency involving the following',
 
 1086                     u'variables:', 
u','.join(varnames)]))
 
 1088         node.set_colour(DFS.Black)
 
 1091         if (isinstance(node, cellml_variable) 
or node.is_ode()):
 
 1092             self._cml_sorting_variables_stack.pop()
 
 1097         During the topological sort, add a finished assignment to the 
 1098         list.  This list can then be executed in order to simulate the 
 1101         The element added can either be a MathML expression 
 1102         representing an assignment, or a CellML variable element, 
 1103         indicating an assignment due to a variable mapping. 
 1105         self._cml_assignments.append(a)
 
 1107         """Remove the given assignment from our list. 
 1109         This method is used by the partial evaluator.""" 
 1110         self._cml_assignments.remove(a)
 
 1113         Return a sorted list of all the assignments in the model. 
 1115         Assignments can either be instances of cellml_variable, in 
 1116         which case they represent a variable mapping, or instances of 
 1117         mathml_apply, representing top-level assignment expressions. 
 1121         """Clear the assignments list.""" 
 1125         """Perform a binding time analysis on the model's mathematics. 
 1127         This requires variables to have been classified and a 
 1128         topological sort of the mathematics to have been performed. 
 1130         Variables and top-level expressions are processed in the order 
 1131         given by the topological sort, hence only a single pass is 
 1134         Variables are classified based on their type: 
 1135           State, Free -> dynamic 
 1137           Mapped -> binding time of source variable 
 1138           Computed -> binding time of defining expression 
 1140         Expressions are dealt with by recursively annotating 
 1141         subexpressions.  See code in the MathML classes for details. 
 1144             if isinstance(item, cellml_variable):
 
 1146                 item._get_binding_time()
 
 1149                 item._get_binding_time()
 
 1153                                        warn_on_units_errors=
False,
 
 1154                                        check_for_units_conversions=
False):
 
 1155         """Appendix C.3.6: Equation dimension checking.""" 
 1158         for expr 
in assignment_exprs:
 
 1161             except UnitsError, e:
 
 1162                 if warn_on_units_errors:
 
 1164                     e.level = logging.WARNING
 
 1169             for expr 
in assignment_exprs:
 
 1172                     expr._set_in_units(boolean, no_act=
True)
 
 1178                                         level=logging.WARNING)
 
 1179         DEBUG(
'validator', 
'Checked units')
 
 1182         """Check for cyclic units definitions. 
 1184         We do this by doing a depth-first search from unit. 
 1186         if unit.get_colour() != DFS.White:
 
 1189         unit.set_colour(DFS.Gray)
 
 1191         parent = unit.xml_parent 
or self
 
 1192         for u 
in getattr(unit, 
u'unit', []):
 
 1195                 v = parent.get_units_by_name(u.units)
 
 1197                 self.
validation_error(
"The value of the units attribute on a unit element must be taken" 
 1198                                       " from the dictionary of standard units or be the name of a" 
 1199                                       " user-defined unit in the current component or model (5.4.2.2)." 
 1200                                       " Units '%s' are not defined." % u.units)
 
 1202             if v.get_colour() == DFS.White:
 
 1204             elif v.get_colour() == DFS.Gray:
 
 1207                                       % (unit.name, v.name))
 
 1208         unit.set_colour(DFS.Black)
 
 1211         """Create a dictionary mapping units names to objects, for all units definitions in this element.""" 
 1214         def make(name, bases):
 
 1215             return cellml_units.create_new(self, name, bases, standard=
True)
 
 1217         base_units = [
u'ampere', 
u'candela',  
u'dimensionless', 
u'kelvin',
 
 1218                       u'kilogram', 
u'metre', 
u'mole', 
u'second']
 
 1219         base_units.append(
u'#FUDGE#') 
 
 1220         for units 
in base_units:
 
 1221             std_units[units] = make(units, [])
 
 1223         boolean = make(
u'cellml:boolean', [])
 
 1224         std_units[
u'cellml:boolean'] = boolean
 
 1226         gram = make(
'gram', [{
'units': 
'kilogram', 
'multiplier': 
'0.001'}])
 
 1227         litre = make(
'litre', [{
'multiplier': 
'1000', 
'prefix': 
'centi',
 
 1228                                 'units': 
'metre', 
'exponent': 
'3'}])
 
 1230         radian = make(
'radian', [{
'units': 
'metre'},
 
 1231                                  {
'units': 
'metre', 
'exponent': 
'-1'}])
 
 1232         steradian = make(
'steradian', [{
'units': 
'metre', 
'exponent': 
'2'},
 
 1233                                        {
'units': 
'metre', 
'exponent': 
'-2'}])
 
 1234         hertz = make(
'hertz', [{
'units': 
'second', 
'exponent': 
'-1'}])
 
 1235         newton = make(
'newton', [{
'units': 
'metre'},
 
 1236                                  {
'units': 
'kilogram'},
 
 1237                                  {
'units': 
'second', 
'exponent': 
'-2'}])
 
 1238         pascal = make(
'pascal', [{
'units': 
'newton'},
 
 1239                                  {
'units': 
'metre', 
'exponent': 
'-2'}])
 
 1240         joule = make(
'joule', [{
'units': 
'newton'},
 
 1241                                {
'units': 
'metre'}])
 
 1242         watt = make(
'watt', [{
'units': 
'joule'},
 
 1243                              {
'units': 
'second', 
'exponent': 
'-1'}])
 
 1244         coulomb = make(
'coulomb', [{
'units': 
'second'},
 
 1245                                    {
'units': 
'ampere'}])
 
 1246         volt = make(
'volt', [{
'units': 
'watt'},
 
 1247                              {
'units': 
'ampere', 
'exponent': 
'-1'}])
 
 1248         farad = make(
'farad', [{
'units': 
'coulomb'},
 
 1249                                {
'units': 
'volt', 
'exponent': 
'-1'}])
 
 1250         ohm = make(
'ohm', [{
'units': 
'volt'},
 
 1251                            {
'units': 
'ampere', 
'exponent': 
'-1'}])
 
 1252         siemens = make(
'siemens', [{
'units': 
'ampere'},
 
 1253                                    {
'units': 
'volt', 
'exponent': 
'-1'}])
 
 1254         weber = make(
'weber', [{
'units': 
'volt'},
 
 1255                                {
'units': 
'second'}])
 
 1256         tesla = make(
'tesla', [{
'units': 
'weber'},
 
 1257                                {
'units': 
'metre', 
'exponent': 
'-2'}])
 
 1258         henry = make(
'henry', [{
'units': 
'weber'},
 
 1259                                {
'units': 
'ampere', 
'exponent': 
'-1'}])
 
 1260         celsius = make(
'celsius', [{
'units': 
'kelvin', 
'offset': 
'-273.15'}])
 
 1261         lumen = make(
'lumen', [{
'units': 
'candela'},
 
 1262                                {
'units': 
'steradian'}])
 
 1263         lux = make(
'lux', [{
'units': 
'lumen'},
 
 1264                            {
'units': 
'metre', 
'exponent': 
'-2'}])
 
 1265         becquerel = make(
'becquerel', [{
'units': 
'second', 
'exponent': 
'-1'}])
 
 1266         gray = make(
'gray', [{
'units': 
'joule'},
 
 1267                              {
'units': 
'kilogram', 
'exponent': 
'-1'}])
 
 1268         sievert = make(
'sievert', [{
'units': 
'joule'},
 
 1269                                    {
'units': 
'kilogram', 
'exponent': 
'-1'}])
 
 1270         katal = make(
'katal', [{
'units': 
'second', 
'exponent': 
'-1'},
 
 1272         for units 
in [becquerel, celsius, coulomb, farad, gram, gray, henry,
 
 1273                       hertz, joule, katal, litre, lumen, lux, newton, ohm,
 
 1274                       pascal, radian, siemens, sievert, steradian, tesla,
 
 1276             std_units[units.name] = units
 
 1278         std_units[
u'meter'] = std_units[
u'metre']
 
 1279         std_units[
u'liter'] = std_units[
u'litre']
 
 1282         model_units.update(std_units)
 
 1283         if hasattr(self, 
u'units'):
 
 1284             for units 
in self.units:
 
 1285                 if units.name 
in model_units:
 
 1286                     self.
validation_error(
"Units names must be unique within the parent component or model," 
 1287                                           " and must not redefine the standard units (5.4.1.2)." 
 1288                                           " The units definition named '%s' in the model is a duplicate." % units.name)
 
 1289                 model_units[units.name] = units
 
 1291         for u 
in model_units.itervalues():
 
 1295         """Get a dictionary mapping the names of the standard CellML units to their definitions.""" 
 1301         """Get a list of all units objects, including the standard units.""" 
 1304         units = self._cml_units.values()
 
 1305         for comp 
in getattr(self, 
u'component', []):
 
 1306             units.extend(comp.get_all_units())
 
 1310         """Return an object representing the element that defines the units named `uname'.""" 
 1316             raise KeyError(
"Units '%s' are not defined in the current component or model." % uname)
 
 1319         """Add an entry in our units dictionary for units named `name' with element object `units'.""" 
 1328         """Test whether a given units definition appears in the model.""" 
 1329         return units 
in self._cml_units.itervalues()
 
 1332         """Add a units object into the global hashmap.""" 
 1338         """Unique-ify this units object. 
 1340         If an object with the same definition already exists, return that. 
 1341         Otherwise return the given units object. 
 1343         'Same definition' is based on the cellml_units.uniquify_tuple 
 1344         property, which in turn is based partly on the generated name 
 1345         which would be given to these units, since that really *must* 
 1346         be unique in generated models. 
 1348         return self._cml_units_map.get(units.uniquify_tuple, units)
 
 1351         """Have these units been generated already? 
 1353         i.e. is a units object with this definition in our map? 
 1358         """Add explicit units conversion mathematics where necessary.""" 
 1363         """Return a list of the state variable elements in this model.""" 
 1366             if var.get_type() == VarTypes.State:
 
 1367                 state_vars.append(var)
 
 1371         """Return a list of the free variable elements in this model.""" 
 1374             if var.get_type() == VarTypes.Free:
 
 1375                 free_vars.append(var)
 
 1380                                         state_vars_depend_on_odes=
False,
 
 1381                                         state_vars_examined=set()):
 
 1382         """Calculate the extended dependencies of the given nodes. 
 1384         Recurse into the dependency graph, in order to construct a 
 1385         set, for each node in nodes, of all the nodes on which it 
 1386         depends, either directly or indirectly. 
 1388         Each node IS included in its own dependency set. 
 1390         If prune is specified, it should be a set of nodes for which 
 1391         we won't include their dependencies or the nodes themselves. 
 1392         This is useful e.g. for pruning variables required for calculating 
 1393         a stimulus if the stimulus is being provided by another method. 
 1394         prune_deps is similar: dependencies of these nodes will be excluded, 
 1395         but the nodes themselves will be included if asked for. 
 1397         If state_vars_depend_on_odes is True, then considers state variables 
 1398         to depend on the ODE defining them. 
 1400         Requires the dependency graph to be acyclic. 
 1402         Return the union of all the dependency sets. 
 1406             if node 
in prune 
or (isinstance(node, mathml_apply) 
and 
 1407                                  isinstance(node.operator(), mathml_eq) 
and 
 1408                                  isinstance(node.eq.lhs, mathml_ci) 
and 
 1409                                  node.eq.lhs.variable 
in prune):
 
 1411             if type(node) == tuple:
 
 1415                 node = node[0].get_ode_dependency(node[1])
 
 1416                 if orig_node 
in prune_deps:
 
 1420                 free_var = node.eq.lhs.diff.independent_variable
 
 1424             if node 
in prune_deps:
 
 1427             nodedeps = set(node.get_dependencies())
 
 1428             if ode 
and not node._cml_ode_has_free_var_on_rhs:
 
 1433                 nodedeps.remove(free_var)
 
 1434             if (state_vars_depend_on_odes 
and isinstance(node, cellml_variable)
 
 1435                 and node.get_type() == VarTypes.State
 
 1436                 and node 
not in state_vars_examined):
 
 1437                 nodedeps.update(node.get_all_expr_dependencies())
 
 1438                 state_vars_examined.add(node)
 
 1441                                                              prune_deps=prune_deps,
 
 1442                                                              state_vars_depend_on_odes=state_vars_depend_on_odes,
 
 1443                                                              state_vars_examined=state_vars_examined))
 
 1447         """Determine whether this model is self-excitatory, 
 1448         i.e. does not require an external stimulus. 
 1453         property = cellml_metadata.create_rdf_node((
'pycml:is-self-excitatory', NSS[
'pycml']))
 
 1454         source = cellml_metadata.create_rdf_node(fragment_id=meta_id)
 
 1455         return cellml_metadata.get_target(self, source, property) == 
'yes' 
 1457     def xml(self, stream=None, writer=None, **wargs):
 
 1458         """Serialize back to XML. 
 1459         If stream is given, output to stream. 
 1460         If writer is given, use it directly. 
 1461         If neither a stream nor a writer is given, return the output text 
 1462         as a Python string (not Unicode) encoded as UTF-8. 
 1464         This overrides Amara's method, in order to force declaration of 
 1465         various namespaces with prefixes on this element, and to ensure 
 1466         the RDF annotations are up-to-date. 
 1468         See base class docs for possible keyword arguments. 
 1470         extra_namespaces = {
u'cellml': NSS[
u'cml'],
 
 1472                             u'lut': NSS[
u'lut']}
 
 1475         cellml_metadata.update_serialized_rdf(self)
 
 1481             if not wargs.get(
'omitXmlDeclaration'):
 
 1482                 wargs[
'omitXmlDeclaration'] = 
u'yes' 
 1484                 writer = amara.bindery.create_writer(stream, wargs)
 
 1486                 temp_stream = StringIO()
 
 1487                 writer = amara.bindery.create_writer(temp_stream, wargs)
 
 1489             writer.startDocument()
 
 1491         writer.startElement(self.nodeName, self.namespaceURI,
 
 1492                             extraNss=extra_namespaces)
 
 1493         if hasattr(self, 
'xml_attributes'):
 
 1496                 val = self.__dict__[apyname]
 
 1497                 writer.attribute(aqname, val, ans)
 
 1498         for child 
in self.xml_children:
 
 1499             if isinstance(child, unicode):
 
 1502                 child.xml(writer=writer)
 
 1503         writer.endElement(self.nodeName, self.namespaceURI)
 
 1505             writer.endDocument()
 
 1506         return temp_stream 
and temp_stream.getvalue()
 
 1511     Specialised component class, with additional helper methods. 
 1515         element_base.__init__(self)
 
 1523         """Whether to not include the component name in the full names of contained variables.""" 
 1527     def parent(self, relationship=u'encapsulation', namespace=None, name=None, reln_key=None):
 
 1528         """Find the parent of this component in the given hierarchy. 
 1530         We default to the encapsulation hierarchy. 
 1532         relationship gives the type of the hierarchy. If it is not one 
 1533         of the CellML types (i.e. encapsulation or containment) then 
 1534         the namespace URI must be specified.  Multiple non-encapsulation 
 1535         hierarchies of the same type can be specified by giving the name 
 1538         Results are cached for efficiency. 
 1540         key = reln_key 
or (relationship, namespace, name)
 
 1542             assert(reln_key 
is None)
 
 1543             self.xml_parent.build_component_hierarchy(relationship, namespace, name)
 
 1547         """Unset our parent & children in the given hierarchy.""" 
 1552         """Set the parent of this component in the relationship hierarchy indexed by reln_key to parent. 
 1553         Trigger a validation error if we already have a parent in this hierarchy. 
 1554         Also add ourselves to parent's children. 
 1560             self.xml_parent.validation_error(
"In a given hierarchy, a component may not be a child more" 
 1561                                              " than once (6.4.3.2). Component '%s' has multiple parents." 
 1563         if not parent 
is None:
 
 1564             parent._add_child_component(reln_key, self)
 
 1567         """Add child to our list of children in the relationship hierarchy indexed by reln_key.""" 
 1573         """Determine whether we have any children in the given relationship hierarchy.""" 
 1574         return self._cml_children.get(reln_key, []) != []
 
 1578         """Create a dictionary mapping units names to objects, for all units definitions in this element.""" 
 1580         for units 
in getattr(self, 
u'units', []):
 
 1582                 self.validation_error(
"Units names must be unique within the parent component (5.4.1.2)." 
 1583                                       " The name '%s' in component '%s' is duplicated." 
 1584                                       % (units.name, self.
name))
 
 1586                 if self.xml_parent.get_units_by_name(units.name).standard == 
u'yes':
 
 1587                     self.validation_error(
"Units definitions must not redefine the standard units (5.4.1.2)." 
 1588                                           " The name '%s' in component '%s' is not allowed." 
 1589                                           % (units.name, self.
name))
 
 1593             self.xml_parent._add_units_obj(units)
 
 1595         """Return an object representing the element that defines the units named `uname'.""" 
 1603             return self.xml_parent.get_units_by_name(uname)
 
 1605         """Add an entry in our units dictionary for units named `name' with element object `units'.""" 
 1609         self.xml_parent._add_units_obj(units)
 
 1613         """Get a list of all units objects defined in this component.""" 
 1616         return self._cml_units.values()
 
 1619         """Return the variable object with name `varname' in this component.""" 
 1620         return self.xml_parent.get_variable_by_name(self.
name, varname)
 
 1623         """Add a variable to this component.""" 
 1625         self.xml_append(var)
 
 1627         self.xml_parent._add_variable(var, var.name, self.
name)
 
 1630         """Remove a variable from this component.""" 
 1631         if not keep_annotations:
 
 1633             var.remove_rdf_annotations()
 
 1635         self.xml_remove_child(var)
 
 1637         self.xml_parent._del_variable(var.name, self.
name)
 
 1642         """Create a new component with the given name.""" 
 1643         new_comp = elt.xml_create_element(
u'component', NSS[
u'cml'],
 
 1644                                           attributes={
u'name': unicode(name)})
 
 1650     Class representing CellML <variable> elements. 
 1653         super(cellml_variable, self).
__init__()
 
 1658         """Clear the type, dependency, etc. information for this variable. 
 1660         This allows us to re-run the type & dependency analysis for the model. 
 1674         """Hashing function for variables. 
 1676         Hash is based on hashing the full name of the variable, as 
 1677         this must be unique within a model.  Unfortunately, when we do 
 1678         partial evaluation, the full name changes! 
 1680         TODO: do we need a hash function? 
 1682         return hash(self.
fullname(cellml=
True))
 
 1686         Return the full name of this variable, i.e. 
 1687         '(component_name,variable_name)'. 
 1689         If cellml is given as True, return the name in a form compatible with 
 1690         the CellML spec instead, i.e. component_name__variable_name, unless 
 1691         the component has its ignore_component_name property set, in which case 
 1692         just use variable_name. 
 1694         If debug is True, also give information about the variable object. 
 1696         if hasattr(self, 
'xml_parent'):
 
 1697             parent_name = self.xml_parent.name
 
 1698             ignore_component = self.component.ignore_component_name
 
 1700             parent_name = 
'*orphan*' 
 1701             ignore_component = 
True 
 1703             if ignore_component:
 
 1706                 vn = parent_name + 
u'__' + self.name
 
 1708             vn = 
u'(' + parent_name + 
u',' + self.name + 
u')' 
 1710             vn = 
'%s@0x%x' % (vn, id(self))
 
 1715         """Split a variable name as given by cellml_variable.fullname into constituent parts. 
 1717         Returns a tuple (component name, local variable name).  If the component name cannot 
 1718         be identified, it will be returned as the empty string. 
 1721             if varname[0] == 
u'(':
 
 1722                 cname, vname = varname[1:-1].split(
u',')
 
 1724                 cname, vname = varname.split(
u'__', 1)
 
 1726             cname, vname = 
u'', varname
 
 1731         """Return the variable object that has name varname. 
 1733         This method tries to handle various forms of fully qualified variable name, i.e. names which 
 1734         include the name of the component the variable occurs in, including those created by 
 1735         CellMLTranslator.code_name. 
 1737         varname = unicode(varname)
 
 1738         if varname[:4] == 
'var_':
 
 1739             varname = varname[4:]
 
 1740         cname, vname = cellml_variable.split_name(varname)
 
 1741         if len(model.component) == 1 
and cname == model.component.name:
 
 1742             var = model.component.get_variable_by_name(vname)
 
 1745                 var = model.get_variable_by_name(cname, vname)
 
 1749                         vname = cname + 
u'__' + vname
 
 1750                     var = model.component.get_variable_by_name(vname)
 
 1756         return 'cellml_variable' + self.
fullname()
 
 1759         return '<cellml_variable %s @ 0x%x>' % (self.
fullname(), id(self))
 
 1762         return self.xml_parent
 
 1763     component = property(get_component)
 
 1767         return self.component.xml_parent
 
 1770         """Get the cellml_units object giving this variable's units.""" 
 1771         return self.component.get_units_by_name(self.units)
 
 1774         """Add a dependency of this variable. 
 1776         This could be an expression defining it, or a variable it's mapped from. 
 1777         Triggers a validation error if we already have another dependency, 
 1778         since a variable can't be defined in more than one way. 
 1785                     u'gets its value from multiple locations.']))
 
 1787             self._cml_depends_on.append(dep)
 
 1792         Return the list of things this variable depends on. 
 1797         """Add a dependency of this variable as the dependent variable in an ODE. 
 1799         independent_var is the corresponding independent variable, and expr is the 
 1800         expression defining the ODE. 
 1801         Triggers a validation error if the same ODE is defined by multiple expressions. 
 1807                     u'There are multiple definitions of the ODE d',self.
fullname(),
 
 1808                     u'/d',independent_var.fullname()]))
 
 1814         """Update an ODE dependency due to partial evaluation. 
 1816         When the PE processes the LHS of a derivative equation, it alters the independent 
 1817         variable to reference its source directly.  If this new source wasn't originally 
 1818         our independent variable's source (e.g. due to a units conversion expression) then 
 1819         this will break lookups of the ODE via _get_ode_dependency, so we need to update 
 1822         if self.
get_type() == VarTypes.Mapped:
 
 1824         self._cml_depends_on_ode.clear()
 
 1829         Return the expression defining the ODE giving the derivative of this 
 1830         variable w.r.t. independent_var. 
 1831         Triggers a validation error if the ODE has not been defined. 
 1833         if self.
get_type() == VarTypes.Mapped:
 
 1835         free_vars = self._cml_depends_on_ode.keys()
 
 1836         free_vars = dict(zip(map(
lambda v: v.get_source_variable(recurse=
True), free_vars),
 
 1838         independent_src = independent_var.get_source_variable(recurse=
True)
 
 1839         if not independent_src 
in free_vars:
 
 1841                 u'The ODE d',self.
fullname(),
u'/d',independent_var.fullname(),
 
 1842                 u'is used but not defined.']))
 
 1846         """Return all expressions this variable depends on, either directly or as an ODE.""" 
 1847         deps = filter(
lambda d: isinstance(d, mathml_apply), self.
_cml_depends_on)
 
 1848         deps.extend(self._cml_depends_on_ode.values())
 
 1852         """Set this to be a mapped variable which imports its value from src_var. 
 1853         A validation error is generated if we are already mapped. 
 1858             debug = model.get_option(
'debug')
 
 1859             model.validation_error(
u' '.join([
 
 1860                 'A variable with interface "in" may only obtain its',
 
 1861                 'value from one location.\nBoth',
 
 1862                 self._cml_source_var.fullname(debug=debug), 
'and',
 
 1863                 src_var.fullname(debug=debug), 
'are exported to', self.
fullname(debug=debug)]))
 
 1872         Assuming this variable imports its value, return the variable 
 1873         from which we obtain our value. 
 1874         If our value is determined within this component, raise a 
 1877         If recurse is set to True, recursively follow mappings until 
 1878         a non-mapped variable is found. 
 1884                 raise TypeError(
u' '.join([
 
 1885                     'Variable', self.
fullname(), 
u'is not a mapped variable',
 
 1886                     '(i.e. does not obtain its value from another component).' 
 1891                 src = src.get_source_variable(recurse=
True)
 
 1895         """Update the type of this variable. 
 1897         The caller should check that the update makes sense. 
 1899         If this variable already has type Mapped, then update the type of 
 1900         our source variable, instead. 
 1903         if var_type 
is VarTypes.State 
and not self.
_cml_var_type is VarTypes.State:
 
 1905         if self.
_cml_var_type == VarTypes.Mapped 
and not _orig 
is self:
 
 1907             if _orig 
is None: _orig = self
 
 1914         """Return the type of this variable. 
 1916         If follow_maps is True and the value of this variable is imported 
 1917         from another component, then return the type of the variable we 
 1918         get our value from instead. 
 1925         """Note this variable as being used in an expression. 
 1927         Keep track of the usage count. 
 1928         If this is a mapped variable, note its source as being used, 
 1931         Note that if a variable is used in 2 branches of a conditional 
 1932         then this counts as 2 uses. 
 1943         Return the number of times this variable is used in an expression. 
 1948         """Decrement our usage count.""" 
 1949         DEBUG(
'partial-evaluator', 
"Dec usage for", self.
fullname())
 
 1957             model = self.xml_parent.xml_parent
 
 1958             model._pe_repeat = 
u'yes' 
 1962         """Add an RDF annotation about this variable. 
 1964         property must be a tuple (qname, namespace_uri). 
 1965         target may either be a tuple as above, or a unicode string, in which 
 1966         case it is interpreted as a literal RDF node. 
 1968         If the variable does not already have a cmeta:id, one will be created 
 1969         for it with value self.fullname(cellml=True). 
 1971         The actual RDF will be added to the main RDF block in the model, which 
 1972         will be created if it does not exist.  Any existing annotations of this 
 1973         variable with the same property will be removed, unless allow_dup=True. 
 1978             meta_id = cellml_metadata.create_unique_id(self.
model, unicode(self.
fullname(cellml=
True)))
 
 1979             self.xml_set_attribute((
u'cmeta:id', NSS[
'cmeta']), meta_id)
 
 1980         property = cellml_metadata.create_rdf_node(property)
 
 1981         target = cellml_metadata.create_rdf_node(target)
 
 1982         source = cellml_metadata.create_rdf_node(fragment_id=meta_id)
 
 1984             cellml_metadata.add_statement(self.
model, source, property, target)
 
 1986             cellml_metadata.replace_statement(self.
model, source, property, target)
 
 1989         """Get an RDF annotation about this variable. 
 1991         property must be a tuple (qname, namespace_uri). 
 1993         Will return the unique annotation found with source being this variable's id, 
 1994         and the given property.  If no annotation is found (or if the variable does 
 1995         not have a cmeta:id), returns None.  Throws an error if more than one 
 1996         annotation is found. 
 2001         property = cellml_metadata.create_rdf_node(property)
 
 2002         source = cellml_metadata.create_rdf_node(fragment_id=meta_id)
 
 2003         return cellml_metadata.get_target(self.
model, source, property)
 
 2006         """Get all RDF annotations about this variable that use the given property. 
 2008         property must be a tuple (qname, namespace_uri). 
 2010         Will return all annotations found with source being this variable's id, 
 2011         and the given property.  If no annotation is found (or if the variable does 
 2012         not have a cmeta:id), returns the empty list 
 2017         property = cellml_metadata.create_rdf_node(property)
 
 2018         source = cellml_metadata.create_rdf_node(fragment_id=meta_id)
 
 2019         return cellml_metadata.get_targets(self.
model, source, property)
 
 2022         """Remove all RDF annotations about this variable. 
 2024         If property is given, only remove annotations with the given property. 
 2028             DEBUG(
'cellml-metadata', 
"Removing RDF annotations for", self, 
"with id", meta_id)
 
 2029             source = cellml_metadata.create_rdf_node(fragment_id=meta_id)
 
 2031                 property = cellml_metadata.create_rdf_node(property)
 
 2032             cellml_metadata.remove_statements(self.
model, source, property, 
None)
 
 2035         """Set an RDF annotation as 'yes' or 'no' depending on a boolean value.""" 
 2043         """Set the binding time of this variable. 
 2045         Options are members of the BINDING_TIMES Enum. 
 2047         If temporary is True, then we're temporarily overriding the normal setting, 
 2048         so save any existing annotation for later replacement. 
 2051         assert bt 
in BINDING_TIMES
 
 2052         if temporary 
and not hasattr(self, 
'_cml_saved_bt'):
 
 2059         """Unset any stored binding time. 
 2061         If the stored BT was a temporary setting, replace it with the original value. 
 2065         if hasattr(self, 
'_cml_saved_bt'):
 
 2071         elif not only_temporary:
 
 2075         """Return the binding time of this variable, as a member of 
 2076         the BINDING_TIMES Enum. 
 2078         This method will (try to) compute & cache the binding time if 
 2081         Variables are classified based on their type: 
 2082           State, Free -> dynamic 
 2084           Mapped -> binding time of source variable 
 2085           Computed -> binding time of defining expression 
 2091                 bt = getattr(BINDING_TIMES, bt_annotation)
 
 2092                 DEBUG(
'partial-evaluator', 
"BT var", self.
fullname(), 
"is annotated as", bt)
 
 2095                 bt = BINDING_TIMES.dynamic
 
 2096                 DEBUG(
'partial-evaluator', 
"BT var", self.
fullname(), 
"is kept")
 
 2100                 DEBUG(
'partial-evaluator', 
"BT var", self.
fullname(), 
"type", str(t))
 
 2101                 if t 
in [VarTypes.State, VarTypes.Free, VarTypes.Unknown]:
 
 2102                     bt = BINDING_TIMES.dynamic
 
 2103                 elif t == VarTypes.Constant:
 
 2104                     bt = BINDING_TIMES.static
 
 2105                 elif t == VarTypes.Mapped:
 
 2107                 elif t == VarTypes.Computed:
 
 2110                     raise TypeError(
"Unexpected variable type " + str(t) +
 
 2111                                     " of variable " + self.
fullname() +
 
 2113                 DEBUG(
'partial-evaluator', 
"BT var", self.
fullname(), 
"is", bt)
 
 2120         """Determine loosely if this variable is considered constant. 
 2122         Checks if we're Constant, or Computed with a static binding time (or 
 2125         If ignore_annotations is True, will ignore cached binding time values and 
 2126         pe:keep annotations.  It instead finds all variables we depend on, directly or 
 2127         indirectly, and gives a dynamic result iff any is a state or free variable. 
 2131         if t 
in [VarTypes.Constant, VarTypes.Unknown]:
 
 2133         elif t == VarTypes.Computed:
 
 2134             if ignore_annotations:
 
 2135                 dependencies = self.model.calculate_extended_dependencies([self])
 
 2137                 for node 
in dependencies:
 
 2138                     if isinstance(node, cellml_variable) 
and node.get_type() 
in [VarTypes.State, VarTypes.Free]:
 
 2143         elif t == VarTypes.Mapped:
 
 2148         """Set the value of this variable. 
 2150         Expects a floating point or boolean value. 
 2152         If ode is given, it should be an instance of cellml_variable. 
 2153         In this case, we're setting the value of d(self)/d(ode). 
 2155         If this is a mapped variable, assign the value to its source 
 2156         variable instead, unless follow_maps is set to False 
 2159         if follow_maps 
and self.
get_type() == VarTypes.Mapped:
 
 2162             assert type(value) 
in [types.FloatType, types.BooleanType]
 
 2166         """Unset all values for this variable set with set_value.""" 
 2168         if self.
get_type() == VarTypes.Mapped:
 
 2170         self._cml_value.clear()
 
 2172         """Return the value of this variable. 
 2174         If a value has been set with set_value(), return that. 
 2175         Otherwise, the behaviour depends on the type of this variable. 
 2176         If it is Mapped, return the value of the source variable. 
 2177         If it is Constant or State, return its initial value. 
 2178         Otherwise, raise an EvaluationError. 
 2180         If ode is given, it should be an instance of cellml_variable. 
 2181         In this case, we're getting the value of d(self)/d(ode). 
 2186         elif self.
get_type() == VarTypes.Mapped:
 
 2188         elif ode 
is None and self.
get_type() 
in [VarTypes.Unknown,
 
 2189                                                  VarTypes.State, VarTypes.Constant]:
 
 2190             if hasattr(self, 
'initial_value'):
 
 2203         """Whether this variable is a parameter that should be modifiable at run-time.""" 
 2204         return (self.
get_type() == VarTypes.Constant 
and 
 2207         """Set method for the is_modifiable_parameter property. 
 2209         We need a separate method for this to bypass Amara's property setting checks. 
 2211         if is_param 
and self.
get_type() != VarTypes.Constant:
 
 2212             raise ValueError(
"A non-constant variable (%s) cannot be set as a parameter" % (self.
fullname(),))
 
 2217         """Whether this variable should be included in reports of derived quantities.""" 
 2220         """Set method for the is_derived_quantity property. 
 2222         We need a separate method for this to bypass Amara's property setting checks. 
 2228         """Whether a protocol has requested this variable as a model output.""" 
 2231         """Set method for the is_output_variable property. 
 2233         We need a separate method for this to bypass Amara's property setting checks. 
 2239         """Whether PE should retain this variable in the specialised model.""" 
 2245         """Set method for the pe_keep property. 
 2247         We need a separate method for this to bypass Amara's property setting checks. 
 2253         """The canonical name of this variable, as given by Oxford metadata. 
 2255         Returns the empty string if no annotation is given. 
 2258             name = cellml_metadata.namespace_member(annotation, NSS[
'oxmeta'], wrong_ns_ok=
True)
 
 2264         """Set method for the oxmeta_name property. 
 2266         Sets a bqbiol:is RDF annotation with the name. 
 2268         We need a separate method for this to bypass Amara's property setting checks. 
 2270         self.
add_rdf_annotation((
'bqbiol:is', NSS[
'bqbiol']), (
'oxmeta:'+name, NSS[
'oxmeta']))
 
 2273         """Reduce this dynamic variable that is being kept. 
 2275         If it has a static source, then turn it into a constant. 
 2276         Otherwise make it depend directly on an appropriate source: 
 2277         either one of its source variables that is also being kept, 
 2278         or the ultimate defining expression. 
 2280         If update_usage is True then this variable is used in an equation, 
 2281         so reduce the usage of any source variables it no longer depends on. 
 2285         if src._get_binding_time() 
is BINDING_TIMES.static:
 
 2289             value = unicode(src.get_value())
 
 2297         elif self.
get_type() == VarTypes.Mapped:
 
 2300             while not src.pe_keep 
and src.get_type() == VarTypes.Mapped 
and src.get_usage_count() == 1:
 
 2301                 src = src.get_source_variable()
 
 2302             if src.pe_keep 
or src.get_usage_count() > 1:
 
 2314                 defn = src.get_dependencies()[0]
 
 2315                 assert isinstance(defn, mathml_apply)
 
 2321                 defn._cml_assigns_to = self
 
 2322                 defn._pe_process = 
u'retarget' 
 2331     def create_new(elt, name, units, id=None, initial_value=None, interfaces={}):
 
 2332         """Create a new <variable> element with the given name and units. 
 2334         Optionally id, initial_value, and interfaces may also be given. 
 2336         elt may be any existing XML element. 
 2338         attrs = {(
u'units', 
None): unicode(units),
 
 2339                  (
u'name', 
None): unicode(name)}
 
 2341             attrs[(
u'cmeta:id', NSS[
u'cmeta'])] = unicode(id)
 
 2342         if initial_value 
is not None and initial_value != 
u'':
 
 2343             attrs[(
u'initial_value', 
None)] = unicode(initial_value)
 
 2344         for iface, val 
in interfaces.items():
 
 2345             attrs[(iface + 
u'_interface', 
None)] = unicode(val)
 
 2346         new_elt = elt.xml_create_element(
u'variable', NSS[
u'cml'], attributes=attrs)
 
 2350     """A set of cellml_units objects. 
 2352     This class behaves like a normal set, but also has additional 
 2353     methods for operations specific to sets of <units> objects: 
 2354       simplify - allow for multiplication of sets of units 
 2355       dimensionally_equivalent - compare 2 sets of units for dimensional equivalence 
 2356       description - describe the units in this set 
 2358     All units in the set (normally) must be dimensionally equivalent.  The exception is when dealing with 
 2359     Functional Curation protocols which can defined units conversion rules for non-scaling cases.  We can 
 2360     then have sets of alternative units in different dimensions, and the get_consistent_set method helps 
 2361     with selecting from these. 
 2365         Work around annoyance in set implementation of python 2.4.2c1 and on. 
 2366         setobject.c checks for keyword arguments in its __new__ instead of its 
 2367         __init__, so we get an error 
 2368         'TypeError: set() does not take keyword arguments' if we don't do this. 
 2370         return super(UnitsSet, cls).
__new__(cls, iterable)
 
 2373         super(UnitsSet, self).
__init__(iterable)
 
 2379         """Do a shallow copy of this UnitsSet.""" 
 2380         new_set = super(UnitsSet, self).
copy()
 
 2382         new_set._sources = {}
 
 2383         for units, src_list 
in self._sources.iteritems():
 
 2384             new_set._sources[units] = copy.copy(src_list)
 
 2388         """Extract a subset of the units in this set that are dimensionally equivalent. 
 2390         When dealing with potential non-scaling conversions, an expression may have potential units that are 
 2391         not within the same dimension.  However, when deciding on the units for the expression most operations 
 2392         need to handle a set of options that *are* within the same dimension, and this operation supports that. 
 2394         Given a cellml_units object for the desired units of the expression, this method first tries to create 
 2395         a UnitsSet containing just our members in the same dimension.  If we have no members in the desired 
 2396         dimension, then we check that all members of this set are in the same dimension, and return the set 
 2397         itself - there should never be more than 2 different dimensions to choose from (I hope!). 
 2401             if units.dimensionally_equivalent(desired_units):
 
 2403                 new_set._sources[units] = copy.copy(self.
_sources[units])
 
 2407                 if not units.dimensionally_equivalent(rep_u):
 
 2408                     raise ValueError(
"Unexpected dimensional variation in UnitsSet; " + rep_u.description() + 
" and " + units.description()
 
 2414         """Test whether the units in the set are equal to those in another set.""" 
 2416             equal = self.
extract(check_equality=
True).
equals(other.extract(check_equality=
True))
 
 2422         """Extract a representative element from this set. 
 2424         This is intended to be used to get the cellml_units object from a singleton set. 
 2426         If check_equality is True, check that all members of this set have the same multiplicative factor. 
 2428         representative = iter(self).next()
 
 2434                     raise ValueError(
"UnitsSet equality check failed")
 
 2435                 if u.is_simple() 
and not u._rel_error_ok(u.expand().
get_offset(),
 
 2438                     raise ValueError(
"UnitsSet equality check failed")
 
 2439         return representative
 
 2442         """Store a reference to the expression that has these units.""" 
 2447         """Add source units for the given units. 
 2449         This stores references to how units were arrived at when doing a 
 2450         simplify.  It manages lists of (src_units_set, src_units) pairs for 
 2451         each units definition in this set. 
 2453         If src_units_set has no associated expression, then it is 
 2454         considered to be a temporary object, created for example whilst 
 2455         doing an n-ary times operation.  In this case, we add its list of 
 2456         sources for src_units to our sources list instead. 
 2460         if not hasattr(src_units_set, 
'_expression'):
 
 2462             print src_units_set.description(), src_units.description()
 
 2463         if src_units_set._expression:
 
 2464             self.
_sources[units].append((src_units_set, src_units))
 
 2467                 self.
_sources[units].extend(src_units_set._sources[src_units])
 
 2475         """Return the sources list for the given units.""" 
 2476         return self._sources.get(units, [])
 
 2479         """Return an expression that has these units.""" 
 2482     def simplify(self, other_units=None, other_exponent=1):
 
 2483         """Simplify the units in this set. 
 2485         Each cellml_units object in this set is simplified, and a new 
 2486         UnitsSet returned with the results. 
 2488         If other_units is not None, then products of units are 
 2489         calculated.  The returned set is essentially the cartesian 
 2490         product of the input sets under the simplify operator, i.e. 
 2491         u1.simplify(other_units=u2, other_exponent=other_exponent) 
 2492         will be called for each member u1 of self and each member u2 
 2493         of other_units (if other_units is a UnitsSet; otherwise 
 2498             if other_units 
is None:
 
 2499                 res_u = units.simplify()
 
 2500                 result_set.add(res_u)
 
 2501                 result_set._add_source(res_u, self, units)
 
 2503                 if isinstance(other_units, cellml_units):
 
 2504                     other_units = 
UnitsSet([other_units])
 
 2505                 for u 
in other_units:
 
 2506                     res_u = units.simplify(u, other_exponent)
 
 2507                     result_set.add(res_u)
 
 2508                     result_set._add_source(res_u, self, units)
 
 2509                     result_set._add_source(res_u, other_units, u)
 
 2513         """Check for dimensional equivalence between sets of units. 
 2515         Since all units in each set should be equivalent, we just compare 
 2516         an arbitrary member from each set. 
 2518         other_units may be a single cellml_units instance, in which case we 
 2519         compare an arbitrary member of self to it. 
 2522         u2 = other_units.extract()
 
 2523         return u1.dimensionally_equivalent(u2)
 
 2526         """Describe these units. 
 2528         Shows the descriptions of each member, as though this were a set of 
 2529         unicode strings.  If multiple members have the same description, 
 2530         only one instance is shown.  If only one description is being shown, 
 2531         then the curly brackets are not added. 
 2533         desc = list(set(u.description() 
for u 
in self))
 
 2536             desc = 
u'{' + 
u','.join(desc) + 
u'}' 
 2543     Specialised units class. 
 2544     Contains useful methods for defining the standard units dictionary, 
 2545     and checking for units consistency. 
 2547     After being defined, a units definition should be regarded as being 
 2548     immutable, as should individual <unit> elements, so the expansion 
 2549     and simplification methods create new objects, which don't really 
 2550     live in the document tree (they're not a child of any element in the 
 2551     model), although they do have their xml_parent attribute set. 
 2553     Note that <unit> elements must not be made a child of more than one 
 2554     <units> element, otherwise the linked lists get tangled up. 
 2558         super(cellml_units, self).
__init__()
 
 2567         return '<cellml_units %s @ 0x%x>' % (self.name, id(self))
 
 2571         """Generate a tuple used as the basis for our hash value.""" 
 2573             hash_list = [self.is_base_unit()]
 
 2575                 hash_list.append(self.name)
 
 2576             hash_list.extend(list(getattr(self, 
u'unit', [])))
 
 2577             hash_tup = tuple(hash_list)
 
 2584         """For avoiding duplicate identical units definitions. 
 2586         Based on description(cellml=True), since that is what we really want to be unique. 
 2587         Also includes offset information, since that is omitted in the name given by description. 
 2589         l = [self.description(cellml=
True)]
 
 2590         if self.is_simple():
 
 2592             l.append((self.unit.get_units_element().uniquify_tuple,
 
 2593                       self.unit.get_offset()))
 
 2597         """Generate a hash for these units. 
 2599         Hashes a tuple, the first element of which is the result of self.is_base_unit(), 
 2600         the second of which is our name if we are not auto-generated, 
 2601         and the remaining elements of which are our <unit> elements. 
 2608         """Compare 2 units objects, by comparing their hashes. 
 2610         Means using units as dictionary keys is more sane.""" 
 2611         if isinstance(other, cellml_units):
 
 2612             if hash(self) == hash(other):
 
 2615                 return cmp(hash(self), hash(other))
 
 2617             return super(cellml_units, self).
__cmp__(other)
 
 2628         """Test if the relative difference of 2 values is within tol.""" 
 2629         if abs(value1) == 0.0:
 
 2630             return abs(value2) < tol
 
 2632             return (abs(value1-value2)/abs(value1)) < tol
 
 2635         """Compare 2 units elements for equality. 
 2637         Two units are deemed equal if they are both dimensionally equivalent and have the same 
 2638         multiplicative factor (to within a relative tolerance of 10^-6). 
 2640         If both are simple units, they must also have the same offset. 
 2642         equal = isinstance(other, cellml_units) 
and \
 
 2643                 self.dimensionally_equivalent(other) 
and \
 
 2647         if equal 
and self.is_simple():
 
 2648             equal = self._rel_error_ok(self.unit.get_offset(),
 
 2649                                        other.unit.get_offset(),
 
 2655         return self.rootNode.model
 
 2658         """Return these units. 
 2660         Used for interface compatibility with UnitsSet.""" 
 2664         """Return a new UnitsSet containing this cellml_units object. 
 2666         Used for interface compatibility with UnitsSet, where the method performs a shallow copy. 
 2671         """Return a human-readable name for these units. 
 2673         The name will be descriptive and based on the consituent <unit> elements, e.g. 'volt per second^2' 
 2675         By default, if these are user-defined units, then return self.name.  Set force to True to override this behaviour. 
 2677         Set cellml to True to get a description that is also a valid CellML identifier. 
 2679         if self.is_base_unit():
 
 2681         elif not force 
and not self._cml_generated:
 
 2684             descs, per_descs = [], []
 
 2686             m = self.get_multiplier()
 
 2688                 descs.append(unicode(m))
 
 2690             dimensionless = self.get_units_by_name(
'dimensionless')
 
 2691             for unit 
in self.unit:
 
 2692                 if unit.get_units_element() 
is dimensionless:
 
 2694                 desc = [getattr(unit, 
u'prefix_', 
u''), unit.get_units_element().name]
 
 2695                 e = unit.get_exponent()
 
 2700                     desc.extend([
'^', str(abs(e))])
 
 2701                 desc = 
u''.join(desc)
 
 2703                     per_descs.append(desc)
 
 2708             descs = 
u' '.join(descs)
 
 2710             desc = 
u' per '.join([descs] + per_descs)
 
 2712                 desc = 
u'dimensionless' 
 2714                 desc = 
u"'" + desc + 
u"'" 
 2717                 desc = 
u"'" + desc[1:] + 
u"'" 
 2720                 desc = desc.replace(
u"'", 
u"").replace(
u"^", 
u"")
 
 2721                 desc = desc.replace(
u"*", 
u"").replace(
u".", 
u"_")
 
 2722                 desc = desc.replace(
u" ", 
u"_")
 
 2726         """Return an object representing the element that defines the units named `uname'.""" 
 2728         return self.xml_parent.get_units_by_name(uname)
 
 2731         """Expand to a product of powers of base units. 
 2733         Expand this units definition according to the algorithm given in appendix C.3.4 of the CellML spec. 
 2735         Caches and returns the resulting <units> object, which will be 
 2736         newly created if there are any changes made, or this object if not. 
 2738         if self._cml_expanded 
is None:
 
 2740             if self.is_base_unit():
 
 2742                 self._cml_expanded = self
 
 2743             elif self.is_simple():
 
 2747                 expanded_child = self.unit.get_units_element().
expand()
 
 2749                 m1p1 = self.unit.get_multiplicative_factor()
 
 2750                 o1 = self.unit.get_offset()
 
 2751                 if expanded_child.is_base_unit():
 
 2756                     ref_obj_new = expanded_child
 
 2759                     m2p2 = expanded_child.unit.get_multiplicative_factor()
 
 2760                     o2 = expanded_child.unit.get_offset()
 
 2763                     o_new = o1 + o2/m1p1
 
 2766                     ref_obj_new = expanded_child.unit.get_units_element()
 
 2768                 attrs = {
u'name': self.name}
 
 2769                 self._cml_expanded = self.xml_create_element(
u'units',
 
 2772                 self._cml_expanded._cml_generated = 
True 
 2773                 attrs = {
u'units': ref_obj_new.name,
 
 2774                          u'multiplier': unicode(m_new),
 
 2775                          u'offset': unicode(o_new)}
 
 2776                 unit = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 2780                 unit._set_units_element(ref_obj_new)
 
 2781                 self._cml_expanded.xml_append(unit)
 
 2786                 attrs = {
u'name': self.name}
 
 2787                 exp_units = self.xml_create_element(
u'units', NSS[
u'cml'],
 
 2789                 exp_units._cml_generated = 
True 
 2793                 for unit 
in self.unit:
 
 2794                     m_t *= unit.get_multiplicative_factor() 
 
 2797                     e = unit.get_exponent()
 
 2799                     exp_u = unit.get_units_element().
expand()
 
 2800                     if exp_u.is_base_unit():
 
 2802                         attrs = {
u'units': exp_u.name,
 
 2803                                  u'exponent': unicode(e)}
 
 2804                         u = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 2806                         exp_units.xml_append(u)
 
 2810                         for u 
in exp_u.unit:
 
 2811                             m_t *= u.get_multiplicative_factor() ** e
 
 2812                             attrs = {
u'units': u.units,
 
 2813                                      u'exponent': unicode(
 
 2814                                 u.get_exponent() * e)}
 
 2815                             u_new = u.xml_create_element(
u'unit',
 
 2818                             exp_units.xml_append(u_new)
 
 2848                 first_unit = sorted(exp_units.unit, key=
lambda u: u.units)[0]
 
 2849                 first_unit.multiplier = unicode(m_t)
 
 2851                 self._cml_expanded = exp_units
 
 2853             self._cml_expanded.xml_parent = self.xml_parent
 
 2855         return self._cml_expanded
 
 2858         """Return True iff this is a base units definition.""" 
 2859         return getattr(self, 
u'base_units', 
u'no') == 
u'yes' 
 2861         """Return True iff this is a simple units definition. 
 2863         Units are simple if: 
 2864           there is 1 <unit> element 
 2865           the exponent is omitted or has value 1.0 
 2866           the referenced units are simple or base units 
 2869         if not self.is_base_unit() 
and len(self.unit) == 1:
 
 2870             if self.unit.get_exponent() == 1.0:
 
 2871                 u = self.unit.get_units_element()
 
 2872                 if u.is_base_unit() 
or u.is_simple():
 
 2877         """Return the multiplicative factor of this units definition. 
 2879         The multiplicative factor of a units definition can be defined as 
 2880         the product of the multiplicative factors of its unit children. 
 2882         m = reduce(operator.mul,
 
 2883                    map(
lambda unit: unit.get_multiplicative_factor(),
 
 2884                        getattr(self, 
u'unit', [])),
 
 2889         """Return the multiplier of this units definition. 
 2891         The multiplier of a units definition can be defined as the product 
 2892         of the multipliers of its unit children. 
 2894         return reduce(operator.mul,
 
 2895                       map(
lambda unit: unit.get_multiplier(),
 
 2896                           getattr(self, 
u'unit', [])),
 
 2900         """Return the offset associated with this units definition. 
 2902         If these are simple units, return the offset on our unit child. 
 2903         Otherwise, return 0. 
 2905         if self.is_simple():
 
 2906             o = self.unit.get_offset()
 
 2912     def create_new(parent, name, bases, add_to_parent=False, standard=False):
 
 2913         """Create a new units definition element. 
 2915         It requires either a cellml_model or cellml_component element 
 2916         to be passed as the parent for the definition.  If 
 2917         add_to_parent is set to true the new units element will be 
 2918         appended to parent's children. 
 2920         The bases parameter should be a list of dictionaries suitable 
 2921         for use as the keyword arguments of cellml_units._based_on. 
 2922         If the list is empty it will be defined as a base unit. 
 2925         attrs = {
u'name': unicode(name)}
 
 2927             attrs[
u'base_units'] = 
u'yes' 
 2929             attrs[
u'standard'] = 
u'yes' 
 2930         u = parent.xml_create_element(
u'units', NSS[
u'cml'], attributes=attrs)
 
 2932             parent.xml_append(u)
 
 2934             u.xml_parent = parent 
 
 2937             u._based_on(**basis)
 
 2940     def _based_on(self, units=None, prefix=None, exponent=None,
 
 2941                   multiplier=
None, offset=
None):
 
 2942         """Convenience function for defining new units.""" 
 2943         for v 
in [
'units', 
'prefix', 
'exponent', 
'multiplier', 
'offset']:
 
 2945             exec 
"if type(%s) == str: %s = unicode(%s)" % (v,v,v)
 
 2947             exec 
"assert(%s is None or type(%s) == unicode)" % (v,v)
 
 2948         assert(
not hasattr(self, 
'base_units') 
or self.base_units == 
u'no')
 
 2949         attrs = {
u'units': units}
 
 2952             assert(
not hasattr(self, 
'unit'))
 
 2953             attrs[
u'offset'] = offset
 
 2954             if not prefix 
is None: attrs[
u'prefix'] = prefix
 
 2955             if not exponent 
is None: attrs[
u'exponent'] = exponent
 
 2958             if not prefix 
is None: attrs[
u'prefix'] = prefix
 
 2959             if not exponent 
is None: attrs[
u'exponent'] = exponent
 
 2960             if not multiplier 
is None: attrs[
u'multiplier'] = multiplier
 
 2961         self.xml_append(self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 2966     units_name_counter = [0]
 
 2967     def simplify(self, other_units=None, other_exponent=1):
 
 2968         """Simplify these units. 
 2970         Create a new <units> element representing a simplified version of 
 2971         this element.  This implements the algorithm of appendix C.3.1.  It 
 2972         is however slightly different, in order to allow for units 
 2973         conversions rather than just preserving dimensional equivalence. 
 2975         If other_units is not None, then produce a simplified version of 
 2976         the product of these units and other_units.  other_units are 
 2977         considered to be raised to the power of other_exponent (so a 
 2978         quotient can be performed by passing other_exponent=-1).  Note that 
 2979         other_exponent should have numeric type. 
 2981         If other_units is a UnitsSet, then we construct a UnitsSet 
 2982         containing self, and call the simplify method on that, thus 
 2983         returning a UnitsSet instance. 
 2985         Multiplicative factors on the original <unit> objects are 
 2986         maintained on the generated references, by taking the product of 
 2987         all factors from <unit> objects that contribute to the new <unit> 
 2988         object.  Note that this means that we may need to retain a 
 2989         reference to dimensionless with a multiplier, for example if the 
 2990         quotient of centimetres by metres is taken. 
 2992         Offsets are only permitted on simple units, so may not appear where 
 2993         there are multiple <unit> elements.  Hence when a product of units 
 2994         is taken, any offsets are discarded. 
 2996         if isinstance(other_units, UnitsSet):
 
 3001         if (other_units, other_exponent) 
in self._cml_simplified:
 
 3002             return self._cml_simplified[(other_units, other_exponent)]
 
 3006         if self.is_base_unit():
 
 3008             u = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 3009                                         attributes={
u'units': self.name})
 
 3011             u._set_units_element(self)
 
 3014             units = list(self.unit)
 
 3015         our_unit_elements = frozenset(units)
 
 3016         other_unit_elements = 
None 
 3017         if not other_units 
is None:
 
 3018             if other_units.is_base_unit():
 
 3020                 attrs = {
u'units': other_units.name,
 
 3021                          u'exponent': unicode(other_exponent)}
 
 3022                 u = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 3024                 u.xml_parent = other_units
 
 3025                 u._set_units_element(other_units)
 
 3027                 if other_exponent == 1:
 
 3028                     other_unit_elements = frozenset([u])
 
 3030                 if other_exponent == 1:
 
 3031                     units.extend(list(other_units.unit))
 
 3032                     other_unit_elements = frozenset(other_units.unit)
 
 3034                     for unit 
in other_units.unit:
 
 3038                         u.exponent = unicode(other_exponent *
 
 3040                         u.multiplier = unicode(u.get_multiplier() **
 
 3045         dimensionless = self.get_units_by_name(
u'dimensionless')
 
 3046         d = {dimensionless: []}
 
 3048             obj = unit.get_units_element()
 
 3056         for obj 
in d.keys():
 
 3057             if obj != dimensionless 
and len(d[obj]) > 1:
 
 3059                 expt = sum(map(
lambda u: u.get_exponent(), d[obj]))
 
 3062                     attrs = {
u'units': 
u'dimensionless'}
 
 3064                     attrs = {
u'units': d[obj][0].units,
 
 3065                              u'exponent': unicode(expt)}
 
 3068                 m = reduce(operator.mul,
 
 3069                            map(
lambda u: u.get_multiplicative_factor(),
 
 3071                 attrs[
u'multiplier'] = unicode(m)
 
 3073                 new = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 3075                 new.xml_parent = self
 
 3078                     d[dimensionless].append(new)
 
 3083             elif obj != dimensionless 
and d[obj]:
 
 3087                 d[obj] = d[obj][0].clone()
 
 3092         m = reduce(operator.mul,
 
 3093                    map(
lambda u: u.get_multiplicative_factor(),
 
 3097             del d[dimensionless]
 
 3101             d[dimensionless] = d[dimensionless][0].clone()
 
 3102             d[dimensionless].multiplier = unicode(m)
 
 3113             new_units = dimensionless
 
 3116             unit_elements = set(d.values())
 
 3117             if unit_elements == our_unit_elements:
 
 3119             elif unit_elements == other_unit_elements:
 
 3120                 new_units = other_units
 
 3123                 self.units_name_counter[0] += 1
 
 3124                 uname = 
u'___units_' + str(self.units_name_counter[0])
 
 3126                 new_units = self.xml_create_element(
 
 3127                     u'units', NSS[
u'cml'], attributes={
u'name': uname})
 
 3128                 new_units._cml_generated = 
True 
 3130                 new_units.xml_parent = self._best_parent(other_units)
 
 3132                 for unit 
in unit_elements:
 
 3133                     new_units.xml_append(unit)
 
 3134                 if self.model._is_new_units_obj(new_units):
 
 3137                     new_units.xml_parent.add_units(uname, new_units)
 
 3139         new_units = self.model._get_units_obj(new_units)
 
 3140         self._cml_simplified[(other_units, other_exponent)] = new_units
 
 3144         """Return a suitable parent for units formed from self and other_units. 
 3146         If either constituent is in a component, that component should be the 
 3147         parent, otherwise the model should be. 
 3149         p1, p2 = self.xml_parent, other_units 
and other_units.xml_parent
 
 3150         if p2 
and p1 != p2 
and isinstance(p1, cellml_model):
 
 3155         """Form the quotient of two units definitions. 
 3157         This method does not simplify the resulting units. 
 3158         Quotient units will be cached. 
 3160         if not other_units 
in self._cml_quotients:
 
 3162             self.units_name_counter[0] += 1
 
 3163             uname = 
u'___units_' + str(self.units_name_counter[0])
 
 3164             quotient_units = self.xml_create_element(
 
 3165                 u'units', NSS[
u'cml'], attributes={
u'name': uname})
 
 3166             quotient_units._cml_generated = 
True 
 3167             quotient_units.xml_parent = self._best_parent(other_units)
 
 3169             u = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 3170                                         attributes={
u'units': self.name})
 
 3171             u._set_units_element(self)
 
 3172             quotient_units.xml_append(u)
 
 3173             u = self.xml_create_element(
u'unit', NSS[
u'cml'],
 
 3174                                         attributes={
u'units': self.name,
 
 3175                                                     u'exponent': 
u'-1'})
 
 3176             u._set_units_element(other_units)
 
 3177             quotient_units.xml_append(u)
 
 3179             if self.model._is_new_units_obj(quotient_units):
 
 3180                 quotient_units.xml_parent.add_units(uname, quotient_units)
 
 3181             quotient_units = self.model._get_units_obj(quotient_units)
 
 3182             self._cml_quotients[other_units] = quotient_units
 
 3183         return self._cml_quotients[other_units]
 
 3186         """Return True iff other_units is dimensionally equivalent to self. 
 3188         As per appendix C.2.2, two units definitions have dimensional 
 3189         equivalence if, when each is recursively expanded and 
 3190         simplified into a product of base units, each has the same set 
 3191         of base units with the same exponent on corresponding base units. 
 3194         if isinstance(other_units, UnitsSet):
 
 3195             other_units = other_units.extract()
 
 3196         u2 = other_units.expand().
simplify()
 
 3199         if u1.is_base_unit():
 
 3203                 d1[u.get_units_element()] = u.get_exponent()
 
 3204         if u2.is_base_unit():
 
 3208                 d2[u.get_units_element()] = u.get_exponent()
 
 3210         sym_diff = set(d1.keys()) ^ set(d2.keys())
 
 3212             dimensionless = self.get_units_by_name(
u'dimensionless')
 
 3213             if not sym_diff == set([dimensionless]):
 
 3220                 if d1[k] != d2[k]: 
return False 
 3229     """Specialised class for <unit> elements. 
 3231     Maintains a reference to the object representing the units definition 
 3232     it references, provides some helpful accessor type methods, and allows 
 3233     safe, easy cloning of <unit> elements. 
 3237         element_base.__init__(self)
 
 3242         """Create a tuple to be used for hashing/equality tests.""" 
 3248         """Compare two <unit> elements. 
 3250         Two <unit> elements are equal if they reference the same <units> 
 3251         element, and have the same prefix, multiplier, etc. 
 3254         if isinstance(other, cellml_unit):
 
 3255             eq = self.
_hash_tup() == other._hash_tup()
 
 3258         """The inverse of self.__eq__(other).""" 
 3259         return not self.
__eq__(other)
 
 3261         """Richer hashing function than the default based on object id. 
 3263         Returns the hash of a tuple of relevant attributes.""" 
 3268         Return the object representing the <units> element that this <unit> element references. 
 3272             self.
_cml_units_obj = self.xml_parent.get_units_by_name(self.units)
 
 3276         Set the object representing the <units> element that this <unit> element references. 
 3278         Don't use unless you know what you're doing. 
 3284     SI_PREFIXES = {
"yotta": 24, 
"zetta": 21, 
"exa": 18, 
"peta": 15,
 
 3285                    "tera": 12, 
"giga": 9, 
"mega": 6, 
"kilo": 3,
 
 3286                    "hecto": 2, 
"deka": 1,
 
 3287                    "deci": -1, 
"centi": -2,
 
 3288                    "milli": -3, 
"micro": -6, 
"nano": -9, 
"pico": -12,
 
 3289                    "femto": -15, 
"atto": -18, 
"zepto": -21, 
"yocto": -24}
 
 3292         """Return the factor this units reference is multiplied by. 
 3294         Return the quantity m.p^e as a floating point number, where: 
 3295           m is the multiplier (default value 1.0) 
 3296           p is the multiplicative factor due to the prefix (default 10^0=1) 
 3297           e is the exponent (default 1) 
 3301         p = getattr(self, 
u'prefix_', 0) 
 
 3310         """Return the multiplier of this units reference, as a float.""" 
 3311         return float(getattr(self, 
u'multiplier', 1))
 
 3314         """Return the exponent of this units reference, as a float.""" 
 3315         return float(getattr(self, 
u'exponent', 1))
 
 3318         """Return the offset of this units reference, as a float.""" 
 3319         return float(getattr(self, 
u'offset', 0))
 
 3322         """Clone this object. 
 3324         Return a new <unit> element that has the same attributes as this 
 3328         for apyname, aname 
in self.xml_attributes.iteritems():
 
 3329             attrs[aname] = getattr(self, apyname)
 
 3330         new = self.xml_create_element(
u'unit', NSS[
u'cml'], attributes=attrs)
 
 3333         new.xml_parent = self.xml_parent
 
 3339     Exception class for errors raised trying to evaluate a MathML expression. 
 3343 class MathsError(Exception):
 
 3345     Exception class for validation errors raised while checking mathematics. 
 3347     def __init__(self, context_obj, message, warn=False, level=None):
 
 3348         """Create a mathematics validation error. 
 3350         context_class should be the object that is reporting the error. 
 3351         message gives more details on what went wrong. 
 3352         If warn is set to true then produce a warning message, not an error. 
 3353         level gives the level of the message logged. 
 3357         self.
level = level 
or (logging.ERROR,logging.WARNING)[warn]
 
 3366         if isinstance(context_obj, cellml_variable):
 
 3370         expr_root = context_obj.xml_xpath(
u'ancestor-or-self::*[local-name(..)="math"]')[0]
 
 3372         math = expr_root.xml_parent
 
 3373         for elt 
in math.xml_element_children():
 
 3374             if elt 
is expr_root: 
break 
 3375             if elt.localName 
in [
u'apply', 
u'semantics']:
 
 3377         for elt 
in math.xml_element_children(math.xml_parent):
 
 3378             if elt 
is math: 
break 
 3379             if elt.localName == 
u'math':
 
 3382         vref = context_obj.xml_xpath(
u'ancestor::cml:variable_ref')
 
 3384             self.
reaction_spec = 
' in mathematics for variable "%s" in a reaction' % vref[0].variable
 
 3390         """Only show the XML where the error occurred.""" 
 3396         return msg.encode(
'UTF-8')
 
 3399         "Convert integer i to an ordinal string." 
 3400         if i // 10 == 1:  suf = 
'th' 
 3401         elif i % 10 == 1: suf = 
'st' 
 3402         elif i % 10 == 2: suf = 
'nd' 
 3403         elif i % 10 == 3: suf = 
'rd' 
 3405         return "%d%s" % (i, suf)
 
 3408         if self.
warn: type = 
'Warning' 
 3409         else: type = 
'Error' 
 3410         msg = [type, 
' ', where, 
': ', self.
message]
 
 3415                          ' in component ', self.
cname, 
'\n  XPath: ',
 
 3420             xml = self.context.xml(indent = 
u'yes',
 
 3421                                    omitXmlDeclaration = 
u'yes')
 
 3422             msg = msg + 
u'\n' + unicode(xml, encoding=
'UTF-8')
 
 3431     Exception class for validation errors raised during units checking 
 3434     def __init__(self, context_obj, message, warn=False, level=None):
 
 3435         """Create a units validation error. 
 3437         context_class should be the object that is reporting the error. 
 3438         message gives more details on what went wrong. 
 3439         If warn is set to true then produce a warning message, not an error. 
 3440         level gives the level of the message logged. 
 3442         MathsError.__init__(self, context_obj, message, warn=warn, level=level)
 
 3450     "Return the i'th child element of elt.  Indexing starts at 1." 
 3452     for e 
in elt.xml_children:
 
 3453         if getattr(e, 
'nodeType', 
None) == Node.ELEMENT_NODE:
 
 3458         raise ValueError(
"<"+elt.localName+
"> does not have "+str(i)+
 
 3459                          " child element(s).")
 
 3461     "Get the first child element of elt." 
 3470     """Base class for units mixin classes.""" 
 3472         """Add mathematics for an explicit units conversion. 
 3474         Wraps expr in the expression 
 3475         m[to_units/defn_units]*(expr-o1[defn_units]) + o2[to_units]. 
 3478         if hasattr(expr.model, 
'_cml_special_units_converter') 
and not defn_units.dimensionally_equivalent(to_units):
 
 3481                 model._cml_conversions_needed = 
True 
 3484                 expr = expr.model._cml_special_units_converter(expr, defn_units, to_units)
 
 3489                 defn_units = expr.get_units().
extract(check_equality=
True)
 
 3491                 print 'ouch', expr.xml()
 
 3492                 for u 
in expr.get_units():
 
 3493                     print u.description(), u.get_multiplier(), expr.get_units()._get_sources(u)
 
 3495         defn_units_exp = defn_units.expand().
simplify()
 
 3496         to_units_exp = to_units.expand().
simplify()
 
 3498         m = (defn_units_exp.get_multiplicative_factor() / to_units_exp.get_multiplicative_factor())
 
 3500         orig_expr, parent = expr, expr.xml_parent
 
 3501         dummy = expr.xml_create_element(
u'dummy', NSS[
u'm'])
 
 3503         parent.replace_child(expr, dummy) 
 
 3504         if defn_units_exp.get_offset() != 0:
 
 3506             uattr = orig_expr._ensure_units_exist(defn_units, no_act=no_act)
 
 3507             new_expr = mathml_apply.create_new(expr, 
u'minus',
 
 3508                                                [expr, (unicode(defn_units_exp.get_offset()), uattr)])
 
 3509             new_expr._cml_units = defn_units
 
 3512             quotient_units = to_units.quotient(defn_units)
 
 3514             uattr = orig_expr._ensure_units_exist(quotient_units, no_act=no_act)
 
 3516             new_expr = mathml_apply.create_new(expr, 
u'times', [(unicode(m), uattr), expr])
 
 3517             new_expr._cml_units = to_units
 
 3519         if to_units_exp.get_offset() != 0:
 
 3521             uattr = orig_expr._ensure_units_exist(to_units, no_act=no_act)
 
 3522             new_expr = mathml_apply.create_new(expr, 
u'plus',
 
 3523                                                [expr, (unicode(to_units_exp.get_offset()), uattr)])
 
 3524             new_expr._cml_units = to_units
 
 3527         if expr 
is not orig_expr:
 
 3528             model._cml_conversions_needed = 
True 
 3531         parent.replace_child(dummy, expr)
 
 3535         """Try to set the units of the given element. 
 3537         Generates a debug message if this isn't possible. 
 3539         if hasattr(elt, 
'_set_in_units') 
and callable(elt._set_in_units):
 
 3540             elt._set_in_units(units, no_act)
 
 3541         elif elt.localName 
in [
u'false', 
u'true']:
 
 3542             boolean = self.component.get_units_by_name(
'cellml:boolean')
 
 3543             if boolean 
is not units:
 
 3546         elif elt.localName 
in [
u'notanumber', 
u'pi', 
u'infinity', 
u'exponentiale']:
 
 3547             dimensionless = self.component.get_units_by_name(
'dimensionless')
 
 3548             if dimensionless 
is not units:
 
 3553                   'Cannot set units (to', units.description(), 
') for element', elt.localName)
 
 3557     """Contains the _set_in_units method for ci, cn, etc.""" 
 3559         """Set the units this element should be expressed in. 
 3561         Where these aren't the units it's defined in, replace self by suitable units conversion mathematics. 
 3563         defn_units = self.get_units(return_set=
False)
 
 3564         if defn_units != units:
 
 3573         """Set the units of the application of this operator. 
 3575         The default behaviour for many operators is to simply set all operands to have the given units. 
 3578         app = self.xml_parent
 
 3581         for operand 
in list(app.operands()):
 
 3585             app._cml_units = units
 
 3590         """Set the units of the application of this operator. 
 3592         This method is used for the relational operators.  It ignores the 
 3593         given units, and instead ensures that all operands have the same 
 3594         units.  The units it chooses are those that are 'least' amongst the 
 3595         possibilities for the operand units, i.e. that have the smallest 
 3596         multiplicative factor when expanded. 
 3598         app = self.xml_parent
 
 3599         min_factor, operand_units = 
None, 
None 
 3600         for us 
in app._get_operand_units():
 
 3601             if isinstance(us, cellml_units):
 
 3605                 if f < min_factor 
or min_factor 
is None:
 
 3610         for operand 
in list(app.operands()):
 
 3615             app._cml_units = units
 
 3620         """Set the units of the application of this operator. 
 3622         This mixin is used for the <times> and <divide> operators. 
 3623         There are 2 possible strategies here.  One is to pick one of the 
 3624         operands and convert it so that the overall units match those 
 3625         required.  The other is to pick units from the set of those 
 3626         possible for this application, and convert the result to the 
 3627         desired units.  We go with the latter option, picking the units 
 3628         that are least in the sense that they have the least multipicative 
 3629         factor, but where possible that factor is no less than that on the 
 3632         app = self.xml_parent
 
 3633         min_factor, best_factor = 
None, 
None 
 3634         least_units, best_units = 
None, 
None 
 3636         DEBUG(
'validator', 
'>',self.localName,
':',desired_factor,
 
 3637               desired_units.description())
 
 3638         units_set = app.get_units().get_consistent_set(desired_units)
 
 3639         for possible_units 
in units_set:
 
 3641             if min_factor 
is None or f<min_factor:
 
 3642                 least_units, min_factor = possible_units, f
 
 3643             if f >= desired_factor 
and (best_factor 
is None or f < best_factor):
 
 3644                 best_units, best_factor = possible_units, f
 
 3645         if best_units 
is None:
 
 3650         DEBUG(
'validator', 
'\t<-',
 
 3652               units.description())
 
 3654         app._add_units_conversion(app, units, desired_units, no_act)
 
 3656         for src_units_set, src_units 
in app.get_units()._get_sources(units):
 
 3657             expr = src_units_set.get_expression()
 
 3658             DEBUG(
'validator', 
'#',self.localName,
':',
 
 3659                   src_units.description(),expr.localName)
 
 3663             app._cml_units = units
 
 3668         """Set the units of this element. 
 3670         For container elements, we set the units of the child(ren). 
 3674         for elt 
in self.xml_children[:]:
 
 3675             if getattr(elt, 
'nodeType', 
None) == Node.ELEMENT_NODE:
 
 3683     """Base class for MathML elements.""" 
 3692         return '<%s (%s) at 0x%x>' % (self.__class__.__name__, str(self), id(self))
 
 3695         """Customise copying of MathML expressions. 
 3697         When copying an expression tree, we only want to deepcopy the 
 3698         XML, not the additional info that we have attached to it - 
 3699         these should be copied as references to the originals. 
 3701         new_elt = copy.copy(self)
 
 3703         assert id(self) 
not in memo
 
 3704         memo[id(self)] = new_elt
 
 3706         for name, value 
in self.__dict__.iteritems():
 
 3707             name_copy = copy.deepcopy(name, memo)
 
 3708             if not name.startswith(
'_cml'):
 
 3709                 new_dict[name_copy] = copy.deepcopy(value, memo)
 
 3711                 new_dict[name_copy] = value
 
 3712         new_elt.__dict__.update(new_dict)
 
 3717         """Properly clone a MathML sub-expression. 
 3719         Makes sure siblings and parent don't get copied too. 
 3722         next_elem, par = expr.next_elem, getattr(expr, 
'xml_parent', 
None)
 
 3723         expr.next_elem = 
None   
 3724         expr.xml_parent = 
None  
 3725         new_expr = copy.deepcopy(expr) 
 
 3726         expr.next_elem = next_elem 
 
 3727         expr.xml_parent = par      
 
 3731         """Properly clone this expression. 
 3733         If register is True, then keep a link to this expression in the clone. 
 3735         clone = mathml.clone(self)
 
 3737             clone._cml_source_expr_for_clone = self
 
 3742         If this is a clone with a registered original expression, return it. 
 3743         Otherwise returns None. 
 3748         "Cache & return the enclosing component element." 
 3750             def get_ancestor(elt, name):
 
 3751                 while elt 
and elt.localName != name:
 
 3752                     elt = elt.parentNode
 
 3754             comp = get_ancestor(self, 
u'component')
 
 3759                 solver_info = get_ancestor(self, 
u'solver_info')
 
 3763                     raise ValueError(
"MathML element " + str(self) + 
" does not occur in a model component!")
 
 3765     component = property(get_component)
 
 3768         """Forget cached component and variable references in this MathML tree. 
 3770         Used by partial evaluator when moving maths to a new component, and by 
 3771         simulation protocols. 
 3775         if isinstance(elt, mathml):
 
 3776             elt._cml_component = 
None 
 3777         for child 
in self.xml_element_children(elt):
 
 3778             if hasattr(child, 
'_unset_cached_links'):
 
 3779                 child._unset_cached_links()
 
 3786         """Cache & return the enclosing model element.""" 
 3792         """Evaluate the given element. 
 3794         Tries to evaluate the given element, and raises an EvaluationError 
 3795         if this is not possible. 
 3797         if hasattr(elt, 
'evaluate') 
and callable(elt.evaluate):
 
 3798             return elt.evaluate()
 
 3799         elif elt.localName == 
u'pi':
 
 3808         """Ensure that there is an element in the XML tree giving this 
 3811         Add a new <units> element if this expression has generated units. 
 3813         If units is not None, use the given units rather than those of 
 3816         Return an attribute dictionary with the appropriate units 
 3820             return {(
u'cml:units', NSS[
u'cml']): 
u'#UNUSED#'}
 
 3823                 units = self.get_units().
extract()
 
 3825             units = self.model._get_units_obj(units)
 
 3826             if units._cml_generated 
and units.name[:3] == 
"___":
 
 3830                 for unit 
in getattr(units, 
u'unit', []):
 
 3831                     self._ensure_units_exist(unit.get_units_element())
 
 3832                     unit.units = unit.get_units_element().name
 
 3834                 msg = 
"Adding units " + units.name + 
" as " 
 3835                 units.name = units.description(cellml=
True)
 
 3836                 msg = msg + units.name
 
 3837                 DEBUG(
'partial-evaluator', msg.encode(
'UTF-8'))
 
 3838                 if units.name == units.unit.units:
 
 3840                     DEBUG(
'partial-evaluator', 
'Generated units',
 
 3841                           units.name, 
'identical to referenced units; ignoring.')
 
 3842                     assert units.get_multiplicative_factor() == 1
 
 3843                     assert units.get_offset() == 0
 
 3845                     units.xml_parent.add_units(units.name, units)
 
 3846                     units.xml_parent.xml_append(units)
 
 3847             attrs = {(
u'cml:units', NSS[
u'cml']): units.name}
 
 3850             attrs = {(
u'cml:units', NSS[
u'cml']): 
u'#FUDGE#'}
 
 3854         """Return the variable object for the given ci element. 
 3856         This method is more general than ci_elt.variable, working even 
 3857         for ci elements outside of a component.  Such elements *must* 
 3858         contain a fully qualified variable name, i.e. including the 
 3859         name of the component the variable occurs in.  This method 
 3860         handles a variety of encodings of variable names that contain 
 3864             var = ci_elt.variable
 
 3866             varname = unicode(ci_elt).strip()
 
 3867             var = cellml_variable.get_variable_object(self.model, varname)
 
 3871         """Return a list of 'variable' objects used in the given expression. 
 3873         This method doesn't make use of the dependency information 
 3874         generated when validating the model, but parses the 
 3875         mathematics afresh.  It is used to refresh the dependency 
 3876         lists after partial evaluation, and to determine dependencies 
 3877         in mathematics added outside the normal model structure 
 3878         (e.g. Jacobian calculation). 
 3880         If an ODE appears, includes the mathml_apply instance defining 
 3881         the ODE.  Otherwise all returned objects will be 
 3882         cellml_variable instances. 
 3885         if isinstance(expr, mathml_ci):
 
 3886             res.add(self.varobj(expr))
 
 3887         elif isinstance(expr, mathml_apply) 
and \
 
 3888                  expr.operator().localName == 
u'diff':
 
 3889             dep_var = self.varobj(expr.ci)
 
 3890             indep_var = self.varobj(expr.bvar.ci)
 
 3891             res.add(dep_var.get_ode_dependency(indep_var))
 
 3892         elif hasattr(expr, 
'xml_children'):
 
 3893             for child 
in expr.xml_children:
 
 3894                 res.update(self.vars_in(child))
 
 3898         """Check whether this element represents the same tree as a given element.""" 
 3899         if this 
is None: this = self
 
 3900         equal = (this.localName == other.localName
 
 3901                  and len(getattr(this, 
'xml_children', [])) == len(getattr(other, 
'xml_children', [])))
 
 3902         if equal 
and this.localName 
in [
u'cn', 
u'ci']:
 
 3903             equal = unicode(this) == unicode(other)
 
 3904         if equal 
and hasattr(this, 
'xml_children'):
 
 3905             for tc, oc 
in zip(self.xml_element_children(this), self.xml_element_children(other)):
 
 3906                 if not self.same_tree(oc, tc):
 
 3912         """Transfer our complexity to the new element. 
 3914         PE is replacing us by a new element.  If we are annotated with 
 3915         a complexity - the complexity of this expression prior to PE - 
 3916         then transfer the annotation to the new element. 
 3919             new_elt._cml_complexity = self._cml_complexity
 
 3920         except AttributeError:
 
 3924         """Adjust ancestor complexity because old_elt changed to new_elt. 
 3926         The purpose of this method is to allow us to keep track of 
 3927         what the complexity of each expression node *was* prior to PE 
 3928         being performed.  Thus we cannot just re-compute complexities, 
 3929         but must update values using the original complexities.  If a 
 3930         variable definition is instantiated, then the complexity of 
 3931         the expression containing the lookup must be adjusted to 
 3932         reflect the additional expense of evaluating the defining 
 3935         When this method is called, only new_elt is a child of self. 
 3943             new = new_elt._cml_complexity
 
 3944             old = old_elt._cml_complexity
 
 3945         except AttributeError:
 
 3950             if isinstance(elt, mathml_piecewise):
 
 3952                 ac, piece_ac = 0, []
 
 3953                 for piece 
in getattr(elt, 
u'piece', []):
 
 3954                     ac += 
child_i(piece, 2)._cml_complexity
 
 3955                     piece_ac.append(
child_i(piece, 1)._cml_complexity)
 
 3956                 if hasattr(elt, 
u'otherwise'):
 
 3957                     piece_ac.append(
child_i(elt.otherwise, 1)._cml_complexity)
 
 3959                 elt._cml_complexity = ac
 
 3960             elif hasattr(elt, 
'_cml_complexity'):
 
 3961                 elt._cml_complexity += (new - old)
 
 3962             elt = getattr(elt, 
'xml_parent', 
None)
 
 3966         """Classify variables in the given expression according to how they are used. 
 3968         In the process, compute and return a set of variables on which that expression depends. 
 3970         If dependencies_only then the variable classification will not be 
 3971         done, only dependencies will be analysed.  This is useful for doing 
 3972         a 'light' re-analysis if the dependency set has been reduced; if the 
 3973         set has increased then the topological sort of equations may need to 
 3976         The function needs_special_treatment may be supplied to override the 
 3977         default recursion into sub-trees.  It takes a single sub-tree as 
 3978         argument, and should either return the dependency set for that 
 3979         sub-tree, or None to use the default recursion.  This is used when 
 3980         re-analysing dependencies after applying lookup tables, since table 
 3981         lookups only depend on the keying variable. 
 3983         if hasattr(elt, 
'classify_variables'):
 
 3984             dependencies = elt.classify_variables(**kwargs)
 
 3986             dependencies = set()
 
 3987             needs_special_treatment = kwargs.get(
'needs_special_treatment', 
lambda e: 
None)
 
 3988             for e 
in elt.xml_element_children():
 
 3989                 child_deps = needs_special_treatment(e)
 
 3990                 if child_deps 
is None:
 
 3991                     child_deps = self.classify_child_variables(e, **kwargs)
 
 3992                 dependencies.update(child_deps)
 
 3997         super(mathml_math, self).
__init__()
 
 4002     Base class for MathML constructor elements, e.g. apply and piecewise. 
 4005         super(mathml_constructor, self).
__init__()
 
 4010         Calculate a rough estimate of the computation time for 
 4011         evaluating the given element. 
 4013         If lookup_tables is True, then assume we're using lookup tables 
 4015         If store_result is True, the complexity is saved to the 
 4016         _cml_complexity attribute. 
 4017         If algebraic is True, the complexity is calculated as a dictionary, 
 4018         mapping node types to the number of occurences of that type. 
 4020         kw[
'lookup_tables'] = kw.get(
'lookup_tables', 
False)
 
 4021         kw[
'store_result'] = kw.get(
'store_result', 
False)
 
 4022         kw[
'algebraic'] = kw.get(
'algebraic', 
False)
 
 4025         if kw[
'lookup_tables'] 
and \
 
 4026              elt.getAttributeNS(NSS[
'lut'], 
u'possible', 
u'no') == 
u'yes':
 
 4028             if hasattr(self.
rootNode, 
'num_lookup_tables'):
 
 4030                 self.rootNode.num_lookup_tables += 1
 
 4038                 ac = 2*1 + 2*1 + 1 + 1 + 3*0.7
 
 4039         elif hasattr(elt, 
'tree_complexity') \
 
 4040            and callable(elt.tree_complexity):
 
 4041             ac = elt.tree_complexity(**kw)
 
 4042         elif elt.localName 
in [
'true', 
'false', 
'cn', 
'exponentiale', 
'pi']:
 
 4043             if kw[
'algebraic']: ac[
'constant'] = 1
 
 4045         elif elt.localName == 
'ci':
 
 4046             if kw[
'algebraic']: ac[
'variable'] = 1
 
 4048         elif elt.localName 
in [
'degree', 
'logbase']:
 
 4053         if kw[
'store_result'] 
and isinstance(elt, mathml):
 
 4054             elt._cml_complexity = ac
 
 4058         """Helper method to get the binding time of a MathML element.""" 
 4059         if hasattr(elt, 
'_get_binding_time'):
 
 4061             return elt._get_binding_time()
 
 4062         elif elt.localName 
in [
u'true', 
u'false', 
u'exponentiale', 
u'pi']:
 
 4063             return BINDING_TIMES.static
 
 4066                                   " of element " + elt.localName)
 
 4069         """Helper method to get the units of a MathML element.""" 
 4070         if hasattr(elt, 
'get_units'):
 
 4073         elif hasattr(elt, 
'_cml_units') 
and elt._cml_units:
 
 4078             if elt.localName 
in [
u'false', 
u'true']:
 
 4080                     [self.component.get_units_by_name(
'cellml:boolean')],
 
 4082             elif elt.localName 
in [
u'notanumber', 
u'pi', 
u'infinity',
 
 4085                     [self.component.get_units_by_name(
'dimensionless')],
 
 4090                     u'Unsupported element "', elt.localName, 
'".']))
 
 4096         """Try to reduce the given element. 
 4098         Call the _reduce method on elt, if it has one. 
 4099         If not, do nothing (we assume elt cannot be reduced). 
 4101         if hasattr(elt, 
'_reduce') 
and callable(elt._reduce):
 
 4104             DEBUG(
'partial-evaluator', 
"Don't know how to reduce",
 
 4109         """Evaluate self and return <cn>, <true> or <false>, as appropriate.""" 
 4110         value = self.evaluate()
 
 4112             new_elt = self.xml_create_element(
u'true', NSS[
u'm'])
 
 4113         elif value 
is False:
 
 4114             new_elt = self.xml_create_element(
u'false', NSS[
u'm'])
 
 4118             new_elt = self.xml_create_element(
u'cn', NSS[
u'm'],
 
 4119                                               content=unicode(
"%.17g" % value),
 
 4124         """Update usage counts of variables used in the given expression. 
 4126         By default, increment the usage count of any variable occuring 
 4127         in a <ci> element within expr.  If remove is set to True, 
 4128         then decrement the usage counts instead. 
 4130         if isinstance(expr, mathml_ci):
 
 4132                 expr.variable._decrement_usage_count()
 
 4134                 raise NotImplementedError(
"_update_usage_counts currently only reliable for remove=True")
 
 4135                 expr.variable._used()
 
 4136         elif isinstance(expr, mathml_apply) 
and isinstance(expr.operator(),
 
 4142             for e 
in self.xml_element_children(expr):
 
 4153         Convert the text content of this element to a floating point 
 4154         value and return it.  Will handle the type attribute and, if 
 4155         relevant to the type, the sep child element, but does not yet 
 4156         handle the base attribute. 
 4158         if hasattr(self, 
u'base'):
 
 4159             raise ValueError(
'pycml does not yet support the base attribute on cn elements')
 
 4160         if hasattr(self, 
u'type'):
 
 4162                 val = float(unicode(self))
 
 4163             elif self.
type == 
u'integer':
 
 4164                 val = int(unicode(self))
 
 4165             elif self.
type == 
u'e-notation':
 
 4166                 assert len(self.xml_children) == 3
 
 4167                 assert self.xml_children[1] 
is self.sep
 
 4168                 mantissa = unicode(self.xml_children[0]).strip()
 
 4169                 exponent = unicode(self.xml_children[2]).strip()
 
 4170                 val = float(mantissa + 
'e' + exponent)
 
 4171             elif self.
type == 
u'rational':
 
 4172                 assert len(self.xml_children) == 3
 
 4173                 assert self.xml_children[1] 
is self.sep
 
 4174                 numer = int(unicode(self.xml_children[0]))
 
 4175                 denom = int(unicode(self.xml_children[2]))
 
 4178                 raise ValueError(
'Unsupported type attribute for cn element: ' 
 4181             val = float(unicode(self))
 
 4185         """Return the binding time of this expression. 
 4187         The binding time of a <cn> element is always static, 
 4188         unless the CellML is annotated otherwise. 
 4190         bt = self.getAttributeNS(NSS[
'pe'], 
u'binding_time', 
u'static')
 
 4191         return getattr(BINDING_TIMES, bt)
 
 4194         """Reduce this expression by evaluating its static parts. 
 4196         Is actually a no-op; we must have been annotated explicitly as dynamic. 
 4201         """Return the units this number is expressed in.""" 
 4202         if not self._cml_units:
 
 4203             self._cml_units = UnitsSet([self.component.get_units_by_name(self.units)], expression=self)
 
 4205             u = self._cml_units.extract()
 
 4212         """Create a new <cn> element with the given value and units.""" 
 4213         attrs = {(
u'cml:units', NSS[
u'cml']): unicode(units)}
 
 4214         new_elt = elt.xml_create_element(
u'cn', NSS[
u'm'],
 
 4216                                          content=unicode(value))
 
 4227         """Forget cached component and variable references in this MathML tree. 
 4229         Used by partial evaluator when moving maths to a new component, and by 
 4230         simulation protocols. 
 4237         """Cache & return the variable object refered to by this element.""" 
 4239             vname = unicode(self).strip()
 
 4241             self.
_cml_variable = self.component.get_variable_by_name(vname)
 
 4245         """Set the variable object referred to by this element.""" 
 4249         """Return the units of the variable represented by this element.""" 
 4254             u = self._cml_units.extract()
 
 4261         Evaluate this expression by returning the value of the 
 4262         variable it represents. 
 4264         return self.variable.get_value()
 
 4267         """Return the binding time of this expression. 
 4269         The binding time of a <ci> element is that of the variable it 
 4272         return self.variable._get_binding_time()
 
 4275         """Update the variable reference to use a canonical name.""" 
 4276         self.xml_remove_child(unicode(self))
 
 4277         if new_name 
is None:
 
 4278             new_name = self.variable.fullname(cellml=
True)
 
 4279         self.xml_append(unicode(new_name))
 
 4283         """Reduce this expression by evaluating its static parts. 
 4285         If this is a static variable, replace by its value (as a <cn> element). 
 4287         Otherwise the behaviour depends on the number of uses of this 
 4288         variable.  If there is only one, instantiate the definition of 
 4289         this variable here in place of the <ci> element, otherwise 
 4290         leave the element unchanged to avoid code duplication. 
 4293         DEBUG(
'partial-evaluator', 
"Reducing", self.variable.fullname(),
 
 4295         if bt 
is BINDING_TIMES.static:
 
 4297             attrs = {(
u'cml:units', NSS[
u'cml']): self.variable.units}
 
 4298             cn = self.xml_create_element(
u'cn', NSS[
u'm'],
 
 4299                                          content=unicode(
"%.17g" % value),
 
 4301             DEBUG(
'partial-evaluator', 
"  value =", unicode(cn))
 
 4302             self._xfer_complexity(cn)
 
 4303             self.xml_parent.xml_insert_after(self, cn)
 
 4304             self.xml_parent.xml_remove_child(self)
 
 4305             self.variable._decrement_usage_count()
 
 4307             defns = self.variable.get_dependencies()
 
 4313             if isinstance(defn, cellml_variable):
 
 4314                 if self.variable.pe_keep:
 
 4316                     DEBUG(
'partial-evaluator', 
"Keeping",
 
 4317                           self.variable.fullname())
 
 4318                     self.variable._reduce(update_usage=
True)
 
 4321                     ci = self.xml_create_element(
 
 4322                         u'ci', NSS[
u'm'], content=defn.fullname(cellml=
True))
 
 4323                     self._xfer_complexity(ci)
 
 4324                     ci._set_variable_obj(defn)
 
 4325                     DEBUG(
'partial-evaluator', 
"  to", defn.fullname())
 
 4326                     self.xml_parent.xml_insert_after(self, ci)
 
 4327                     self.xml_parent.xml_remove_child(self)
 
 4329                     self.variable._decrement_usage_count(follow_maps=
False)
 
 4332             elif isinstance(defn, mathml_apply):
 
 4333                 if (
not self.variable.pe_keep 
and 
 4334                     (self.variable.get_usage_count() == 1 
or 
 4335                      self.rootNode.partial_evaluator.is_instantiable(defn))):
 
 4339                     rhs = mathml.clone(list(defn.operands())[1])
 
 4340                     DEBUG(
'partial-evaluator', 
"  to", rhs)
 
 4341                     parent = self.xml_parent
 
 4342                     parent.xml_insert_after(self, rhs)
 
 4343                     parent.xml_remove_child(self)
 
 4344                     parent._adjust_complexity(self, rhs)
 
 4346                     defn._pe_process = 
u'remove' 
 4347                     self.variable._decrement_usage_count()
 
 4348             elif defn 
is not None:
 
 4349                 raise ValueError(
"Unexpected variable definition: " + defn.xml())
 
 4353                            needs_special_treatment=
lambda n: 
None):
 
 4354         """Classify variables in this expression according to how they are used. 
 4356         For ci elements we just return a set containing the referenced variable 
 4357         as the single dependency.  If dependencies_only is False, we also mark 
 4358         the variable as used. 
 4361         if not dependencies_only:
 
 4367         """Create a new <ci> element with the given variable name.""" 
 4368         new_elt = elt.xml_create_element(
u'ci', NSS[
u'm'],
 
 4369                                          content=unicode(variable_name))
 
 4374     QUALIFIERS = frozenset((
'degree', 
'bvar', 
'logbase',
 
 4375                             'lowlimit', 
'uplimit', 
'interval', 
'condition',
 
 4376                             'domainofapplication', 
'momentabout'))
 
 4379         """Classifications of operators.""" 
 4380         absRound = frozenset((
'abs', 
'floor', 
'ceiling', 
'rem'))
 
 4381         timesDivide = frozenset((
'times', 
'divide'))
 
 4382         plusMinus = frozenset((
'plus', 
'minus'))
 
 4383         trig = frozenset((
'sin', 
'cos', 
'tan', 
'sec', 
'csc', 
'cot',
 
 4384                           'sinh', 
'cosh', 
'tanh', 
'sech', 
'csch', 
'coth',
 
 4385                           'arcsin', 
'arccos', 
'arctan',
 
 4386                           'arcsec', 
'arccsc', 
'arccot',
 
 4387                           'arcsinh', 
'arccosh', 
'arctanh',
 
 4388                           'arcsech', 
'arccsch', 
'arccoth'))
 
 4389         elementary = frozenset((
'exp', 
'log', 
'ln')).union(trig)
 
 4390         relations = frozenset((
'eq', 
'neq', 
'gt', 
'lt', 
'geq', 
'leq'))
 
 4391         logical = frozenset((
'and', 
'or', 
'xor', 
'not'))
 
 4394         super(mathml_apply, self).
__init__()
 
 4399         """Clear the type, dependency, etc. information for this equation. 
 4401         This allows us to re-run the type & dependency analysis for the model.""" 
 4409         """Return the list of variables this expression depends on.""" 
 4413         """Return the element representing the operator being applied.""" 
 4417         """Return True iff element is a qualifier element.""" 
 4422         Return an iterable over the elements representing the qualifiers for 
 4425         quals = self.xml_element_children()
 
 4430         Return an iterable over the elements representing the operands for 
 4434         operands = self.xml_element_children()
 
 4441         Return an iterable containing a <units> element for each operand 
 4449         Evaluate this expression, and return its value, if possible. 
 4453         if hasattr(op, 
'evaluate') 
and callable(op.evaluate):
 
 4454             return op.evaluate()
 
 4456             raise EvaluationError(
"Don't know how to evaluate the operator " + op.localName)
 
 4460         Calculate a rough estimate of the computation time for 
 4461         evaluating this <apply> element. 
 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. 
 4467         If lookup_tables is True, then assume we're using lookup tables 
 4469         If algebraic is True, the complexity is calculated as a dictionary, 
 4470         mapping node types to the number of occurences of that type. 
 4472         kw[
'algebraic'] = kw.get(
'algebraic', 
False)
 
 4473         if kw[
'algebraic']: ac = {}
 
 4477         op_name = self.
operator().localName
 
 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
 
 4487                 if kw[
'algebraic']: ac[
'round'] = 1
 
 4489         elif op_name 
in OPS.elementary:
 
 4490             if kw[
'algebraic']: ac[
'elementary'] = 1
 
 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
 
 4498         elif op_name == 
'power':
 
 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
 
 4505                 if kw[
'algebraic']: ac[
'power'] = 1
 
 4507         elif op_name == 
'root':
 
 4508             if kw[
'algebraic']: ac[
'root'] = 1
 
 4510         elif op_name == 
'diff':
 
 4511             if kw[
'algebraic']: ac[
'variable'] = 1
 
 4524         """Set the units this expression should be given in. 
 4526         If these aren't our natural units (as given by an initial 
 4527         get_units) then we need to add units conversion code. 
 4531         if units 
is current_units:
 
 4535         if units 
in current_units:
 
 4539             for src_units_set, src_units 
in current_units._get_sources(units):
 
 4540                 expr = src_units_set.get_expression()
 
 4543             if not done 
and not no_act:
 
 4549             if hasattr(op, 
'_set_in_units') 
and callable(op._set_in_units):
 
 4550                 op._set_in_units(units, no_act)
 
 4553                     "Don't know how to select units for operands of operator",
 
 4554                     op.localName, 
"when its units are", units.description()]))
 
 4557         """Recursively check this expression for dimensional consistency. 
 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. 
 4563         If yes, returns a <units> element for the whole expression, based 
 4564         on the rules in appendix C.3.3. 
 4566         Throws a UnitsError if the units are inconsistent. 
 4574         operand_units_idx = itertools.izip(operand_units, itertools.count(1))
 
 4576         dimensionless = self.model.get_units_by_name(
u'dimensionless')
 
 4577         boolean = self.model.get_units_by_name(
u'cellml:boolean')
 
 4579         if op 
in self.OPS.relations | self.OPS.plusMinus:
 
 4580             our_units = operand_units.next().
copy()
 
 4582             if boolean 
in our_units:
 
 4584                     u'Operator',op,
u'has boolean operands, which does not make sense.']))
 
 4586             for u 
in operand_units:
 
 4587                 if not hasattr(self.
model, 
'_cml_special_units_converter') 
and not our_units.dimensionally_equivalent(u):
 
 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']))
 
 4593             if op 
in self.OPS.relations:
 
 4596         elif op 
in self.OPS.logical:
 
 4598             for u, i 
in operand_units_idx:
 
 4599                 if not boolean 
in u:
 
 4601                         u'Operator',op,
u'requires operands to be booleans;',
 
 4602                         u'operand',str(i),
u'has units',u.description()]))
 
 4605         elif op 
in self.OPS.elementary:
 
 4607             for u, i 
in operand_units_idx:
 
 4608                 if not u.dimensionally_equivalent(dimensionless):
 
 4610                         u'Operator',op,
u'requires operands to be dimensionless;',
 
 4611                         u'operand',str(i),
u'has units',u.description()]))
 
 4614                 if hasattr(self, 
u'logbase'):
 
 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()]))
 
 4621             our_units = 
UnitsSet([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()]))
 
 4637             if arg_units.equals(dimensionless):
 
 4638                 our_units = 
UnitsSet([dimensionless])
 
 4645                     raise UnitsError(self, 
'Unable to units check power with an exponent that can vary at run-time',
 
 4647                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 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)]),
 
 4654                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 4655                 our_units = dimensionless.simplify(arg_units, expt)
 
 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'):
 
 4664                 if not u.dimensionally_equivalent(dimensionless):
 
 4666                         u'The degree qualifier must have dimensionless units, not',u.description()]))
 
 4674             if not type(degree) 
is float:
 
 4677                     raise UnitsError(self, 
'Unable to units check root with a degree that can vary at run-time',
 
 4679                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 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)]),
 
 4685                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 4686             our_units = dimensionless.simplify(arg_units, 1/degree)
 
 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)
 
 4696                     if not u.dimensionally_equivalent(dimensionless):
 
 4698                             u'The degree qualifier must have dimensionless units, not',u.description()]))
 
 4702                 raise UnitsError(self, 
u'A diff operator must have a bvar qualifier')
 
 4706             if not type(degree) 
is float:
 
 4709                     raise UnitsError(self, 
'Unable to units check derivative with a degree that can vary at run-time',
 
 4711                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 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)]),
 
 4717                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 4718             for e 
in self.xml_element_children(self.bvar):
 
 4719                 if not e.localName == 
u'degree':
 
 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:
 
 4730                         u'Operator',op,
u'has boolean operands, which does not make sense.']))
 
 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':
 
 4739                 our_units = operand_units.next()
 
 4740                 our_units = our_units.simplify(other_units=operand_units.next(), other_exponent=-1)
 
 4743                 our_units = operand_units.next().
copy()
 
 4746             raise UnitsError(self, 
u' '.join([
u'Unsupported operator for units checking:', op]),
 
 4748                              level=logging.WARNING_TRANSLATE_ERROR)
 
 4751         if isinstance(our_units, cellml_units):
 
 4755         our_units.set_expression(self)
 
 4759         """Check the current component owns the variable being assigned to. 
 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 
 4767         Adds to the model's error list if the check fails.  This method 
 4768         always returns None. 
 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':
 
 4776             var = first_operand.variable
 
 4777             for iface 
in [
u'public_interface', 
u'private_interface']:
 
 4778                 if getattr(var, iface, 
u'none') == 
u'in':
 
 4780                         u'Variable', var.fullname(),
 
 4781                         u'is assigned to in a math element, but has its',
 
 4782                         iface, 
u'set to "in".']))
 
 4785                            dependencies_only=
False,
 
 4786                            needs_special_treatment=
lambda n: 
None):
 
 4788         Classify variables in this expression according to how they are 
 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. 
 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 
 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. 
 4810         dependencies = set()
 
 4811         ode_indep_var = 
None 
 4813         if op.localName == 
u'diff':
 
 4816             dependencies.add((op.dependent_variable, op.independent_variable))
 
 4817             if not dependencies_only:
 
 4825                 if lhs.localName == 
u'ci':
 
 4828                     var._add_dependency(self)
 
 4830                     if not dependencies_only:
 
 4833                         if t == VarTypes.Constant 
or t == VarTypes.MaybeConstant:
 
 4834                             self.model.validation_warning(
 
 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(
 
 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':
 
 4848                     diff = lhs.operator()
 
 4849                     if diff.localName == 
u'diff':
 
 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)
 
 4857                         ode_indep_var = indep
 
 4858                         if not dependencies_only:
 
 4863                         raise MathsError(self, 
u'Assignment statements are expected to be an ODE or assign to a variable.',
 
 4865                                          level=logging.WARNING_TRANSLATE_ERROR)
 
 4867                     raise MathsError(self, 
u'Assignment statements are expected to be an ODE or assign to a variable.',
 
 4869                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 4873                 dependencies.update(self.classify_child_variables(oper, dependencies_only=dependencies_only,
 
 4874                                                                   needs_special_treatment=needs_special_treatment))
 
 4881             if ode_indep_var 
in dependencies:
 
 4885                 dependencies.add(ode_indep_var)
 
 4893         """Test whether this is a top-level assignment expression.""" 
 4897         """Return True iff this is the assignment of an ODE. 
 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. 
 4908         """Return True iff this is a straightforward assignment expression. 
 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. 
 4918         """Return the variable assigned to by this assignment. 
 4920         Should only be called on a top-level assignment expression. 
 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. 
 4926         If it's an ODE, return a pair 
 4927         (dependent variable, independent variable). 
 4930             raise TypeError(
"not a top-level apply element")
 
 4935         """Return the binding time of this expression. 
 4937         The binding time will be computed recursively and cached. 
 4938         It will also be made available as an attribute in the XML. 
 4940         It is computed by taking the least upper bound of the binding 
 4941         times of our operands, unless the operator possesses an 
 4948         if hasattr(self, 
u'binding_time'):
 
 4954         if check_operator 
and hasattr(op, 
'_get_binding_time'):
 
 4958             bts = [BINDING_TIMES.static]
 
 4965         self.xml_set_attribute((
u'pe:binding_time', NSS[
u'pe']), unicode(self.
_cml_binding_time))
 
 4969         """Reduce this expression by evaluating its static parts.""" 
 4973         DEBUG(
'partial-evaluator', 
"Reducing", op.localName, getattr(self, 
u'id', 
''))
 
 4974         if check_operator 
and hasattr(op, 
'_reduce'):
 
 4978             if bt == BINDING_TIMES.static:
 
 4981                 self._xfer_complexity(new_elt)
 
 4982                 self.replace_child(self, new_elt, self.xml_parent)
 
 4985             elif bt == BINDING_TIMES.dynamic:
 
 4993         """Create a new MathML apply element, with given content. 
 4995         elt should be any element in the document. 
 4997         operator is used as the name of the first, empty, child. 
 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.) 
 5005         qualifiers specifies a list of qualifier elements. 
 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)
 
 5013             if isinstance(op, basestring):
 
 5015                 op = app.xml_create_element(
u'ci', NSS[
u'm'],
 
 5016                                             content=unicode(op))
 
 5017             elif isinstance(op, tuple):
 
 5019                 if isinstance(op[1], dict):
 
 5022                     attrs = {(
u'cml:units', NSS[
u'cml']): unicode(op[1])}
 
 5023                 op = app.xml_create_element(
u'cn', NSS[
u'm'],
 
 5025                                             content=unicode(op[0]))
 
 5034         super(mathml_piecewise, self).
__init__()
 
 5040         Calculate a rough estimate of the computation time for 
 5041         evaluating this <piecewise> element. 
 5043         The real evaluation time will generally depend on run time 
 5044         data, which makes things tricky.  Here we estimate by taking 
 5045         the sum of the complexities of the conditions and the maximum 
 5046         of the complexity of the cases, in order to obtain an upper 
 5049         If lookup_tables is True, then assume we're using lookup tables 
 5051         If algebraic is True, the complexity is calculated as a dictionary, 
 5052         mapping node types to the number of occurences of that type. 
 5054         If self.rootNode.num_lookup_tables exists, this method will 
 5055         update the count of lookup tables based on this expression, 
 5056         unless the argument 'count_tables' is False or algebraic is True. 
 5058         kw[
'algebraic'] = kw.get(
'algebraic', 
False)
 
 5059         alg = kw[
'algebraic']
 
 5060         if alg: ac, piece_dicts = {}, []
 
 5063         count_lts = hasattr(self.
rootNode, 
'num_lookup_tables') 
and kw.get(
'count_tables', 
True) 
and not alg
 
 5067             num_lts = self.rootNode.num_lookup_tables
 
 5070         for piece 
in getattr(self, 
u'piece', []):
 
 5075                 nlts = self.rootNode.num_lookup_tables
 
 5078                 piece_dicts.append(piece_ac)
 
 5081                 piece_acs.append(piece_ac)
 
 5083                 piece_num_lts.append(self.rootNode.num_lookup_tables - nlts)
 
 5084         if hasattr(self, 
u'otherwise'):
 
 5086                 nlts = self.rootNode.num_lookup_tables
 
 5089                 piece_dicts.append(ow_ac)
 
 5093                 piece_acs.append(ow_ac)
 
 5095                 piece_num_lts.append(self.rootNode.num_lookup_tables - nlts)
 
 5096         max_idx, max_piece_ac = 
max_i(piece_acs)
 
 5102             self.rootNode.num_lookup_tables -= sum(piece_num_lts)
 
 5103             self.rootNode.num_lookup_tables += piece_num_lts[max_idx]
 
 5107         """Set the units this expression should be given in. 
 5109         This is done recursively by setting the units for each option. 
 5111         We also set the units on each condition to be boolean, since 
 5112         subexpressions of the conditions may need units conversions added. 
 5118         boolean = self.model.get_units_by_name(
u'cellml:boolean')
 
 5119         for piece 
in getattr(self, 
u'piece', []):
 
 5122         if hasattr(self, 
u'otherwise'):
 
 5126         """Recursively check this expression for dimensional consistency. 
 5128         The first child elements of each <piece> and <otherwise> element 
 5129         should have dimensionally equivalent units (the resulting <units> 
 5130         element will be dimensionally equivalent to these).  The second child 
 5131         elements of each <piece> should have units of cellml:boolean. 
 5133         If consistent, returns a <units> element for the whole expression. 
 5134         Throws a UnitsError if the units are inconsistent. 
 5139         boolean = self.model.get_units_by_name(
u'cellml:boolean')
 
 5140         for piece 
in getattr(self, 
u'piece', []):
 
 5143             if not boolean 
in units:
 
 5145                     u'The second child element of a <piece> element must have' 
 5146                     u'units of cellml:boolean, not',units.description()]))
 
 5150         if hasattr(self, 
u'otherwise'):
 
 5151             value_elt = 
child_i(self.otherwise, 1)
 
 5153         for piece 
in getattr(self, 
u'piece', []):
 
 5155             if our_units 
is None:
 
 5159                 if not our_units.dimensionally_equivalent(units):
 
 5161                         u'The first child elements of children of a piecewise',
 
 5162                         u'element must have dimensionally equivalent units;',
 
 5163                         units.description(),
'and',our_units.description(),
 
 5165                 our_units.update(units)
 
 5167         if our_units 
is None:
 
 5169                 u'A piecewise element must have at least one piece or',
 
 5170                 u'otherwise child in order to have defined units.']))
 
 5173         our_units.set_expression(self)
 
 5177                            needs_special_treatment=
lambda n: 
None):
 
 5178         """Classify variables in this expression according to how they are used. 
 5180         In the process, compute and return a set of variables on which 
 5181         this expression depends. 
 5183         If dependencies_only then the variable classification will not be 
 5184         done, only dependencies will be analysed.  This is useful for doing 
 5185         a 'light' re-analysis if the dependency set has been reduced; if the 
 5186         set has increased then the topological sort of equations may need to 
 5189         The function needs_special_treatment may be supplied to override the 
 5190         default recursion into sub-trees.  It takes a single sub-tree as 
 5191         argument, and should either return the dependency set for that 
 5192         sub-tree, or None to use the default recursion.  This is used when 
 5193         re-analysing dependencies after applying lookup tables, since table 
 5194         lookups only depend on the keying variable. 
 5196         dependencies = set()
 
 5197         pieces = list(getattr(self, 
u'piece', []))
 
 5198         if hasattr(self, 
u'otherwise'):
 
 5199             pieces.append(self.otherwise)
 
 5200         for piece 
in pieces:
 
 5201             dependencies.update(self.classify_child_variables(piece, dependencies_only=dependencies_only,
 
 5202                                                               needs_special_treatment=needs_special_treatment))
 
 5206         """Evaluate this piecewise expression. 
 5208         Tests choices in the order they occur in the file. 
 5209         Only evaluates a choice if its condition evaluates to True. 
 5211         for piece 
in getattr(self, 
u'piece', []):
 
 5213             cond_value = self.
eval(condition)
 
 5214             if cond_value 
is True:
 
 5220             if hasattr(self, 
u'otherwise'):
 
 5224                     "A piecewise element where the pieces aren't mutually",
 
 5225                     "exhaustive requires an otherwise element."]))
 
 5229         """Return the binding time of this expression. 
 5231         The binding time will be computed recursively and cached. 
 5232         It will also be made available as an attribute in the XML. 
 5234         It is computed by taking the least upper bound of the binding 
 5235         times of (some of) the conditions and cases. 
 5237         Condition & case binding times are computed in the order given 
 5238         in the file.  If a condition is static with value False, its 
 5239         associated case is not considered.  If a condition is static 
 5240         with value True, subsequent conditions & cases and the 
 5241         otherwise (if present) will not be considered. 
 5247         if hasattr(self, 
u'binding_time'):
 
 5253         bts = [BINDING_TIMES.static]
 
 5254         for piece 
in getattr(self, 
u'piece', []):
 
 5257             if bt 
is BINDING_TIMES.static:
 
 5258                 cond_value = self.
eval(condition)
 
 5259                 if cond_value 
is True:
 
 5274             if hasattr(self, 
u'otherwise'):
 
 5281         self.xml_set_attribute((
u'pe:binding_time', NSS[
u'pe']),
 
 5286         """Reduce this expression by evaluating its static parts. 
 5288         Even in a dynamic conditional, where a condition is static and 
 5289         evaluates to False, the associated case is discarded. 
 5298             deletable_pieces = []
 
 5299             found_dynamic_piece = 
False 
 5300             for piece 
in getattr(self, 
u'piece', []):
 
 5303                 if bt 
is BINDING_TIMES.static:
 
 5304                     cond_value = self.
eval(condition)
 
 5305                     if cond_value 
is True:
 
 5306                         if not found_dynamic_piece:
 
 5315                         deletable_pieces.append(piece)
 
 5316                 elif bt 
is BINDING_TIMES.dynamic:
 
 5317                     found_dynamic_piece = 
True 
 5324                 for piece 
in deletable_pieces:
 
 5326                     self.xml_remove_child(piece)
 
 5328                 if hasattr(self, 
u'otherwise'):
 
 5329                     if not found_dynamic_piece:
 
 5332                         new_elt = 
child_i(self.otherwise, 1)
 
 5337         if new_elt 
is not None:
 
 5339             for piece 
in getattr(self, 
u'piece', []):
 
 5340                 if not new_elt 
is child_i(piece, 1):
 
 5345                     piece.xml_remove_child(
child_i(piece, 2))
 
 5346                     piece.xml_remove_child(new_elt)
 
 5347             if hasattr(self, 
u'otherwise') 
and \
 
 5348                not new_elt 
is child_i(self.otherwise, 1):
 
 5352             self._xfer_complexity(new_elt)
 
 5353             self.replace_child(self, new_elt, self.xml_parent)
 
 5359         """Create a new piecewise element. 
 5361         elt is any element in the current document. 
 5363         pieces is a list of pairs of expressions: (case, condition). 
 5365         otherwise, if given, is the default case. 
 5367         piecewise = elt.xml_create_element(
u'piecewise', NSS[
u'm'])
 
 5368         for piece 
in pieces:
 
 5372             piece_elt = elt.xml_create_element(
u'piece', NSS[
u'm'])
 
 5373             piece_elt.xml_append(case)
 
 5374             piece_elt.xml_append(cond)
 
 5375             piecewise.xml_append(piece_elt)
 
 5378             otherwise_elt = elt.xml_create_element(
u'otherwise', NSS[
u'm'])
 
 5379             otherwise_elt.xml_append(otherwise)
 
 5380             piecewise.xml_append(otherwise_elt)
 
 5385     """Class representing the MathML lambda construct. 
 5387     Note that we don't support lambda occuring in CellML models.  However, it is used 
 5388     for defining special units conversion rules using the protocol syntax. 
 5392         """Create a new lambda from the sequence of bvar names and expression.""" 
 5393         lambda_ = elt.xml_create_element(
u'lambda', NSS[
u'm'])
 
 5394         for bvar_name 
in bound_var_names:
 
 5395             bvar = elt.xml_create_element(
u'bvar', NSS[
u'm'])
 
 5396             bvar.xml_append(mathml_ci.create_new(elt, bvar_name))
 
 5397             lambda_.xml_append(bvar)
 
 5399         lambda_.xml_append(body_expr)
 
 5404     """Base class for MathML operator elements.""" 
 5406         """Raise an EvaluationError due to wrong operand count. 
 5408         found is the number of operands found; wanted is a list of suitable 
 5409         numbers of operands. 
 5412             "Wrong number of operands for <", self.localName, 
"> ",
 
 5413             "(found ", str(found), 
"; wanted", 
' or '.join(map(str, wanted)), 
")"]))
 
 5417     """Class representing the diff element, containing some useful methods.""" 
 5421         Return the variable object w.r.t which we are differentiating. 
 5425         apply_elt = self.xml_parent
 
 5426         if not hasattr(apply_elt.bvar, 
u'ci'):
 
 5427             raise MathsError(apply_elt, 
u'Differential operator does not have a variable element as bound variable.')
 
 5428         return apply_elt.bvar.ci.variable
 
 5433         Return the variable object being differentiated. 
 5435         apply_elt = self.xml_parent
 
 5436         operand = apply_elt.operands().next()
 
 5437         if not operand.localName == 
u'ci':
 
 5438             raise MathsError(apply_elt, 
u'Derivatives of non-variables are not supported.')
 
 5439         return operand.variable
 
 5443         Set the types of the dependent & independent variables: State for 
 5444         the dependent variable and Free for the independent variable. 
 5445         Gives a validation warning if they already have 'incompatible' 
 5449         model = self.xml_parent.model
 
 5452         if not dep.get_type() == VarTypes.Mapped 
and \
 
 5453                not hasattr(dep, 
u'initial_value'):
 
 5454             model.validation_warning(
u' '.join([
 
 5455                 u'The state variable',dep.fullname(),
 
 5456                 u'does not have an initial value given.']),
 
 5457                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 5459         if dep.get_type(follow_maps=
True) == VarTypes.Computed:
 
 5460             model.validation_warning(
u' '.join([
 
 5461                 u'The state variable',dep.fullname(),
 
 5462                 u'is also assigned to directly.']),
 
 5463                                      level=logging.WARNING_TRANSLATE_ERROR)
 
 5464         dep._set_type(VarTypes.State)
 
 5466         t = indep.get_type(follow_maps=
True)
 
 5467         if t != VarTypes.Free:
 
 5468             if t != VarTypes.Unknown:
 
 5469                 if t == VarTypes.Computed:
 
 5470                     reason = 
u'is computed in an expression.' 
 5471                 elif t == VarTypes.State:
 
 5472                     reason = 
u'is a state variable itself.' 
 5474                     reason = 
u'has an initial value specified.' 
 5475                 model.validation_warning(
u' '.join([
 
 5476                     u'The derivative of',dep.fullname(),
 
 5477                     u'is taken with respect to',indep.fullname(),
 
 5478                     u'but the latter', reason]),
 
 5479                                          level=logging.WARNING_TRANSLATE_ERROR)
 
 5481             indep._set_type(VarTypes.Free)
 
 5484         """Return the binding time of the enclosing <apply> element. 
 5486         This is the binding time of the expression defining this ODE. 
 5488         expr = self.dependent_variable.get_ode_dependency(
 
 5490         return expr._get_binding_time()
 
 5493         """Reduce this expression by evaluating its static parts. 
 5495         If the whole expression is static, proceed as normal for an 
 5496         <apply>.  Otherwise just rename the variable references.  We 
 5497         can't instantiate the definition, because there will always be 
 5498         another user - external code. 
 5500         This operator is special cased because we need to alter its 
 5501         qualifier, but mathml_apply only considers operands.  MathML 
 5502         data binding can be annoying at times! 
 5504         app = self.xml_parent
 
 5505         bt = app._get_binding_time()
 
 5506         if bt == BINDING_TIMES.static:
 
 5508             app._reduce(check_operator=
False)
 
 5511             for ci 
in [app.ci, app.bvar.ci]:
 
 5512                 ci._set_variable_obj(ci.variable.get_source_variable(recurse=
True))
 
 5517         """Construct an ODE expression: d(state_var)/d(bvar) = rhs.""" 
 5518         bvar_elt = elt.xml_create_element(
u'bvar', NSS[
u'm'])
 
 5519         bvar_elt.xml_append(mathml_ci.create_new(elt, bvar))
 
 5520         diff = mathml_apply.create_new(elt, 
u'diff', [state_var], [bvar_elt])
 
 5521         ode = mathml_apply.create_new(elt, 
u'eq', [diff, rhs])
 
 5526         """Reduce this expression by evaluating its static parts. 
 5528         If the whole expression is static, proceed as normal for an 
 5529         <apply>.  Otherwise check if we have more than one static 
 5530         operand.  If we do, we can combine them in a new static 
 5531         expression and evaluate that as a whole. 
 5533         app = self.xml_parent
 
 5534         bt = app._get_binding_time()
 
 5535         if bt == BINDING_TIMES.static 
or not self.model.get_option(
'partial_pe_commutative'):
 
 5537             app._reduce(check_operator=
False)
 
 5540             static_opers = filter(
lambda e: app._get_element_binding_time(e) == BINDING_TIMES.static,
 
 5542             if len(static_opers) > 1:
 
 5544                 for oper 
in static_opers:
 
 5545                     app.safe_remove_child(oper)
 
 5547                 new_expr = mathml_apply.create_new(self, self.localName, static_opers)
 
 5549                 app.xml_append(new_expr)
 
 5552             app._reduce(check_operator=
False)
 
 5555     """Class representing the MathML <plus> operator.""" 
 5557         super(mathml_plus, self).
__init__()
 
 5561         """Evaluate by summing the operands of the enclosing <apply>.""" 
 5562         app = self.xml_parent
 
 5563         ops = app.operands()
 
 5566             value += self.
eval(operand)
 
 5570     """Class representing the MathML <minus> operator.""" 
 5572         """Evaluate the enclosing <apply> element. 
 5574         Behaviour depends on the number of operands: we perform 
 5575         either a unary or binary minus. 
 5577         app = self.xml_parent
 
 5578         ops = list(app.operands())
 
 5580             value = -self.
eval(ops[0])
 
 5582             value = self.
eval(ops[0]) - self.
eval(ops[1])
 
 5588     """Class representing the MathML <times> operator.""" 
 5591         Evaluate by taking the product of the operands of the enclosing <apply>. 
 5593         app = self.xml_parent
 
 5594         ops = app.operands()
 
 5597             value *= self.
eval(operand)
 
 5601     """Class representing the MathML <divide> operator.""" 
 5603         """Evaluate by dividing the 2 operands of the enclosing <apply>.""" 
 5604         app = self.xml_parent
 
 5605         ops = list(app.operands())
 
 5608         numer = self.
eval(ops[0])
 
 5609         denom = self.
eval(ops[1])
 
 5613         """Reduce this expression by evaluating its static parts. 
 5615         If the whole expression is static, proceed as normal for an 
 5616         <apply>.  If just the denominator is static, transform the 
 5617         expression into a multiplication. 
 5619         app = self.xml_parent
 
 5620         bt = app._get_binding_time()
 
 5621         if bt == BINDING_TIMES.static:
 
 5623             app._reduce(check_operator=
False)
 
 5626             ops = list(app.operands())
 
 5629             bt = app._get_element_binding_time(ops[1])
 
 5630             if bt == BINDING_TIMES.static:
 
 5632                 dummy = self.xml_create_element(
u'dummy', NSS[
u'm'])
 
 5633                 app.replace_child(ops[1], dummy)
 
 5634                 new_expr = mathml_apply.create_new(
 
 5635                     self, 
u'divide', [(
u'1', 
u'dimensionless'),
 
 5637                 app.replace_child(dummy, new_expr)
 
 5638                 app._reduce_elt(new_expr)
 
 5640                 times = self.xml_create_element(
u'times', NSS[
u'm'])
 
 5641                 app.replace_child(self, times)
 
 5643                 app._reduce(check_operator=
False)
 
 5646                 app._reduce(check_operator=
False)
 
 5649     """Class representing the MathML <exp> operator.""" 
 5651         """Return e to the power of the single operand.""" 
 5652         app = self.xml_parent
 
 5653         ops = list(app.operands())
 
 5656         return math.exp(self.
eval(ops[0]))
 
 5659     """Class representing the MathML <ln> operator.""" 
 5661         """Return the natural logarithm of the single operand.""" 
 5662         app = self.xml_parent
 
 5663         ops = list(app.operands())
 
 5666         return math.log(self.
eval(ops[0]))
 
 5669     """Class representing the MathML <log> operator.""" 
 5671         """Return the logarithm of the single operand.""" 
 5672         app = self.xml_parent
 
 5673         ops = list(app.operands())
 
 5676         if hasattr(app, 
u'logbase'):
 
 5677             base = self.
eval(app.logbase)
 
 5680         return math.log(self.
eval(ops[0]), base)
 
 5683     """Class representing the MathML <abs> operator.""" 
 5685         """Return the absolute value of the single operand.""" 
 5686         app = self.xml_parent
 
 5687         ops = list(app.operands())
 
 5690         return abs(self.
eval(ops[0]))
 
 5693     """Class representing the MathML <power> operator.""" 
 5695         """Set the units of the application of this operator. 
 5697         Set the exponent to have units of dimensionless, and the operand to 
 5698         have an arbitrary member of its possible units set. 
 5700         Where these mean the <apply> doesn't have the given units, wrap it 
 5701         in suitable units conversion mathematics. 
 5703         app = self.xml_parent
 
 5704         defn_units_set = app.get_units()
 
 5705         defn_units = defn_units_set.extract()
 
 5706         app._add_units_conversion(app, defn_units, units, no_act)
 
 5709             app._cml_units = defn_units
 
 5711         dimensionless = app.model.get_units_by_name(
'dimensionless')
 
 5712         ops = list(app.operands())
 
 5715         for src_units_set, src_units 
in defn_units_set._get_sources(defn_units):
 
 5716             expr = src_units_set.get_expression()
 
 5721         """Return the first operand to the power of the second.""" 
 5722         app = self.xml_parent
 
 5723         ops = list(app.operands())
 
 5726         return self.
eval(ops[0]) ** self.
eval(ops[1])
 
 5729         """Reduce this expression by evaluating its static parts. 
 5731         If the whole expression is static, proceed as normal for an <apply>. 
 5732         Otherwise check if the exponent is static and the expression being exponentiated 
 5733         is a <ci>.  If so, and the exponent is equal to 2, 3, or 4, convert the expression 
 5734         to a multiplication. 
 5736         app = self.xml_parent
 
 5737         bt = app._get_binding_time()
 
 5739         if bt != BINDING_TIMES.static 
and self.model.get_option(
'pe_convert_power'):
 
 5740             base, expt = list(app.operands())
 
 5741             expt_bt = app._get_element_binding_time(expt)
 
 5742             if expt_bt == BINDING_TIMES.static 
and isinstance(base, mathml_ci):
 
 5743                 expt_val = self.
eval(expt)
 
 5744                 if expt_val 
in [2,3,4]:
 
 5746                     app.safe_remove_child(base)
 
 5748                     for _ 
in range(1, expt_val):
 
 5749                         operands.append(base.clone_self())
 
 5750                         base.variable._used()
 
 5751                     new_app = mathml_apply.create_new(app, 
u'times', operands)
 
 5752                     app.replace_child(app, new_app, app.xml_parent)
 
 5758             app._reduce(check_operator=
False)
 
 5761     """Class representing the MathML <root> operator.""" 
 5763         """Set the units of the application of this operator. 
 5765         Set the degree to have units of dimensionless, and the operand to 
 5766         have an arbitrary member of its possible units set. 
 5768         Where these mean the <apply> doesn't have the given units, wrap it 
 5769         in suitable units conversion mathematics. 
 5771         app = self.xml_parent
 
 5772         defn_units_set = app.get_units()
 
 5773         defn_units = defn_units_set.extract()
 
 5774         app._add_units_conversion(app, defn_units, units, no_act)
 
 5777             app._cml_units = defn_units
 
 5779         if hasattr(app, 
u'degree'):
 
 5780             dimensionless = app.model.get_units_by_name(
'dimensionless')
 
 5783         for src_units_set, src_units 
in defn_units_set._get_sources(defn_units):
 
 5784             expr = src_units_set.get_expression()
 
 5789         Return the operand to the given degree, if present. 
 5790         Otherwise return the square root of the operand. 
 5792         app = self.xml_parent
 
 5793         ops = list(app.operands())
 
 5796         if hasattr(app, 
u'degree'):
 
 5797             degree = self.
eval(app.degree)
 
 5800         return self.
eval(ops[0]) ** (1/degree)
 
 5803     """Class representing the MathML <and> operator.""" 
 5805         """Return the logical conjunction of the operands. 
 5807         Evaluates operands in the order given in the file, and will 
 5808         short-circuit at the first which evaluates to False. 
 5810         app = self.xml_parent
 
 5811         ops = app.operands()
 
 5814             value = value 
and self.
eval(operand)
 
 5819         """Return the binding time of the enclosing <apply> element. 
 5821         Short-circuit if a static False operand occurs before any dynamic 
 5822         operands, returning static.  Otherwise return the least upper bound 
 5823         of operand binding times, as usual. 
 5825         app = self.xml_parent
 
 5826         bts = [BINDING_TIMES.static]
 
 5827         for operand 
in app.operands():
 
 5828             bt = app._get_element_binding_time(operand)
 
 5829             if bt 
is BINDING_TIMES.static:
 
 5830                 value = self.
eval(operand)
 
 5831                 if not value 
and len(bts) == 1:
 
 5842     """Class representing the MathML <or> operator.""" 
 5844         """Return the logical disjunction of the operands. 
 5846         Evaluates operands in the order given in the file, and will 
 5847         short-circuit at the first which evaluates to True. 
 5849         app = self.xml_parent
 
 5850         ops = app.operands()
 
 5853             value = value 
or self.
eval(operand)
 
 5858         """Return the binding time of the enclosing <apply> element. 
 5860         Short-circuit if a static True operand occurs before any dynamic 
 5861         operands, returning static.  Otherwise return the least upper bound 
 5862         of operand binding times, as usual. 
 5864         app = self.xml_parent
 
 5865         bts = [BINDING_TIMES.static]
 
 5866         for operand 
in app.operands():
 
 5867             bt = app._get_element_binding_time(operand)
 
 5868             if bt 
is BINDING_TIMES.static:
 
 5869                 value = self.
eval(operand)
 
 5870                 if value 
and len(bts) == 1:
 
 5881     """Class representing the MathML <leq> operator.""" 
 5884         Return True iff the value of the first operand is 
 5885         less than or equal to the value of the second. 
 5887         app = self.xml_parent
 
 5888         ops = list(app.operands())
 
 5891         return self.
eval(ops[0]) <= self.
eval(ops[1])
 
 5894     """Class representing the MathML <lt> operator.""" 
 5897         Return True iff the value of the first operand is 
 5898         less than the value of the second. 
 5900         app = self.xml_parent
 
 5901         ops = list(app.operands())
 
 5904         return self.
eval(ops[0]) < self.
eval(ops[1])
 
 5907     """Class representing the MathML <geq> operator.""" 
 5910         Return True iff the value of the first operand is 
 5911         greater than or equal to the value of the second. 
 5913         app = self.xml_parent
 
 5914         ops = list(app.operands())
 
 5917         return self.
eval(ops[0]) >= self.
eval(ops[1])
 
 5920     """Class representing the MathML <gt> operator.""" 
 5923         Return True iff the value of the first operand is 
 5924         greater than the value of the second. 
 5926         app = self.xml_parent
 
 5927         ops = list(app.operands())
 
 5930         return self.
eval(ops[0]) > self.
eval(ops[1])
 
 5933     """Class representing the MathML <neq> operator.""" 
 5935         """Evaluate the enclosing <apply> element. 
 5937         Return True iff the 2 operands are not equal. 
 5939         app = self.xml_parent
 
 5940         ops = list(app.operands())
 
 5944         return (self.
eval(ops[0]) != self.
eval(ops[1]))
 
 5947     """Class representing the MathML <eq> operator.""" 
 5949         """Return True iff the enclosing <apply> is a top-level expression.""" 
 5950         return self.xml_parent.is_top_level()
 
 5953         """Set the units of the application of this operator. 
 5955         If this is a top-level <eq/>, then force the RHS to take the units 
 5956         of the LHS.  Otherwise, behave as for other relational operators. 
 5959             ops = self.xml_parent.operands()
 
 5961             lhs_units = lhs.get_units().
extract()
 
 5965                 self.xml_parent._cml_units = units
 
 5970         """Evaluate the enclosing <apply> element. 
 5972         The behaviour depends on whether the enclosing <apply> is a 
 5973         top-level expression or not, i.e. whether this is an 
 5974         assignment or a comparison. 
 5976         If an assignment, evaluate the RHS, assign the value to 
 5977         the variable on the LHS, and return it. 
 5979         If a comparison, return True iff the 2 operands are equal. 
 5981         app = self.xml_parent
 
 5982         ops = list(app.operands())
 
 5988             value = self.
eval(ops[1])
 
 5989             var = app.assigned_variable()
 
 5990             if app.is_assignment():
 
 5991                 var.set_value(value)
 
 5993                 indepvar = var[1].get_source_variable(recurse=
True)
 
 5994                 var[0].set_value(value, ode=indepvar)
 
 5999             value = (self.
eval(ops[0]) == self.
eval(ops[1]))
 
 6003         """Return the binding time of the enclosing <apply> element. 
 6005         If this is a top-level expression, then only recurse into the RHS, 
 6006         otherwise proceed as normal for an apply. 
 6008         There is one further special case: if this is a top-level 
 6009         expression and the variable assigned to is annotated to be 
 6010         kept in the specialised model, then the expression is dynamic. 
 6012         app = self.xml_parent
 
 6014             annotated_as_kept = 
False 
 6016                 DEBUG(
'partial-evaluator', 
"BT ODE",
 
 6017                       map(
lambda v: v.fullname(), app.assigned_variable()))
 
 6019                 DEBUG(
'partial-evaluator', 
"BT expr",
 
 6020                       app.assigned_variable().fullname())
 
 6021                 if app.assigned_variable().pe_keep:
 
 6022                     annotated_as_kept = 
True 
 6023             ops = list(app.operands())
 
 6027             bt = app._get_element_binding_time(rhs)
 
 6028             if annotated_as_kept:
 
 6029                 bt = BINDING_TIMES.dynamic
 
 6031             bt = app._get_binding_time(check_operator=
False)
 
 6035         """Reduce this expression by evaluating its static parts. 
 6037         If this is a top-level assignment, then just reduce the RHS. 
 6038         Otherwise proceed as normal for an <apply>. 
 6040         app = self.xml_parent
 
 6042             ops = list(app.operands())
 
 6046             app._reduce_elt(rhs)
 
 6048             app._reduce(check_operator=
False)
 
 6052         """Return the right hand side of this expression. 
 6054         Should only be called if we're actually an assignment. 
 6057             ops = self.xml_parent.operands()
 
 6061             raise ValueError(
"Not an assignment expression.")
 
 6064         """Return the left hand side of this expression. 
 6066         Should only be called if we're actually an assignment. 
 6069             ops = self.xml_parent.operands()
 
 6072             raise ValueError(
"Not an assignment expression.")
 
 6075     """Class representing the MathML <rem> operator.""" 
 6077         """Return the remainder when the first operand is divided by the second.""" 
 6078         app = self.xml_parent
 
 6079         ops = list(app.operands())
 
 6082         return self.
eval(ops[0]) % self.
eval(ops[1])
 
 6085     """Class representing the MathML <logbase> element.""" 
 6087         """Evaluate this element, by evaluating its child.""" 
 6091     """Class representing the MathML <degree> element.""" 
 6093         """Evaluate this element, by evaluating its child.""" 
 6097     """Class representing the MathML <otherwise> element. 
 6099     Only defined to make it inherit from mathml. 
 6103 class mathml_piece(mathml):
 
 6104     """Class representing the MathML <piece> element. 
 6106     Only defined to make it inherit from mathml. 
 6111     """Class representing the MathML <sin> operator.""" 
 6113         """Return the sine of the single operand.""" 
 6114         app = self.xml_parent
 
 6115         ops = list(app.operands())
 
 6118         return math.sin(self.
eval(ops[0]))
 
 6121     """Class representing the MathML <cos> operator.""" 
 6123         """Return the cosine of the single operand.""" 
 6124         app = self.xml_parent
 
 6125         ops = list(app.operands())
 
 6128         return math.cos(self.
eval(ops[0]))
 
 6131     """Class representing the MathML <tan> operator.""" 
 6133         """Return the tangent of the single operand.""" 
 6134         app = self.xml_parent
 
 6135         ops = list(app.operands())
 
 6138         return math.tan(self.
eval(ops[0]))
 
 6141     """Class representing the MathML <arcsin> operator.""" 
 6143         """Return the arc sine of the single operand, in radians.""" 
 6144         app = self.xml_parent
 
 6145         ops = list(app.operands())
 
 6148         return math.asin(self.
eval(ops[0]))
 
 6151     """Class representing the MathML <arccos> operator.""" 
 6153         """Return the arc cosine of the single operand, in radians.""" 
 6154         app = self.xml_parent
 
 6155         ops = list(app.operands())
 
 6158         return math.acos(self.
eval(ops[0]))
 
 6161     """Class representing the MathML <arctan> operator.""" 
 6163         """Return the arc tangent of the single operand, in radians.""" 
 6164         app = self.xml_parent
 
 6165         ops = list(app.operands())
 
 6168         return math.atan(self.
eval(ops[0]))
 
def search_for_assignments
def _build_units_dictionary
def wrong_number_of_operands
def clear_dependency_info
def _update_ode_dependency
def _validate_component_hierarchies
def _has_child_components
def set_is_derived_quantity
def set_rdf_annotation_from_boolean
def clear_dependency_info
def dimensionally_equivalent
def build_component_hierarchy
def get_multiplicative_factor
def do_binding_time_analysis
def _get_element_binding_time
def _set_element_in_units
def _check_variable_mappings
def get_original_of_clone
def get_all_expr_dependencies
def get_variable_by_cmeta_id
def get_validation_errors
def set_is_modifiable_parameter
def _set_parent_component
_cml_ode_has_free_var_on_rhs
def get_validation_warnings
def _rel_error_ok
def eq(self, other): """Compare 2 units elements for equality. 
def get_component_by_name
def remove_rdf_annotations
def show_xml_context_only
def _check_maths_name_references
def _add_sorted_assignment
_cml_source_expr_for_clone
def calculate_extended_dependencies
def _check_variable_units_exist
def _add_units_conversion
def dimensionally_equivalent
_cml_sorting_variables_stack
def make_xml_binder
Helpful utility functions #. 
def is_modifiable_parameter
def build_name_dictionaries
def set_is_output_variable
def add_units_conversions
_cml_depends_on
Move the definition to this component defn._unset_cached_links() defn.xml_parent.xml_remove_child(def...
def get_variable_by_oxmeta_name
def classify_child_variables
def _check_connection_units
def _decrement_usage_count
def _build_units_dictionary
def _check_dimensional_consistency
def get_variables_by_ontology_term
def get_multiplicative_factor
def ignore_component_name