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

Public Member Functions

def is_instantiable (self, expr)
 
def parteval (self, doc, solver_info, lookup_tables_analyser=None)
 

Public Attributes

 doc
 
 solver_info
 
 lookup_tables_analyser
 

Private Member Functions

def _debug (self, *args)
 
def _expr_lhs (self, expr)
 
def _describe_expr (self, expr)
 
def _process_ci_elts (self, elt, func)
 
def _rename_var (self, elt)
 
def _do_reduce_eval_loop (self, expr_source)
 
def _reduce_evaluate_expression (self, expr)
 
def _get_assignment_exprs (self, skip_solver_info=True)
 
def _is_source_of (self, v1, v2)
 
def _check_retargetting (self, ci_elt)
 

Detailed Description

Partial Evaluation #.

Perform partial evaluation of a CellML model.

Definition at line 57 of file optimize.py.

Member Function Documentation

◆ _check_retargetting()

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.

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

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

◆ _debug()

def CellMLToNektar.optimize.PartialEvaluator._debug (   self,
args 
)
private

◆ _describe_expr()

def CellMLToNektar.optimize.PartialEvaluator._describe_expr (   self,
  expr 
)
private
Describe this expression for debug info.

Definition at line 72 of file optimize.py.

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

References CellMLToNektar.optimize.PartialEvaluator._expr_lhs().

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

◆ _do_reduce_eval_loop()

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.

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

References CellMLToNektar.cellml_metadata.RdfProcessor._debug(), CellMLToNektar.optimize.PartialEvaluator._debug(), CellMLToNektar.optimize.PartialEvaluator._reduce_evaluate_expression(), Nektar::LibUtilities::H5DataSource.doc, CellMLToNektar.optimize.PartialEvaluator.doc, CellMLToNektar.optimize.LookupTableAnalyser.doc, CellMLToNektar.translators.CellMLTranslator.doc, and CellMLToNektar.translators.ConfigurationStore.doc.

◆ _expr_lhs()

def CellMLToNektar.optimize.PartialEvaluator._expr_lhs (   self,
  expr 
)
private
Display the LHS of this expression.

Definition at line 64 of file optimize.py.

64 def _expr_lhs(self, expr):
65 """Display the LHS of this expression."""
66 lhs = expr.assigned_variable()
67 if isinstance(lhs, cellml_variable):
68 return lhs.fullname()
69 else:
70 return lhs[0].fullname() + u'/' + lhs[1].fullname()
71

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

◆ _get_assignment_exprs()

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.

160 def _get_assignment_exprs(self, skip_solver_info=True):
161 """Get an iterable over all assignments in the model that are mathml_apply instances."""
162 if not skip_solver_info:
163 skip = set()
164 else:
165 skip = set(self.solver_info.get_modifiable_mathematics())
166 for e in self.doc.model.get_assignments():
167 if isinstance(e, mathml_apply) and e not in skip:
168 assert e.is_ode() or e.is_assignment()
169 yield e
170

References Nektar::LibUtilities::H5DataSource.doc, CellMLToNektar.optimize.PartialEvaluator.doc, CellMLToNektar.optimize.LookupTableAnalyser.doc, CellMLToNektar.translators.CellMLTranslator.doc, CellMLToNektar.translators.ConfigurationStore.doc, CellMLToNektar.optimize.PartialEvaluator.solver_info, and CellMLToNektar.optimize.LookupTableAnalyser.solver_info.

◆ _is_source_of()

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.

198 def _is_source_of(self, v1, v2):
199 """Test if v1 is a source of the mapped variable v2."""
200 if v1 is v2:
201 return True
202 elif v2.get_type() == VarTypes.Mapped:
203 return self._is_source_of(v1, v2.get_source_variable())
204 else:
205 return False
206

References CellMLToNektar.optimize.PartialEvaluator._is_source_of().

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

◆ _process_ci_elts()

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.

