Commit 5a21744f authored by Felix Schindler's avatar Felix Schindler Committed by GitHub

Merge pull request #861 from pymor/min-theta-approach

Min-theta approach
parents 31521c2d 6ce34232
Pipeline #46365 passed with stages
in 97 minutes and 51 seconds
......@@ -61,6 +61,12 @@ Bibliography
SIAM Journal on Matrix Analysis and Applications, 30(2),
609-638, 2008.
.. [Haa17] Haasdonk, B.,
Reduced basis methods for parametrized PDEs - a tutorial
introduction for stationary and instationary problems,
in Model reduction and approximation, SIAM, Philadelphia, PA, 15,
65-136, 2017.
.. [HDO11] Haasdonk, B.; Dihlmann, M. & Ohlberger, M.,
A training set and multiple bases generation approach for
parameterized model reduction based on adaptive grids in
......
......@@ -8,6 +8,7 @@ import numpy as np
from pymor.core.base import ImmutableObject, abstractmethod
from pymor.parameters.base import Parametric
from pymor.tools.floatcmp import float_cmp
class ParameterFunctional(ImmutableObject, Parametric):
......@@ -325,3 +326,133 @@ class ConstantParameterFunctional(ParameterFunctional):
def d_mu(self, component, index=()):
return self.with_(constant_value=0, name=self.name + '_d_mu')
class MinThetaParameterFunctional(ParameterFunctional):
"""|ParameterFunctional| implementing the min-theta approach from [Haa17]_ (Proposition 2.35).
Let V denote a Hilbert space and let a: V x V -> K denote a parametric coercive bilinear form with affine
decomposition ::
a(u, v, mu) = sum_{q = 1}^Q theta_q(mu) a_q(u, v),
for Q positive coefficient |ParameterFunctional|s theta_1, ..., theta_Q and positive semi-definite component
bilinear forms a_1, ..., a_Q: V x V -> K. Let mu_bar be a parameter with respect to which the coercivity constant
of a(., ., mu_bar) is known, i.e. we known alpha_mu_bar > 0, s.t. ::
alpha_mu_bar |u|_V^2 <= a(u, u, mu=mu_bar).
The min-theta approach from [Haa17]_ (Proposition 2.35) allows to obtain a computable bound for the coercivity
constant of a(., ., mu) for arbitrary parameters mu, since ::
a(u, u, mu=mu) >= min_{q = 1}^Q theta_q(mu)/theta_q(mu_bar) a(u, u, mu=mu_bar).
Given a list of the thetas, the |Parameter| mu_bar and the constant alpha_mu_bar, this functional thus evaluates
to ::
alpha_mu_bar * min_{q = 1}^Q theta_q(mu)/theta_q(mu_bar)
Parameters
----------
thetas
List or tuple of |ParameterFunctional|s
mu_bar
Parameter associated with alpha_mu_bar.
alpha_mu_bar
Known coercivity constant.
name
Name of the functional.
"""
def __init__(self, thetas, mu_bar, alpha_mu_bar=1., name=None):
assert isinstance(thetas, (list, tuple))
assert len(thetas) > 0
assert all([isinstance(theta, (Number, ParameterFunctional)) for theta in thetas])
thetas = tuple(ConstantParameterFunctional(theta) if not isinstance(theta, ParameterFunctional) else theta
for theta in thetas)
self.build_parameter_type(*thetas)
mu_bar = self.parse_parameter(mu_bar)
thetas_mu_bar = np.array([theta(mu_bar) for theta in thetas])
assert np.all(thetas_mu_bar > 0)
assert isinstance(alpha_mu_bar, Number)
assert alpha_mu_bar > 0
self.__auto_init(locals())
self.thetas_mu_bar = thetas_mu_bar
def evaluate(self, mu=None):
mu = self.parse_parameter(mu)
thetas_mu = np.array([theta(mu) for theta in self.thetas])
assert np.all(thetas_mu > 0)
return self.alpha_mu_bar * np.min(thetas_mu / self.thetas_mu_bar)
def d_mu(self, component, index=()):
raise NotImplementedError
class MaxThetaParameterFunctional(ParameterFunctional):
"""|ParameterFunctional| implementing the max-theta approach from [Haa17]_ (Exercise 5.12).
Let V denote a Hilbert space and let a: V x V -> K denote a continuous bilinear form or l: V -> K a continuous
linear functional, either with affine decomposition ::
a(u, v, mu) = sum_{q = 1}^Q theta_q(mu) a_q(u, v) or l(v, mu) = sum_{q = 1}^Q theta_q(mu) l_q(v)
for Q coefficient |ParameterFunctional|s theta_1, ..., theta_Q and continuous bilinear forms
a_1, ..., a_Q: V x V -> K or continuous linear functionals l_q: V -> K. Let mu_bar be a parameter with respect to
which the continuity constant of a(., ., mu_bar) or l(., mu_bar) is known, i.e. we known gamma_mu_bar > 0, s.t. ::
a(u, v, mu_bar) <= gamma_mu_bar |u|_V |v|_V or l(v, mu_bar) <= gamma_mu_bar |v|_V.
The max-theta approach from [Haa17]_ (Exercise 5.12) allows to obtain a computable bound for the continuity
constant of a(., ., mu) or l(., mu) for arbitrary parameters mu, since ::
a(u, v, mu=mu) <= |max_{q = 1}^Q theta_q(mu)/theta_q(mu_bar)| |a(u, v, mu=mu_bar)|
or ::
l(v, mu=mu) <= |max_{q = 1}^Q theta_q(mu)/theta_q(mu_bar)| |l(v, mu=mu_bar)|,
if all theta_q(mu_bar) != 0.
Given a list of the thetas, the |Parameter| mu_bar and the constant gamma_mu_bar, this functional thus evaluates
to ::
gamma_mu_bar * max{q = 1}^Q theta_q(mu)/theta_q(mu_bar)
Parameters
----------
thetas
List or tuple of |ParameterFunctional|s
mu_bar
Parameter associated with gamma_mu_bar.
gamma_mu_bar
Known continuity constant.
name
Name of the functional.
"""
def __init__(self, thetas, mu_bar, gamma_mu_bar=1., name=None):
assert isinstance(thetas, (list, tuple))
assert len(thetas) > 0
assert all([isinstance(theta, (Number, ParameterFunctional)) for theta in thetas])
thetas = tuple(ConstantParameterFunctional(f) if not isinstance(f, ParameterFunctional) else f
for f in thetas)
self.build_parameter_type(*thetas)
mu_bar = self.parse_parameter(mu_bar)
thetas_mu_bar = np.array([theta(mu_bar) for theta in thetas])
assert not np.any(float_cmp(thetas_mu_bar, 0))
assert isinstance(gamma_mu_bar, Number)
assert gamma_mu_bar > 0
self.__auto_init(locals())
self.thetas_mu_bar = thetas_mu_bar
def evaluate(self, mu=None):
mu = self.parse_parameter(mu)
thetas_mu = np.array([theta(mu) for theta in self.thetas])
assert np.all(np.logical_or(thetas_mu < 0, thetas_mu > 0))
return self.gamma_mu_bar * np.abs(np.max(thetas_mu / self.thetas_mu_bar))
def d_mu(self, component, index=()):
raise NotImplementedError
import numpy as np
import pytest
from pymor.parameters.functionals import (
ConstantParameterFunctional,
ExpressionParameterFunctional,
MinThetaParameterFunctional,
MaxThetaParameterFunctional,
ParameterFunctional)
from pymortests.base import runmodule
def test_min_theta_parameter_functional():
thetas = (ExpressionParameterFunctional('2*mu', {'mu': ()}),
ConstantParameterFunctional(1),
1)
mu_bar = 3
alpha_mu_bar = 10
theta = MinThetaParameterFunctional(thetas, mu_bar, alpha_mu_bar)
thetas = [ConstantParameterFunctional(t) if not isinstance(t, ParameterFunctional) else t
for t in thetas]
mu = 1
expected_value = alpha_mu_bar * np.min(np.array([t(mu) for t in thetas])/np.array([t(mu_bar) for t in thetas]))
actual_value = theta.evaluate(mu)
assert expected_value == actual_value
def test_min_theta_parameter_functional_fails_for_wrong_input():
thetas = (ExpressionParameterFunctional('2*mu', {'mu': ()}),
ConstantParameterFunctional(1),
-1)
mu_bar = -3
alpha_mu_bar = 10
with pytest.raises(AssertionError):
theta = MinThetaParameterFunctional(thetas, mu_bar, alpha_mu_bar)
def test_max_theta_parameter_functional():
thetas = (ExpressionParameterFunctional('2*mu', {'mu': ()}),
ConstantParameterFunctional(1),
-1)
mu_bar = -3
gamma_mu_bar = 10
theta = MaxThetaParameterFunctional(thetas, mu_bar, gamma_mu_bar)
thetas = [ConstantParameterFunctional(t) if not isinstance(t, ParameterFunctional) else t
for t in thetas]
mu = 1
expected_value = gamma_mu_bar * np.abs(np.max(np.array([t(mu) for t in thetas])/np.array([t(mu_bar) for t in
thetas])))
actual_value = theta.evaluate(mu)
assert expected_value == actual_value
if __name__ == "__main__":
runmodule(filename=__file__)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment