Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
Public Member Functions | Public Attributes | Private Member Functions | List of all members
CellMLToNektar.optimize.PartialEvaluator Class Reference

Partial Evaluation #. More...

Inheritance diagram for CellMLToNektar.optimize.PartialEvaluator:
Inheritance graph
[legend]
Collaboration diagram for CellMLToNektar.optimize.PartialEvaluator:
Collaboration graph
[legend]

Public Member Functions

def is_instantiable
 
def parteval
 

Public Attributes

 doc
 
 solver_info
 
 lookup_tables_analyser
 

Private Member Functions

def _debug
 
def _expr_lhs
 
def _describe_expr
 
def _process_ci_elts
 
def _rename_var
 
def _do_reduce_eval_loop
 
def _reduce_evaluate_expression
 
def _get_assignment_exprs
 
def _is_source_of
 
def _check_retargetting
 

Detailed Description

Partial Evaluation #.

Perform partial evaluation of a CellML model.

Definition at line 57 of file optimize.py.

Member Function Documentation

def CellMLToNektar.optimize.PartialEvaluator._check_retargetting (   self,
  ci_elt 
)
private
Check if this new variable reference means a retarget needs to change.

A retarget occurs when a kept dynamic mapped variable is changed to computed
because its source variable(s) are only used once and are not kept.  But new
mathematics may use one or more of those sources, in which case we need to
revert the retarget.

Definition at line 207 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._debug(), CellMLToNektar.cellml_metadata.RdfProcessor._debug(), and CellMLToNektar.optimize.PartialEvaluator._is_source_of().

208  def _check_retargetting(self, ci_elt):
209  """Check if this new variable reference means a retarget needs to change.
210 
211  A retarget occurs when a kept dynamic mapped variable is changed to computed
212  because its source variable(s) are only used once and are not kept. But new
213  mathematics may use one or more of those sources, in which case we need to
214  revert the retarget.
215  """
216  # Is this a retarget?
217  var = ci_elt.variable
218  root_defn = var.get_source_variable(recurse=True).get_dependencies()
219  if root_defn:
220  root_defn = root_defn[0]
221  else:
222  return
223  if (isinstance(root_defn, mathml_apply)
224  and hasattr(root_defn, '_pe_process')
225  and root_defn._pe_process == 'retarget'):
226  # Are we a source of the 'new' assignee?
227  assignee = root_defn._cml_assigns_to
228  if not self._is_source_of(assignee, var):
229  if var.get_type() == VarTypes.Computed:
230  # We were the original source variable; stop retargetting
231  self._debug('Ceasing re-target of', root_defn, 'to', assignee)
232  del root_defn._pe_process
233  root_defn._cml_assigns_to = var
234  else:
235  # Re-target to var instead
236  self._debug('Changing re-target of', root_defn, 'from', assignee, 'to', var)
237  var._cml_var_type = VarTypes.Computed
238  root_defn._cml_assigns_to = var
239  var._cml_depends_on = [root_defn]
240  var._cml_source_var = None
241  # assignee should now map to var
242  assignee._cml_var_type = VarTypes.Mapped
243  assignee._cml_source_var = var
244  assignee._cml_depends_on = [var]
def CellMLToNektar.optimize.PartialEvaluator._debug (   self,
  args 
)
private
Output debug info from the PE process.

Definition at line 59 of file optimize.py.

Referenced by CellMLToNektar.optimize.PartialEvaluator._check_retargetting(), CellMLToNektar.optimize.PartialEvaluator._do_reduce_eval_loop(), CellMLToNektar.optimize.PartialEvaluator._reduce_evaluate_expression(), and CellMLToNektar.optimize.PartialEvaluator._rename_var().

59 
60  def _debug(self, *args):
61  """Output debug info from the PE process."""
62  logger = logging.getLogger('partial-evaluator')
63  logger.debug(' '.join(map(str, args)))
def CellMLToNektar.optimize.PartialEvaluator._describe_expr (   self,
  expr 
)
private
Describe this expression for debug info.

Definition at line 72 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._expr_lhs().

