Source code for TransportMaps.Maps.LinearSpanParametricTriangularComponentwiseMapBase

#
# This file is part of TransportMaps.
#
# TransportMaps is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# TransportMaps is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with TransportMaps.  If not, see <http://www.gnu.org/licenses/>.
#
# Transport Maps Library
# Copyright (C) 2015-2018 Massachusetts Institute of Technology
# Uncertainty Quantification group
# Department of Aeronautics and Astronautics
#
# Author: Transport Map Team
# Website: transportmaps.mit.edu
# Support: transportmaps.mit.edu/qa/
#

import numpy as np

from ..Misc import \
    required_kwargs

from .Functionals import \
    LinearSpanTensorizedParametricFunctional

from .ParametricTriangularComponentwiseMapBase import ParametricTriangularComponentwiseMap

__all__ = [
    'LinearSpanParametricTriangularComponentwiseMap',
    'CommonBasisLinearSpanParametricTriangularComponentwiseMap',
]


[docs]class LinearSpanParametricTriangularComponentwiseMap( ParametricTriangularComponentwiseMap ): r""" Triangular map :math:`T[{\bf a}_{1:d_y}]({\bf x})= [T_1[{\bf a}_1],\ldots,T_{d_y}[{\bf a}_{d_y}]]^\top` where :math:`T_i[{\bf a}_i](x_{1:i}) := {\bf a}_i^\top \, \Phi(x_{1:i}) `. """ @required_kwargs('active_vars', 'approx_list') def __init__(self, **kwargs): r""" Kwargs: active_vars (:class:`list<list>` [:math:`d`] of :class:`list<list>`): for each dimension lists the active variables. approx_list (:class:`list<list>` [:math:`d`] of :class:`LinearSpanTensorizedParametricFunctional<TransportMaps.Maps.Functionals.LinearSpanTensorizedParametricFunctional`): list of parametric functionals for each dimension full_basis_list (:class:`list` of :class:`list`): list of basis for each input of each component for a full triangular map (this is needed for some adaptivity algorithm) """ approx_list = kwargs['approx_list'] if not all( [ isinstance(a, LinearSpanTensorizedParametricFunctional) for a in approx_list ] ): raise ValueError("All the approximation functions must be instances " + "of the class LinearSpanTensorizedParametricFunctional") super(LinearSpanParametricTriangularComponentwiseMap, self).__init__(**kwargs) self.full_basis_list = kwargs.get('full_basis_list')
[docs] def get_identity_coeffs(self): r""" Returns the coefficients corresponding to the identity map Returns: (:class:`ndarray<numpy.ndarray>` [:math:`N`]): coefficients """ # Define the identity map coeffs = [] for a in self.approx_list: cc = np.zeros(a.n_coeffs) idx = next(i for i,x in enumerate(a.multi_idxs) if x == tuple([0]*(a.dim_in-1)+[1])) cc[idx] = 1. coeffs.append(cc) return np.hstack(coeffs)
[docs]class CommonBasisLinearSpanParametricTriangularComponentwiseMap( LinearSpanParametricTriangularComponentwiseMap ): r""" Triangular map :math:`T[{\bf a}_{1:d_y}]({\bf x})= [T_1[{\bf a}_1],\ldots,T_{d_y}[{\bf a}_{d_y}]]^\top` where :math:`T_i[{\bf a}_i](x_{1:i}) := {\bf a}_i^\top \, \Phi(x_{1:i})`, and for each dimension :math:`i`, every component :math:`T_k` share the same basis type. This is leads to some more efficient evaluation operations. """ @required_kwargs('active_vars', 'approx_list') def __init__(self, **kwargs): r""" Kwargs: active_vars (:class:`list<list>` [:math:`d`] of :class:`list<list>`): for each dimension lists the active variables. approx_list (:class:`list<list>` [:math:`d`] of :class:`LinearSpanTensorizedParametricFunctional<TransportMaps.Maps.Functionals.LinearSpanTensorizedParametricFunctional`): list of parametric functionals for each dimension full_basis_list (:class:`list` of :class:`list`): list of basis for each input of each component for a full triangular map (this is needed for some adaptivity algorithm) """ super(CommonBasisLinearSpanParametricTriangularComponentwiseMap, self).__init__(**kwargs) # Checks self.basis_list = [None for i in range(self.dim)] for a,avars in zip(self.approx_list, self.active_vars): for b, avar in zip(a.basis_list,avars): if self.basis_list[avar] is None: self.basis_list[avar] = b if not( self.basis_list[avar] is b ): raise ValueError("Fixed a dimension, all the basis for " + "this dimension must be of the same object " + "for all T_i") # Init self.logger.warning( "Be advised that in the current implementation " + \ "of the \"CommonBasis\" componentwise maps, " + \ "max_orders does not get updated when underlying " + \ "directional orders change!!! (e.g. adaptivity)" ) self.max_orders = np.zeros(self.dim, dtype=int) for a,avar in zip(self.approx_list, self.active_vars): a_max_ord = a.directional_orders idxs = tuple( np.where( self.max_orders[avar] < a_max_ord )[0] ) for i in idxs: self.max_orders[avar[i]] = a_max_ord[i]
[docs] def precomp_evaluate(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`T({\bf x},{\bf a})` This returns a list of uni-variate Vandermonde matrices with order maximum among the components :math:`T_i`. Enriches the dictionaries in the ``precomp`` list if necessary. Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`ndarray<numpy.ndarray>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} to_compute_flag = False for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): try: tmp = p['V_list'] except KeyError: to_compute_flag = True break if to_compute_flag: vand = [ b.GradVandermonde(x[:,i], int(o)) for i,(o,b) in enumerate(zip(self.max_orders,self.basis_list)) ] for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): # Vandermonde matrices p['V_list'] = [ vand[var] for var in avar ] return precomp
[docs] def precomp_grad_x(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`\nabla_{\bf x}T({\bf x},{\bf a})` Enriches the dictionaries in the ``precomp`` list if necessary. Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`ndarray<numpy.ndarray>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} # precomp_evaluate part self.precomp_evaluate(x, precomp) # precomp_grad_x part to_compute_flag = False for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): try: tmp = p['partial_x_V_list'] except KeyError: to_compute_flag = True break if to_compute_flag: vand = [ b.GradVandermonde(x[:,i], int(o), k=1) for i,(o,b) in enumerate(zip(self.max_orders,self.basis_list)) ] for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): p['partial_x_V_list'] = [ vand[var] for var in avar ] return precomp
[docs] def precomp_hess_x(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`\nabla^2_{\bf x}T({\bf x},{\bf a})` Enriches the dictionaries in the ``precomp`` list if necessary. Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`ndarray<numpy.ndarray>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} # precomp_evaluate and precomp_grad_x parts self.precomp_evaluate(x, precomp) self.precomp_grad_x(x, precomp) # precomp_hess_x part to_compute_flag = False for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): try: tmp = p['partial2_x_V_list'] except KeyError: to_compute_flag = True break if to_compute_flag: vand = [ b.GradVandermonde(x[:,i], int(o), k=2) for i,(o,b) in enumerate(zip(self.max_orders,self.basis_list)) ] for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): p['partial2_x_V_list'] = [ vand[var] for var in avar ] return precomp
[docs] def precomp_nabla3_x(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`\nabla^3_{\bf x}T({\bf x},{\bf a})` Enriches the dictionaries in the ``precomp`` list if necessary. Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`ndarray<numpy.ndarray>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} # precomp_evaluate precomp_grad_x precomp_hess_x parts self.precomp_evaluate(x, precomp) self.precomp_grad_x(x, precomp) self.precomp_hess_x(x, precomp) # precomp_nabla3_x to_compute_flag = False for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): try: tmp = p['partial3_x_V_list'] except KeyError: to_compute_flag = True break if to_compute_flag: vand = [ b.GradVandermonde(x[:,i], int(o), k=3) for i,(o,b) in enumerate(zip(self.max_orders, self.basis_list)) ] for i,(avar,p) in enumerate(zip(self.active_vars, precomp['components'])): p['partial3_x_V_list'] = [ vand[var] for var in avar ] return precomp
[docs] def precomp_partial_xd(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`\partial_{x_k}T_k({\bf x},{\bf a})` for :math:`k=1,\ldots,d` Enriches the dictionaries in the ``precomp`` list if necessary. Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`ndarray<numpy.ndarray>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} # precomp_evaluate and precomp_grad_x parts self.precomp_evaluate(x, precomp) self.precomp_grad_x(x, precomp) # Generate partial_xd_V_last fields for p in precomp['components']: p['partial_xd_V_last'] = p['partial_x_V_list'][-1] return precomp
[docs] def precomp_grad_x_partial_xd(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`\nabla_{\bf x}\partial_{x_k}T_k({\bf x},{\bf a})` for :math:`k=1,\ldots,d` Enriches the dictionaries in the ``precomp`` list if necessary. Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`dict<dict>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} # precomp_evaluate, precomp_grad_x and precomp_hess_x parts self.precomp_evaluate(x, precomp) self.precomp_grad_x(x, precomp) self.precomp_hess_x(x, precomp) # Generate partial2_xd_V_last fields for p in precomp['components']: p['partial2_xd_V_last'] = p['partial2_x_V_list'][-1] return precomp
[docs] def precomp_hess_x_partial_xd(self, x, precomp=None, precomp_type='uni'): r""" Precompute necessary structures for the evaluation of :math:`\nabla^2_{\bf x}\partial_{x_k}T_k({\bf x},{\bf a})` for :math:`k=1,\ldots,d` Args: x (:class:`ndarray<numpy.ndarray>` [:math:`m,d`]): evaluation points precomp (dict): list of dictionaries of precomputed values precomp_type (str): only option 'uni' is allowed for this TransportMap Returns: (:class:`dict<dict>` of :class:`list<list>` [:math:`d`] :class:`dict<dict>`) -- necessary structures """ if precomp_type != 'uni': raise ValueError("Only option 'uni' is allowed for CommonBasisTransportMaps") if precomp is None: precomp = {'components': [{} for i in range(self.dim)]} # precomp_evaluate, precomp_grad_x, precomp_hess_x, precomp_nabla3_x parts self.precomp_evaluate(x, precomp) self.precomp_grad_x(x, precomp) self.precomp_hess_x(x, precomp) self.precomp_nabla3_x(x, precomp) # Generate partial3_xd_V_last fields for p in precomp['components']: p['partial3_xd_V_last'] = p['partial3_x_V_list'][-1] return precomp