Source code for pymor.parameters.functionals

# This file is part of the pyMOR project (http://www.pymor.org).
# Copyright 2013-2019 pyMOR developers and contributors. All rights reserved.
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from numbers import Number

import numpy as np

from pymor.parameters.interfaces import ParameterFunctionalInterface


[docs]class ProjectionParameterFunctional(ParameterFunctionalInterface): """|ParameterFunctional| returning a component of the given parameter. For given parameter `mu`, this functional evaluates to :: mu[component_name][index] Parameters ---------- component_name The name of the parameter component to return. component_shape The shape of the parameter component. index See above. name Name of the functional. """ def __init__(self, component_name, component_shape, index=(), name=None): if isinstance(component_shape, Number): component_shape = () if component_shape == 0 else (component_shape,) assert len(index) == len(component_shape) assert not component_shape or index < component_shape self.__auto_init(locals()) self.build_parameter_type({component_name: component_shape})
[docs] def evaluate(self, mu=None): mu = self.parse_parameter(mu) return mu[self.component_name].item(self.index)
[docs] def d_mu(self, component, index=()): check, index = self._check_and_parse_input(component, index) if check: if component == self.component_name and index == self.index: return ConstantParameterFunctional(1, name=self.name + '_d_mu') return ConstantParameterFunctional(0, name=self.name + '_d_mu')
[docs]class GenericParameterFunctional(ParameterFunctionalInterface): """A wrapper making an arbitrary Python function a |ParameterFunctional| Note that a GenericParameterFunctional can only be :mod:`pickled <pymor.core.pickle>` if the function it is wrapping can be pickled. For this reason, it is usually preferable to use :class:`ExpressionParameterFunctional` instead of :class:`GenericParameterFunctional`. Parameters ---------- mapping The function to wrap. The function has signature `mapping(mu)`. parameter_type The |ParameterType| of the |Parameters| the functional expects. name The name of the functional. derivative_mappings A dict containing all partial derivatives of each component and index in the |ParameterType| with the signature `derivative_mappings[component][index](mu)` """ def __init__(self, mapping, parameter_type, name=None, derivative_mappings=None): self.__auto_init(locals()) self.build_parameter_type(parameter_type)
[docs] def evaluate(self, mu=None): mu = self.parse_parameter(mu) value = self.mapping(mu) # ensure that we return a number not an array if isinstance(value, np.ndarray): return value.item() else: return value
[docs] def d_mu(self, component, index=()): check, index = self._check_and_parse_input(component, index) if check: if self.derivative_mappings is None: raise ValueError('You must provide a dict of expressions for all \ partial derivatives in self.parameter_type') else: if component in self.derivative_mappings: return GenericParameterFunctional(self.derivative_mappings[component][index], self.parameter_type, name=self.name + '_d_mu') else: raise ValueError('derivative mappings does not contain item {}'.format(component)) return ConstantParameterFunctional(0, name=self.name + '_d_mu')
[docs]class ExpressionParameterFunctional(GenericParameterFunctional): """Turns a Python expression given as a string into a |ParameterFunctional|. Some |NumPy| arithmetic functions like `sin`, `log`, `min` are supported. For a full list see the `functions` class attribute. .. warning:: :meth:`eval` is used to evaluate the given expression. Using this class with expression strings from untrusted sources will cause mayhem and destruction! Parameters ---------- expression A Python expression in the parameter components of the given `parameter_type`. parameter_type The |ParameterType| of the |Parameters| the functional expects. name The name of the functional. derivative_expressions A dict containing a Python expression for the partial derivatives of each parameter component. """ functions = {k: getattr(np, k) for k in {'sin', 'cos', 'tan', 'arcsin', 'arccos', 'arctan', 'arctan2', 'sinh', 'cosh', 'tanh', 'arcsinh', 'arccosh', 'arctanh', 'exp', 'exp2', 'log', 'log2', 'log10', 'sqrt', 'array', 'min', 'minimum', 'max', 'maximum', 'pi', 'e', 'sum', 'prod', 'abs', 'sign', 'zeros', 'ones'}} functions['norm'] = np.linalg.norm functions['polar'] = lambda x: (np.linalg.norm(x, axis=-1), np.arctan2(x[..., 1], x[..., 0]) % (2*np.pi)) functions['np'] = np def __init__(self, expression, parameter_type, name=None, derivative_expressions=None): self.expression = expression code = compile(expression, '<expression>', 'eval') functions = self.functions def get_lambda(exp_code): return lambda mu: eval(exp_code, functions, mu) exp_mapping = get_lambda(code) if derivative_expressions is not None: derivative_mappings = derivative_expressions.copy() for (key,exp) in derivative_mappings.items(): exp_array = np.array(exp, dtype=object) for exp in np.nditer(exp_array, op_flags=['readwrite'], flags= ['refs_ok']): exp_code = compile(str(exp), '<expression>', 'eval') mapping = get_lambda(exp_code) exp[...] = mapping derivative_mappings[key] = exp_array else: derivative_mappings = None super().__init__(exp_mapping, parameter_type, name, derivative_mappings) self.__auto_init(locals())
[docs] def __reduce__(self): return (ExpressionParameterFunctional, (self.expression, self.parameter_type, getattr(self, '_name', None), self.derivative_expressions))
[docs]class ProductParameterFunctional(ParameterFunctionalInterface): """Forms the product of a list of |ParameterFunctionals| or numbers. Parameters ---------- factors A list of |ParameterFunctionals| or numbers. name Name of the functional. """ def __init__(self, factors, name=None): assert len(factors) > 0 assert all(isinstance(f, (ParameterFunctionalInterface, Number)) for f in factors) self.__auto_init(locals()) self.build_parameter_type(*(f for f in factors if isinstance(f, ParameterFunctionalInterface)))
[docs] def evaluate(self, mu=None): mu = self.parse_parameter(mu) return np.array([f.evaluate(mu) if hasattr(f, 'evaluate') else f for f in self.factors]).prod()
[docs] def d_mu(self, component, index=()): raise NotImplementedError
[docs]class ConjugateParameterFunctional(ParameterFunctionalInterface): """Conjugate of a given |ParameterFunctional| Evaluates a given |ParameterFunctional| and returns the complex conjugate of the value. Parameters ---------- functional The |ParameterFunctional| of which the complex conjuate is taken. name Name of the functional. """ def __init__(self, functional, name=None): self.functional = functional self.name = name or f'{functional.name}_conj' self.build_parameter_type(functional)
[docs] def evaluate(self, mu=None): mu = self.parse_parameter(mu) return np.conj(self.functional.evaluate(mu))
[docs] def d_mu(self, component, index=()): raise NotImplementedError
[docs]class ConstantParameterFunctional(ParameterFunctionalInterface): """|ParameterFunctional| returning a constant value for each parameter. Parameters ---------- constant_value value of the functional name Name of the functional. """ def __init__(self, constant_value, name=None): self.constant_value = constant_value self.__auto_init(locals())
[docs] def evaluate(self, mu=None): return self.constant_value
[docs] def d_mu(self, component, index=()): return self.with_(constant_value=0, name=self.name + '_d_mu')