Referenced by CellMLToNektar.optimize.PartialEvaluator._reduce_evaluate_expression().

72 
73  def _describe_expr(self, expr):
74  """Describe this expression for debug info."""
75  if isinstance(expr, mathml_apply):
76  if expr.is_assignment() or expr.is_ode():
77  return self._expr_lhs(expr)
78  else:
79  return '[nested apply]'
80  elif isinstance(expr, mathml_ci):
81  return expr.variable.fullname()
82  elif isinstance(expr, mathml_cn):
83  return u'cn[' + unicode(expr) + u']'
84  else:
85  return '[unknown]'
def CellMLToNektar.optimize.PartialEvaluator._do_reduce_eval_loop (   self,
  expr_source 
)
private
Do the reduce/evaluate loop.

expr_source is a callable that returns an iterable over expressions.

Definition at line 103 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._debug(), CellMLToNektar.cellml_metadata.RdfProcessor._debug(), and CellMLToNektar.optimize.PartialEvaluator._reduce_evaluate_expression().

104  def _do_reduce_eval_loop(self, expr_source):
105  """Do the reduce/evaluate loop.
106 
107  expr_source is a callable that returns an iterable over expressions.
108  """
109  while True:
110  self.doc.model._pe_repeat = u'no'
111  for expr in list(expr_source()):
112  self._reduce_evaluate_expression(expr)
113  if self.doc.model._pe_repeat == u'no':
114  break
115  self._debug("----- looping -----")
116  del self.doc.model._pe_repeat
def CellMLToNektar.optimize.PartialEvaluator._expr_lhs (   self,
  expr 
)
private
Display the LHS of this expression.

Definition at line 64 of file optimize.py.

Referenced by CellMLToNektar.optimize.PartialEvaluator._describe_expr().

64 
65  def _expr_lhs(self, expr):
66  """Display the LHS of this expression."""
67  lhs = expr.assigned_variable()
68  if isinstance(lhs, cellml_variable):
69  return lhs.fullname()
70  else:
71  return lhs[0].fullname() + u'/' + lhs[1].fullname()
def CellMLToNektar.optimize.PartialEvaluator._get_assignment_exprs (   self,
  skip_solver_info = True 
)
private
Get an iterable over all assignments in the model that are mathml_apply instances.

Definition at line 160 of file optimize.py.

161  def _get_assignment_exprs(self, skip_solver_info=True):
162  """Get an iterable over all assignments in the model that are mathml_apply instances."""
163  if not skip_solver_info:
164  skip = set()
165  else:
166  skip = set(self.solver_info.get_modifiable_mathematics())
167  for e in self.doc.model.get_assignments():
168  if isinstance(e, mathml_apply) and e not in skip:
169  assert e.is_ode() or e.is_assignment()
170  yield e
def CellMLToNektar.optimize.PartialEvaluator._is_source_of (   self,
  v1,
  v2 
)
private
Test if v1 is a source of the mapped variable v2.

Definition at line 198 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._is_source_of().

Referenced by CellMLToNektar.optimize.PartialEvaluator._check_retargetting(), and CellMLToNektar.optimize.PartialEvaluator._is_source_of().

199  def _is_source_of(self, v1, v2):
200  """Test if v1 is a source of the mapped variable v2."""
201  if v1 is v2:
202  return True
203  elif v2.get_type() == VarTypes.Mapped:
204  return self._is_source_of(v1, v2.get_source_variable())
205  else:
206  return False
def CellMLToNektar.optimize.PartialEvaluator._process_ci_elts (   self,
  elt,
  func 
)
private
Apply func to all ci elements in the tree rooted at elt.

Definition at line 86 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._process_ci_elts().

Referenced by CellMLToNektar.translators.ConfigurationStore._find_transmembrane_currents_from_voltage_ode(), CellMLToNektar.optimize.PartialEvaluator._process_ci_elts(), CellMLToNektar.translators.ConfigurationStore._process_ci_elts(), and CellMLToNektar.optimize.PartialEvaluator.is_instantiable().

