Source code for easyvvuq.sampling.transformations

import chaospy as cp
import numpy as np

__author__ = "Juraj Kardos"
__copyright__ = """

    Copyright 2022 Juraj Kardos

    This file is part of EasyVVUQ

    EasyVVUQ is free software: you can redistribute it and/or modify
    it under the terms of the Lesser GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    EasyVVUQ 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
    Lesser GNU General Public License for more details.

    You should have received a copy of the Lesser GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""
__license__ = "LGPL"


[docs] class Transformations: def __init__(self): pass # Applies Rosenblatt transformation # to the independent nodes. # Returns: The transformed nodes or transformed (weights, nodes) # Args: # @Nodes - Independent nodes to be transformed # @Distribution - PDF of the independent nodes # @Distribution_dep - PDF of the correlated nodes # @regression - see PCESampler(regression) parameter
[docs] @staticmethod def rosenblatt(nodes, distribution, distribution_dep, regression=True): # Input nodes are expected to be in sape (ndim x nsamples), # but user might have have provided the transposed array do_transpose = False if not nodes.shape[0] == len(distribution): do_transpose = True nodes = nodes.T if not nodes.shape[0] == len(distribution): raise ValueError("Input nodes have wrong shape.") transformed_nodes = [] transformed_nodes = distribution_dep.inv(distribution.fwd(nodes)) if do_transpose: transformed_nodes = transformed_nodes.T nodes = nodes.T # need to transpose back (args. are passed by reference) # Transform node weights in the pseudo-spectral method if not regression: # The transformed weights are not used transformed_weights = None # TODO: need to add weights argument # transformed_weights = weights * distribution_dep.pdf(transformed_nodes)/distribution.pdf(nodes) return (transformed_weights, transformed_nodes) return transformed_nodes
# Applies Cholesky transformation # to the independent nodes. # Returns: The transformed nodes or transformed (weights, nodes) # Args: # @Nodes - Independent nodes to be transformed # @vary - Vary object containing the PDF of the parameters # @correlation - correlation matrix # @regression - see PCESampler(regression) parameter
[docs] @staticmethod def cholesky(nodes, vary, correlation, regression=True): if str(type(vary)) == "<class 'dict'>": items = vary.items() # simple dictionary else: items = vary.get_items() # Vary object nparams = len(items) # Input nodes are expected to be in shape (ndim x nsamples), # but user might have have provided the transposed array do_transpose = False if not nodes.shape[0] == nparams: do_transpose = True nodes = nodes.T if not nodes.shape[0] == nparams: raise ValueError("Input nodes have wrong shape.") # Shift and stretch the nodes to a unit normal distribution # Until now we have samples from a general non-unit normal distribution nodes_unit = np.zeros(nodes.shape) for i, (param, distribution) in enumerate(items): if type(distribution).__name__ == "Uniform": a = distribution._parameters['lower'] #lower b = distribution._parameters['upper'] #upper nodes_unit[i] = (nodes[i] - a) / (b-a) elif type(distribution).__name__ == "Normal": a = distribution._parameters['shift'] #mu b = distribution._parameters['scale'] #sigma nodes_unit[i] = (nodes[i] - a) / b transformed_nodes = [] L = np.linalg.cholesky(correlation) transformed_nodes = np.matmul(L, nodes_unit) # Shift and stretch the transformed nodes to the target distr. # Until now we had samples from unit uniform (or normal) distributions for i, (param, distribution) in enumerate(items): if type(distribution).__name__ == "Uniform": a = distribution._parameters['lower'] #lower b = distribution._parameters['upper'] #upper transformed_nodes[i] = a + (b-a)*transformed_nodes[i] elif type(distribution).__name__ == "Normal": a = distribution._parameters['shift'] #mu b = distribution._parameters['scale'] #sigma transformed_nodes[i] = a + b*transformed_nodes[i] if do_transpose: transformed_nodes = transformed_nodes.T nodes = nodes.T # need to transpose back (args. are passed by reference) # Tested & implemented only with the point collocation! # For spectral projection we need to work also with # the node weights, which requires some additional care # assert(regression) if not regression: transformed_weights = None return (transformed_weights, transformed_nodes) return transformed_nodes