86 def _process_ci_elts(self, elt, func):
87 """Apply func to all ci elements in the tree rooted at elt."""
88 if isinstance(elt, mathml_ci):
89 func(elt)
90 else:
91 for e in elt.xml_element_children():
92 self._process_ci_elts(e, func)
93

References CellMLToNektar.optimize.PartialEvaluator._process_ci_elts(), and CellMLToNektar.translators.ConfigurationStore._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().

◆ _reduce_evaluate_expression()

def CellMLToNektar.optimize.PartialEvaluator._reduce_evaluate_expression (   self,
  expr 
)
private
Reduce or evaluate a single expression.

Definition at line 117 of file optimize.py.

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

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

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

◆ _rename_var()

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.

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

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

◆ is_instantiable()

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.

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

References CellMLToNektar.optimize.PartialEvaluator._process_ci_elts(), CellMLToNektar.translators.ConfigurationStore._process_ci_elts(), Nektar::LibUtilities::H5DataSource.doc, CellMLToNektar.optimize.PartialEvaluator.doc, CellMLToNektar.optimize.LookupTableAnalyser.doc, CellMLToNektar.translators.CellMLTranslator.doc, CellMLToNektar.translators.ConfigurationStore.doc, and CellMLToNektar.optimize.PartialEvaluator.lookup_tables_analyser.

◆ parteval()

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.

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

Member Data Documentation

◆ doc

CellMLToNektar.optimize.PartialEvaluator.doc

Definition at line 247 of file optimize.py.

Referenced by CellMLToNektar.optimize.LookupTableAnalyser._determine_duplicate_tables(), CellMLToNektar.optimize.LookupTableAnalyser._determine_unneeded_tables(), CellMLToNektar.optimize.PartialEvaluator._do_reduce_eval_loop(), CellMLToNektar.optimize.LookupTableAnalyser._find_tables(), CellMLToNektar.translators.ConfigurationStore._find_transmembrane_currents_from_voltage_ode(), CellMLToNektar.translators.ConfigurationStore._find_variable(), CellMLToNektar.optimize.PartialEvaluator._get_assignment_exprs(), CellMLToNektar.optimize.LookupTableAnalyser.analyse_model(), CellMLToNektar.optimize.LookupTableAnalyser.annotate_as_suitable(), CellMLToNektar.optimize.LookupTableAnalyser.config(), CellMLToNektar.translators.CellMLTranslator.config(), CellMLToNektar.translators.ConfigurationStore.expose_variables(), CellMLToNektar.translators.ConfigurationStore.find_current_vars(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.get_stimulus_assignment(), CellMLToNektar.optimize.PartialEvaluator.is_instantiable(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.lut_parameters(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_backward_euler_mathematics(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_constructor(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_derived_quantities(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_equations(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_get_i_ionic(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_intracellular_calcium(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_lut_class(), CellMLToNektar.translators.CellMLTranslator.output_lut_declarations(), CellMLToNektar.translators.CellMLTranslator.output_lut_deletion(), CellMLToNektar.translators.CellMLTranslator.output_lut_generation(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_lut_indexing_methods(), CellMLToNektar.translators.CellMLTranslator.output_lut_indices(), CellMLToNektar.translators.CellMLTranslator.output_lut_methods(), CellMLToNektar.translators.CellMLTranslator.output_lut_row_lookup_memory(), CellMLToNektar.translators.CellMLTranslator.output_lut_row_lookup_methods(), CellMLToNektar.CellMLToNektarTranslator.CellMLToNektarTranslator.output_rush_larsen_mathematics(), CellMLToNektar.translators.CellMLTranslator.output_table_index_generation(), CellMLToNektar.translators.ConfigurationStore.read_configuration_file(), CellMLToNektar.optimize.LookupTableAnalyser.remove_lut_annotations(), CellMLToNektar.translators.CellMLTranslator.scan_for_lookup_tables(), and CellMLToNektar.translators.ConfigurationStore.validate_metadata().

◆ lookup_tables_analyser

CellMLToNektar.optimize.PartialEvaluator.lookup_tables_analyser

◆ solver_info

CellMLToNektar.optimize.PartialEvaluator.solver_info