86 
87  def _process_ci_elts(self, elt, func):
88  """Apply func to all ci elements in the tree rooted at elt."""
89  if isinstance(elt, mathml_ci):
90  func(elt)
91  else:
92  for e in elt.xml_element_children():
93  self._process_ci_elts(e, func)
def CellMLToNektar.optimize.PartialEvaluator._reduce_evaluate_expression (   self,
  expr 
)
private
Reduce or evaluate a single expression.

Definition at line 117 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._debug(), CellMLToNektar.cellml_metadata.RdfProcessor._debug(), and CellMLToNektar.optimize.PartialEvaluator._describe_expr().

Referenced by CellMLToNektar.optimize.PartialEvaluator._do_reduce_eval_loop().

118  def _reduce_evaluate_expression(self, expr):
119  """Reduce or evaluate a single expression."""
120  if hasattr(expr, '_pe_process'):
121  # This expression has been reduced or evaluated already, but needs further
122  # processing later so hasn't been removed yet.
123  return
124  if expr._get_binding_time() is BINDING_TIMES.static:
125  # Evaluate
126  try:
127  value = expr.evaluate() # Needed here for the is_assignment case
128  except:
129  print "Error evaluating", self._describe_expr(expr)
130  raise
131  self._debug("Evaluated", self._describe_expr(expr), "to", value)
132  if isinstance(expr, mathml_apply):
133  if expr.is_ode():
134  # Replace the RHS with a <cn> element giving the value
135  rhs = expr.eq.rhs
136  new_elt = expr._eval_self()
137  expr.xml_insert_after(rhs, new_elt)
138  expr.xml_remove_child(rhs)
139  elif expr.is_assignment():
140  # The variable assigned to will have its initial_value set,
141  # so we don't need the expression any more. Flag it for removal.
142  expr._pe_process = u'remove'
143  else:
144  # Replace the expression with a <cn> element giving the value
145  new_elt = expr._eval_self()
146  expr.xml_parent.xml_insert_after(expr, new_elt)
147  expr.xml_parent.xml_remove_child(expr)
148  else:
149  # Replace the expression with a <cn> element giving the value
150  expr._reduce()
151  # Update variable usage counts for the top-level apply case
152  if isinstance(expr, mathml_apply):
153  if expr.is_ode() or expr.is_assignment():
154  expr._update_usage_counts(expr.eq.rhs, remove=True)
155  else:
156  expr._update_usage_counts(expr, remove=True)
157  else:
158  # Reduce
159  expr._reduce()
def CellMLToNektar.optimize.PartialEvaluator._rename_var (   self,
  elt 
)
private
Change this ci element to use a canonical name.

Definition at line 94 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._debug(), and CellMLToNektar.cellml_metadata.RdfProcessor._debug().

94 
95  def _rename_var(self, elt):
96  """Change this ci element to use a canonical name."""
97  if elt.xml_parent.localName == u'bvar':
98  # The free variable in a derivative must refer directly to the ultimate source,
99  # since this is assumed in later stages and in code generation.
100  elt._set_variable_obj(elt.variable.get_source_variable(recurse=True))
101  elt._rename()
102  self._debug("Using canonical name", unicode(elt))
def CellMLToNektar.optimize.PartialEvaluator.is_instantiable (   self,
  expr 
)
Determine whether special conditions mean that this assignment can be instantiated.

Normally an assignment can only be instantiated if the assigned-to variable is used only
once, in order to avoid code duplication.
However, if the definition under consideration for instantiation is a function only of a
single LT keying variable (and we will do LT) then code duplication doesn't really matter,
since the whole expression will be converted to a table anyway.  So we should instantiate
regardless of multiple uses in this case.

Note: this does check that only a single keying variable appears, but doesn't check for
the presence of expensive functions.  Of course, if there aren't any expensive functions,
the code duplication isn't that worrying.

Definition at line 171 of file optimize.py.

References CellMLToNektar.optimize.PartialEvaluator._process_ci_elts(), and CellMLToNektar.optimize.PartialEvaluator.lookup_tables_analyser.

