From d05306b2d5c9c76ad9fb5aa532f13e08c5e35e33 Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Thu, 9 Jan 2020 22:46:01 -0500 Subject: [PATCH 1/2] #749 cache evaluate for shape --- pybamm/expression_tree/binary_operators.py | 2 +- pybamm/expression_tree/broadcasts.py | 6 +++--- pybamm/expression_tree/concatenations.py | 2 +- pybamm/expression_tree/functions.py | 2 +- pybamm/expression_tree/independent_variable.py | 4 ++-- pybamm/expression_tree/input_parameter.py | 2 +- pybamm/expression_tree/parameter.py | 4 ++-- pybamm/expression_tree/state_vector.py | 2 +- pybamm/expression_tree/symbol.py | 8 ++++++++ pybamm/expression_tree/unary_operators.py | 18 +++++++++--------- pybamm/expression_tree/variable.py | 2 +- 11 files changed, 30 insertions(+), 22 deletions(-) diff --git a/pybamm/expression_tree/binary_operators.py b/pybamm/expression_tree/binary_operators.py index dffbbaa367..57c79994a5 100644 --- a/pybamm/expression_tree/binary_operators.py +++ b/pybamm/expression_tree/binary_operators.py @@ -191,7 +191,7 @@ def evaluate(self, t=None, y=None, u=None, known_evals=None): right = self.right.evaluate(t, y, u) return self._binary_evaluate(left, right) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape()`. """ left = self.children[0].evaluate_for_shape() right = self.children[1].evaluate_for_shape() diff --git a/pybamm/expression_tree/broadcasts.py b/pybamm/expression_tree/broadcasts.py index 5e32b28098..58bec62363 100644 --- a/pybamm/expression_tree/broadcasts.py +++ b/pybamm/expression_tree/broadcasts.py @@ -100,7 +100,7 @@ def _unary_new_copy(self, child): child, self.broadcast_domain, self.auxiliary_domains, self.broadcast_type ) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a Broadcast. See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` @@ -141,7 +141,7 @@ def _unary_new_copy(self, child): """ See :meth:`pybamm.UnaryOperator.simplify()`. """ return PrimaryBroadcast(child, self.broadcast_domain) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a Broadcast. See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` @@ -173,7 +173,7 @@ def _unary_new_copy(self, child): """ See :meth:`pybamm.UnaryOperator.simplify()`. """ return FullBroadcast(child, self.broadcast_domain, self.auxiliary_domains) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a Broadcast. See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` diff --git a/pybamm/expression_tree/concatenations.py b/pybamm/expression_tree/concatenations.py index 2508b4cc8b..3198d1c380 100644 --- a/pybamm/expression_tree/concatenations.py +++ b/pybamm/expression_tree/concatenations.py @@ -90,7 +90,7 @@ def _concatenation_simplify(self, children): new_symbol.domain = [] return new_symbol - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape` """ if len(self.children) == 0: return np.array([]) diff --git a/pybamm/expression_tree/functions.py b/pybamm/expression_tree/functions.py index 0014b58fbb..8da78b2537 100644 --- a/pybamm/expression_tree/functions.py +++ b/pybamm/expression_tree/functions.py @@ -167,7 +167,7 @@ def evaluate(self, t=None, y=None, u=None, known_evals=None): evaluated_children = [child.evaluate(t, y, u) for child in self.children] return self._function_evaluate(evaluated_children) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Default behaviour: has same shape as all child See :meth:`pybamm.Symbol.evaluate_for_shape()` diff --git a/pybamm/expression_tree/independent_variable.py b/pybamm/expression_tree/independent_variable.py index dceb84ea5b..985b15fd17 100644 --- a/pybamm/expression_tree/independent_variable.py +++ b/pybamm/expression_tree/independent_variable.py @@ -27,7 +27,7 @@ class IndependentVariable(pybamm.Symbol): def __init__(self, name, domain=None, auxiliary_domains=None): super().__init__(name, domain=domain, auxiliary_domains=auxiliary_domains) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain( self.domain, self.auxiliary_domains @@ -57,7 +57,7 @@ def _base_evaluate(self, t, y=None): raise ValueError("t must be provided") return t - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Return the scalar '0' to represent the shape of the independent variable `Time`. See :meth:`pybamm.Symbol.evaluate_for_shape()` diff --git a/pybamm/expression_tree/input_parameter.py b/pybamm/expression_tree/input_parameter.py index 67b8a87551..8d0cbf1e62 100644 --- a/pybamm/expression_tree/input_parameter.py +++ b/pybamm/expression_tree/input_parameter.py @@ -25,7 +25,7 @@ def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ return InputParameter(self.name) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns the scalar 'NaN' to represent the shape of a parameter. See :meth:`pybamm.Symbol.evaluate_for_shape()` diff --git a/pybamm/expression_tree/parameter.py b/pybamm/expression_tree/parameter.py index f99bb09915..7000bca24b 100644 --- a/pybamm/expression_tree/parameter.py +++ b/pybamm/expression_tree/parameter.py @@ -27,7 +27,7 @@ def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ return Parameter(self.name, self.domain) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns the scalar 'NaN' to represent the shape of a parameter. See :meth:`pybamm.Symbol.evaluate_for_shape()` @@ -118,7 +118,7 @@ def _function_parameter_new_copy(self, children): """ return FunctionParameter(self.name, *children, diff_variable=self.diff_variable) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns the sum of the evaluated children See :meth:`pybamm.Symbol.evaluate_for_shape()` diff --git a/pybamm/expression_tree/state_vector.py b/pybamm/expression_tree/state_vector.py index 688f07173a..dbb1d32d36 100644 --- a/pybamm/expression_tree/state_vector.py +++ b/pybamm/expression_tree/state_vector.py @@ -173,7 +173,7 @@ def new_copy(self): evaluation_array=self.evaluation_array, ) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Returns a vector of NaNs to represent the shape of a StateVector. The size of a StateVector is the number of True elements in its evaluation_array diff --git a/pybamm/expression_tree/symbol.py b/pybamm/expression_tree/symbol.py index ca902f5172..a7be13c372 100644 --- a/pybamm/expression_tree/symbol.py +++ b/pybamm/expression_tree/symbol.py @@ -494,6 +494,14 @@ def evaluate_for_shape(self): shape is returned instead, using the symbol's domain. See :meth:`pybamm.Symbol.evaluate()` """ + try: + return self._saved_evaluate_for_shape + except AttributeError: + self._saved_evaluate_for_shape = self._evaluate_for_shape() + return self._saved_evaluate_for_shape + + def _evaluate_for_shape(self): + "See :meth:`Symbol.evaluate_for_shape`" return self.evaluate() def is_constant(self): diff --git a/pybamm/expression_tree/unary_operators.py b/pybamm/expression_tree/unary_operators.py index 9f665ffbb3..4cb2b2f21a 100644 --- a/pybamm/expression_tree/unary_operators.py +++ b/pybamm/expression_tree/unary_operators.py @@ -74,7 +74,7 @@ def evaluate(self, t=None, y=None, u=None, known_evals=None): child = self.child.evaluate(t, y, u) return self._unary_evaluate(child) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ Default behaviour: unary operator has same shape as child See :meth:`pybamm.Symbol.evaluate_for_shape()` @@ -228,7 +228,7 @@ def _unary_new_copy(self, child): return self.__class__(child, self.index, check_size=False) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return self._unary_evaluate(self.children[0].evaluate_for_shape()) def evaluates_on_edges(self): @@ -347,7 +347,7 @@ class Mass(SpatialOperator): def __init__(self, child): super().__init__("mass", child) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return pybamm.evaluate_for_shape_using_domain(self.domain, typ="matrix") @@ -361,7 +361,7 @@ class BoundaryMass(SpatialOperator): def __init__(self, child): super().__init__("boundary mass", child) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return pybamm.evaluate_for_shape_using_domain(self.domain, typ="matrix") @@ -455,7 +455,7 @@ def _unary_new_copy(self, child): return self.__class__(child, self.integration_variable) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain(self.domain) @@ -502,7 +502,7 @@ def __init__(self, child, integration_variable): if isinstance(integration_variable, pybamm.SpatialVariable): self.name += " on {}".format(integration_variable.domain) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): return self.children[0].evaluate_for_shape() @@ -551,7 +551,7 @@ def _unary_new_copy(self, child): return self.__class__(child, vector_type=self.vector_type) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain(self.domain) @@ -612,7 +612,7 @@ def _unary_new_copy(self, child): return self.__class__(child, region=self.region) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain(self.domain) @@ -721,7 +721,7 @@ def _unary_new_copy(self, child): """ See :meth:`UnaryOperator._unary_new_copy()`. """ return self.__class__(child, self.side) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain( self.domain, self.auxiliary_domains diff --git a/pybamm/expression_tree/variable.py b/pybamm/expression_tree/variable.py index c0a350717d..6abd0f7ad5 100644 --- a/pybamm/expression_tree/variable.py +++ b/pybamm/expression_tree/variable.py @@ -39,7 +39,7 @@ def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ return Variable(self.name, self.domain, self.auxiliary_domains) - def evaluate_for_shape(self): + def _evaluate_for_shape(self): """ See :meth:`pybamm.Symbol.evaluate_for_shape_using_domain()` """ return pybamm.evaluate_for_shape_using_domain( self.domain, self.auxiliary_domains From 4e93f1bb4fdc10609b7f815d31727f74be14fa1b Mon Sep 17 00:00:00 2001 From: Valentin Sulzer Date: Fri, 10 Jan 2020 13:05:45 -0500 Subject: [PATCH 2/2] #749 changelop [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ab4302ad..ee7535c193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ## Optimizations +- Added caching for shape evaluation, used during discretisation ([#780](https://github.com/pybamm-team/PyBaMM/pull/780)) - Added an option to skip model checks during discretisation, which could be slow for large models ([#739](https://github.com/pybamm-team/PyBaMM/pull/739)) - Use CasADi's automatic differentation algorithms by default when solving a model ([#714](https://github.com/pybamm-team/PyBaMM/pull/714)) - Avoid re-checking size when making a copy of an `Index` object ([#656](https://github.com/pybamm-team/PyBaMM/pull/656))