From e94ee5e747a1914ae4042af8f1a927e7b16cd14c Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Thu, 11 Jun 2020 21:25:42 +0200 Subject: [PATCH 1/8] [parameters/functionals] add LincombParameterFunctional --- src/pymor/parameters/functionals.py | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/pymor/parameters/functionals.py b/src/pymor/parameters/functionals.py index ea61c8fae3..d6e8f8722b 100644 --- a/src/pymor/parameters/functionals.py +++ b/src/pymor/parameters/functionals.py @@ -314,6 +314,42 @@ def d_mu(self, parameter, index=0): return self.with_(constant_value=0, name=self.name + '_d_mu') +class LincombParameterFunctional(ParameterFunctional): + """A |ParameterFunctional| representing a linear combination of |ParameterFunctionals|. + + The coefficients must be provided as scalars or as. + + Parameters + ---------- + functionals + List of |ParameterFunctionals| whose linear combination is formed. + coefficients + A list of scalar coefficients. + name + Name of the functionial. + + Attributes + ---------- + functionals + coefficients + """ + + def __init__(self, functionals, coefficients, name=None): + assert len(functionals) > 0 + assert len(functionals) == len(coefficients) + assert all(isinstance(f, ParameterFunctional) for f in functionals) + assert all(isinstance(c, Number) for c in coefficients) + self.__auto_init(locals()) + + def evaluate(self, mu=None): + assert self.parameters.assert_compatible(mu) + return sum(c * f(mu) for c, f in zip(self.coefficients, self.functionals)) + + def d_mu(self, parameter, index=0): + functionals_d_mu = [f.d_mu(parameter, index) for f in self.functionals] + return self.with_(functionals=functionals_d_mu, name=self.name + '_d_mu') + + class MinThetaParameterFunctional(ParameterFunctional): """|ParameterFunctional| implementing the min-theta approach from [Haa17]_ (Proposition 2.35). From ef5197081ce7636052375babba962866b285bc69 Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Thu, 11 Jun 2020 21:26:32 +0200 Subject: [PATCH 2/8] [parameters/functionals] add + and - operators and edit * operator --- src/pymor/parameters/functionals.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/pymor/parameters/functionals.py b/src/pymor/parameters/functionals.py index d6e8f8722b..82ab484dbc 100644 --- a/src/pymor/parameters/functionals.py +++ b/src/pymor/parameters/functionals.py @@ -45,11 +45,27 @@ def d_mu(self, parameter, index=0): def __call__(self, mu=None): return self.evaluate(mu) + def __add__(self, other): + if isinstance(other, Number) and other == 0: + return self + elif not isinstance(other, ParameterFunctional): + other = ConstantParameterFunctional(other) + return LincombParameterFunctional([self, other], [1., 1.]) + + __radd__ = __add__ + + def __sub__(self, other): + if isinstance(other, ParameterFunctional): + return LincombParameterFunctional([self, other], [1., -1.]) + else: + return self + (- other) + def __mul__(self, other): - from pymor.parameters.functionals import ProductParameterFunctional - if not isinstance(other, (Number, ParameterFunctional)): - return NotImplemented - return ProductParameterFunctional([self, other]) + if isinstance(other, Number): + return LincombParameterFunctional([self], [other]) + if isinstance(other, ParameterFunctional): + return ProductParameterFunctional([self, other]) + return NotImplemented __rmul__ = __mul__ From 963e48a254b2a698a5de16ff0fb8524e8f892c90 Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Thu, 11 Jun 2020 21:26:49 +0200 Subject: [PATCH 3/8] [pymortest] test for LincombParameterFunctional --- src/pymortests/parameter_functionals.py | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/pymortests/parameter_functionals.py diff --git a/src/pymortests/parameter_functionals.py b/src/pymortests/parameter_functionals.py new file mode 100644 index 0000000000..073ff462c3 --- /dev/null +++ b/src/pymortests/parameter_functionals.py @@ -0,0 +1,39 @@ +# This file is part of the pyMOR project (http://www.pymor.org). +# Copyright 2013-2020 pyMOR developers and contributors. All rights reserved. +# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause) + +from pymortests.base import runmodule +import pytest + +import numpy as np + +from pymor.parameters.functionals import ProjectionParameterFunctional, ExpressionParameterFunctional, LincombParameterFunctional +from pymor.basic import Mu + + +def test_LincombParameterFunctional(): + dict_of_d_mus = {'mu': ['200 * mu[0]', '2 * mu[0]'], 'nu': ['cos(nu[0])']} + + epf = ExpressionParameterFunctional('100 * mu[0]**2 + 2 * mu[1] * mu[0] + sin(nu[0])', + {'mu': 2, 'nu': 1}, + 'functional_with_derivative_and_second_derivative', + dict_of_d_mus) + pf = ProjectionParameterFunctional('mu', 2, 0) + mu = Mu({'mu': [10,2], 'nu': [3]}) + + zero = pf - pf + two_pf = pf + pf + three_pf = pf + 2*pf + pf_plus_one = pf + 1 + sum_ = epf + pf + pf_squared = (pf + 2*epf) * (pf - 2*epf) + 4 * epf * epf + + assert zero(mu) == 0 + assert two_pf(mu) == 2 * pf(mu) + assert three_pf(mu) == 3 * pf(mu) + assert pf_plus_one(mu) == pf(mu) + 1 + assert sum_(mu) == epf(mu) + pf(mu) + assert pf_squared(mu) == pf(mu) * pf(mu) + assert sum_.d_mu('mu', 0)(mu) == epf.d_mu('mu', 0)(mu) + pf.d_mu('mu', 0)(mu) + assert sum_.d_mu('mu', 1)(mu) == epf.d_mu('mu', 1)(mu) + pf.d_mu('mu', 1)(mu) + assert sum_.d_mu('nu', 0)(mu) == epf.d_mu('nu', 0)(mu) + pf.d_mu('nu', 0)(mu) From dbcdc8040299152063a8d50a1a549ede2f08aac9 Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Thu, 11 Jun 2020 21:34:36 +0200 Subject: [PATCH 4/8] [parameters/functionals] small spelling mistake --- src/pymor/parameters/functionals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pymor/parameters/functionals.py b/src/pymor/parameters/functionals.py index 82ab484dbc..b59907bd3b 100644 --- a/src/pymor/parameters/functionals.py +++ b/src/pymor/parameters/functionals.py @@ -342,7 +342,7 @@ class LincombParameterFunctional(ParameterFunctional): coefficients A list of scalar coefficients. name - Name of the functionial. + Name of the functional. Attributes ---------- From dc4a67b7e16a8e274836c9ff1545db07e767c920 Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Thu, 11 Jun 2020 23:20:30 +0200 Subject: [PATCH 5/8] add AUTHORS entry --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 3976bc1834..a2a5161a64 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -18,6 +18,7 @@ * Tim Keil, tim.keil@uni-muenster.de * second order derivatives for parameters * speed up of lincomb operators + * add LincombParameterFunctional * Luca Mechelli, luca.mechelli@uni-konstanz.de * speed up of lincomb operators From fa0876df84a702942ecbf2e2936d03e0fada9736 Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Mon, 15 Jun 2020 16:15:47 +0200 Subject: [PATCH 6/8] Update src/pymor/parameters/functionals.py Co-authored-by: Stephan Rave --- src/pymor/parameters/functionals.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pymor/parameters/functionals.py b/src/pymor/parameters/functionals.py index b59907bd3b..1d32a3fb24 100644 --- a/src/pymor/parameters/functionals.py +++ b/src/pymor/parameters/functionals.py @@ -46,11 +46,14 @@ def __call__(self, mu=None): return self.evaluate(mu) def __add__(self, other): - if isinstance(other, Number) and other == 0: - return self - elif not isinstance(other, ParameterFunctional): + if isinstance(other, Number): + if other == 0: + return self other = ConstantParameterFunctional(other) - return LincombParameterFunctional([self, other], [1., 1.]) + if isinstance(other, ParameterFunctional): + return LincombParameterFunctional([self, other], [1., 1.]) + else: + return NotImplemented __radd__ = __add__ From ec216e3e08a831dbbd82532247d43dd0176c31d2 Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Mon, 15 Jun 2020 16:24:51 +0200 Subject: [PATCH 7/8] revert mul but still remove import --- src/pymor/parameters/functionals.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pymor/parameters/functionals.py b/src/pymor/parameters/functionals.py index 1d32a3fb24..f22e4f018b 100644 --- a/src/pymor/parameters/functionals.py +++ b/src/pymor/parameters/functionals.py @@ -64,11 +64,9 @@ def __sub__(self, other): return self + (- other) def __mul__(self, other): - if isinstance(other, Number): - return LincombParameterFunctional([self], [other]) - if isinstance(other, ParameterFunctional): - return ProductParameterFunctional([self, other]) - return NotImplemented + if not isinstance(other, (Number, ParameterFunctional)): + return NotImplemented + return ProductParameterFunctional([self, other]) __rmul__ = __mul__ From afb42ce717a9d6eae51f595dc2ef6deefc65ee6c Mon Sep 17 00:00:00 2001 From: Tim Keil Date: Mon, 15 Jun 2020 16:25:11 +0200 Subject: [PATCH 8/8] two other small fixes --- src/pymor/parameters/functionals.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pymor/parameters/functionals.py b/src/pymor/parameters/functionals.py index f22e4f018b..c138798967 100644 --- a/src/pymor/parameters/functionals.py +++ b/src/pymor/parameters/functionals.py @@ -334,7 +334,7 @@ def d_mu(self, parameter, index=0): class LincombParameterFunctional(ParameterFunctional): """A |ParameterFunctional| representing a linear combination of |ParameterFunctionals|. - The coefficients must be provided as scalars or as. + The coefficients must be provided as scalars. Parameters ---------- @@ -356,6 +356,8 @@ def __init__(self, functionals, coefficients, name=None): assert len(functionals) == len(coefficients) assert all(isinstance(f, ParameterFunctional) for f in functionals) assert all(isinstance(c, Number) for c in coefficients) + functionals = tuple(functionals) + coefficients = tuple(coefficients) self.__auto_init(locals()) def evaluate(self, mu=None):