172  def is_instantiable(self, expr):
173  """Determine whether special conditions mean that this assignment can be instantiated.
174 
175  Normally an assignment can only be instantiated if the assigned-to variable is used only
176  once, in order to avoid code duplication.
177  However, if the definition under consideration for instantiation is a function only of a
178  single LT keying variable (and we will do LT) then code duplication doesn't really matter,
179  since the whole expression will be converted to a table anyway. So we should instantiate
180  regardless of multiple uses in this case.
181 
182  Note: this does check that only a single keying variable appears, but doesn't check for
183  the presence of expensive functions. Of course, if there aren't any expensive functions,
184  the code duplication isn't that worrying.
185  """
186  instantiate = False
187  if self.lookup_tables_analyser and self.doc.model.get_option('pe_instantiate_tables'):
188  keying_vars = set()
189  all_keying = [True]
190  def func(ci_elt):
191  if self.lookup_tables_analyser.is_keying_var(ci_elt.variable):
192  keying_vars.add(ci_elt.variable)
193  else:
194  all_keying[0] = False
195  self._process_ci_elts(expr.eq.rhs, func)
196  instantiate = len(keying_vars) == 1 and all_keying[0]
197  return instantiate
def CellMLToNektar.optimize.PartialEvaluator.parteval (   self,
  doc,
  solver_info,
  lookup_tables_analyser = None 
)
Do the partial evaluation.

Definition at line 245 of file optimize.py.

246  def parteval(self, doc, solver_info, lookup_tables_analyser=None):
247  """Do the partial evaluation."""
248  self.doc = doc
249  self.solver_info = solver_info
250  self.lookup_tables_analyser = lookup_tables_analyser
251  if lookup_tables_analyser:
252  lookup_tables_analyser.doc = doc
253  doc.partial_evaluator = self
254  # Do BTA and reduce/eval of main model
255  doc.model.do_binding_time_analysis()
257 
258  if solver_info.has_modifiable_mathematics():
259  # Do BTA and reduce/eval of solver info section
260  for expr in solver_info.get_modifiable_mathematics():
261  if not (isinstance(expr, mathml_apply) and expr.is_top_level()):
262  self._process_ci_elts(expr, lambda ci: ci.variable._used())
263  self._process_ci_elts(expr, self._check_retargetting)
264  solver_info.do_binding_time_analysis()
265  self._do_reduce_eval_loop(solver_info.get_modifiable_mathematics)
266 
267  # Process flagged expressions
268  for expr in list(self._get_assignment_exprs()):
269  if hasattr(expr, '_pe_process'):
270  if expr._pe_process == u'remove':
271  if (expr._get_binding_time() == BINDING_TIMES.dynamic and
272  isinstance(expr._cml_assigns_to, cellml_variable) and
273  expr._cml_assigns_to.get_usage_count() > 1):
274  self._debug("Keeping", repr(expr), "due to SolverInfo")
275  continue
276  expr.xml_parent.xml_remove_child(expr)
277  self.doc.model._remove_assignment(expr)
278  elif expr._pe_process == u'retarget':
279  lhs = expr.eq.lhs
280  var = expr._cml_assigns_to
281  ci = mathml_ci.create_new(lhs, var.fullname(cellml=True))
282  self._debug('Re-targetting', lhs, var, ci)
283  ci._set_variable_obj(var)
284  lhs.xml_parent.xml_insert_after(lhs, ci)
285  lhs.xml_parent.xml_remove_child(lhs)
286 
287  # Use canonical variable names in all ci elements
288  for expr in list(self._get_assignment_exprs(False)):
289  # If the assigned-to variable isn't used or kept, remove the assignment
290  if isinstance(expr.eq.lhs, mathml_ci):
291  var = expr.eq.lhs.variable
292  if not (var.get_usage_count() or var.pe_keep):
293  expr.xml_parent.xml_remove_child(expr)
294  doc.model._remove_assignment(expr)
295  continue
296  self._process_ci_elts(expr, self._rename_var)
297  for expr in solver_info.get_modifiable_mathematics():
298  self._process_ci_elts(expr, self._rename_var)
299  solver_info.use_canonical_variable_names()
300 
301  # Tidy up kept variables, in case they aren't referenced in an eq'n.
302  for var in doc.model.get_all_variables():
303  if var.pe_keep:
304  var._reduce()
305 
306  # Remove unused variables
307  for var in list(doc.model.get_all_variables()):
308  assert var.get_usage_count() >= 0
309  if var.get_usage_count() == 0 and not var.pe_keep:
310  var.xml_parent._del_variable(var)
311 
312  # Collapse into a single component
313  new_comp = cellml_component.create_new(doc, u'c')
314  new_comp._cml_created_by_pe = True
315  old_comps = list(getattr(doc.model, u'component', []))
316  doc.model._add_component(new_comp)
317  # We iterate over a copy of the component list so we can delete components
318  # from the model in this loop, and so the new component exists in the model
319  # so we can add content to it.
320  for comp in old_comps:
321  # Move relevant contents into new_comp
322  for units in list(getattr(comp, u'units', [])):
323  # Copy all <units> elements
324  # TODO: Just generate the ones we need, using _ensure_units_exist
325  comp.xml_remove_child(units)
326  new_comp.xml_append(units)
327  for var in list(getattr(comp, u'variable', [])):
328  # Only move used source variables
329  self._debug('Variable', var.fullname(), 'usage', var.get_usage_count(),
330  'type', var.get_type(), 'kept', var.pe_keep)
331  if (var.get_usage_count() and var.get_type() != VarTypes.Mapped) or var.pe_keep:
332  self._debug('Moving variable', var.fullname(cellml=True))
333  # Remove from where it was
334  comp._del_variable(var, keep_annotations=True)
335  # Set name to canonical version
336  var.name = var.fullname(cellml=True)
337  # Place in new component
338  new_comp._add_variable(var)
339  # Don't copy reactions
340  for math in list(getattr(comp, u'math', [])):
341  # Copy all <math> elements with content
342  if math.xml_children:
343  comp.xml_remove_child(math)
344  new_comp.xml_append(math)
345  # Invalidate cached links
346  math._unset_cached_links()
347  doc.model._del_component(comp)
348  # Remove groups & connections
349  for group in list(getattr(doc.model, u'group', [])):
350  doc.model.xml_remove_child(group)
351  for conn in list(getattr(doc.model, u'connection', [])):
352  doc.model.xml_remove_child(conn)
353 
354  # Remove unused variable assignments from the list
355  vs = [v for v in doc.model.get_assignments() if isinstance(v, cellml_variable)]
356  for v in vs:
357  if not v.xml_parent is new_comp:
358  doc.model._remove_assignment(v)
359 
360  # Remove interface attributes from variables
361  for v in new_comp.variable:
362  for iface in [u'public', u'private']:
363  try:
364  delattr(v, iface+u'_interface')
365  except AttributeError:
366  pass
367 
368  # Refresh expression dependency lists
369  for expr in self._get_assignment_exprs(False):
370  expr._cml_depends_on = list(expr.vars_in(expr.eq.rhs))
371  if expr.is_ode():
372  # Add dependency on the independent variable
373  indep_var = expr.eq.lhs.diff.independent_variable
374  if not indep_var in expr._cml_depends_on:
375  expr._cml_depends_on.append(indep_var)
376  # Update ODE definition dependency if needed
377  expr.eq.lhs.diff.dependent_variable._update_ode_dependency(indep_var, expr)
378  return
379 

Member Data Documentation

CellMLToNektar.optimize.PartialEvaluator.doc

Definition at line 247 of file optimize.py.

Referenced by CellMLToNektar.optimize.LookupTableAnalyser.analyse_model(), CellMLToNektar.translators.CellMLTranslator.config(), CellMLToNektar.optimize.LookupTableAnalyser.config(), and CellMLToNektar.translators.CellMLTranslator.scan_for_lookup_tables().

CellMLToNektar.optimize.PartialEvaluator.lookup_tables_analyser

Definition at line 249 of file optimize.py.

Referenced by CellMLToNektar.optimize.PartialEvaluator.is_instantiable().

CellMLToNektar.optimize.PartialEvaluator.solver_info

Definition at line 248 of file optimize.py.