easyvvuq.sampling.transformations
1import chaospy as cp 2import numpy as np 3 4__author__ = "Juraj Kardos" 5__copyright__ = """ 6 7 Copyright 2022 Juraj Kardos 8 9 This file is part of EasyVVUQ 10 11 EasyVVUQ is free software: you can redistribute it and/or modify 12 it under the terms of the Lesser GNU General Public License as published by 13 the Free Software Foundation, either version 3 of the License, or 14 (at your option) any later version. 15 16 EasyVVUQ is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 Lesser GNU General Public License for more details. 20 21 You should have received a copy of the Lesser GNU General Public License 22 along with this program. If not, see <https://www.gnu.org/licenses/>. 23 24""" 25__license__ = "LGPL" 26 27 28class Transformations: 29 def __init__(self): 30 pass 31 32 # Applies Rosenblatt transformation 33 # to the independent nodes. 34 # Returns: The transformed nodes or transformed (weights, nodes) 35 # Args: 36 # @Nodes - Independent nodes to be transformed 37 # @Distribution - PDF of the independent nodes 38 # @Distribution_dep - PDF of the correlated nodes 39 # @regression - see PCESampler(regression) parameter 40 @staticmethod 41 def rosenblatt(nodes, distribution, distribution_dep, regression=True): 42 43 # Input nodes are expected to be in sape (ndim x nsamples), 44 # but user might have have provided the transposed array 45 do_transpose = False 46 if not nodes.shape[0] == len(distribution): 47 do_transpose = True 48 nodes = nodes.T 49 if not nodes.shape[0] == len(distribution): 50 raise ValueError("Input nodes have wrong shape.") 51 52 transformed_nodes = [] 53 54 transformed_nodes = distribution_dep.inv(distribution.fwd(nodes)) 55 56 if do_transpose: 57 transformed_nodes = transformed_nodes.T 58 nodes = nodes.T # need to transpose back (args. are passed by reference) 59 60 # Transform node weights in the pseudo-spectral method 61 if not regression: 62 # The transformed weights are not used 63 transformed_weights = None 64 # TODO: need to add weights argument 65 # transformed_weights = weights * distribution_dep.pdf(transformed_nodes)/distribution.pdf(nodes) 66 return (transformed_weights, transformed_nodes) 67 68 return transformed_nodes 69 70 # Applies Cholesky transformation 71 # to the independent nodes. 72 # Returns: The transformed nodes or transformed (weights, nodes) 73 # Args: 74 # @Nodes - Independent nodes to be transformed 75 # @vary - Vary object containing the PDF of the parameters 76 # @correlation - correlation matrix 77 # @regression - see PCESampler(regression) parameter 78 @staticmethod 79 def cholesky(nodes, vary, correlation, regression=True): 80 81 if str(type(vary)) == "<class 'dict'>": 82 items = vary.items() # simple dictionary 83 else: 84 items = vary.get_items() # Vary object 85 nparams = len(items) 86 87 # Input nodes are expected to be in shape (ndim x nsamples), 88 # but user might have have provided the transposed array 89 do_transpose = False 90 if not nodes.shape[0] == nparams: 91 do_transpose = True 92 nodes = nodes.T 93 if not nodes.shape[0] == nparams: 94 raise ValueError("Input nodes have wrong shape.") 95 96 # Shift and stretch the nodes to a unit normal distribution 97 # Until now we have samples from a general non-unit normal distribution 98 nodes_unit = np.zeros(nodes.shape) 99 for i, (param, distribution) in enumerate(items): 100 if type(distribution).__name__ == "Uniform": 101 a = distribution._parameters['lower'] #lower 102 b = distribution._parameters['upper'] #upper 103 nodes_unit[i] = (nodes[i] - a) / (b-a) 104 elif type(distribution).__name__ == "Normal": 105 a = distribution._parameters['shift'] #mu 106 b = distribution._parameters['scale'] #sigma 107 nodes_unit[i] = (nodes[i] - a) / b 108 109 transformed_nodes = [] 110 L = np.linalg.cholesky(correlation) 111 transformed_nodes = np.matmul(L, nodes_unit) 112 113 # Shift and stretch the transformed nodes to the target distr. 114 # Until now we had samples from unit uniform (or normal) distributions 115 for i, (param, distribution) in enumerate(items): 116 if type(distribution).__name__ == "Uniform": 117 a = distribution._parameters['lower'] #lower 118 b = distribution._parameters['upper'] #upper 119 transformed_nodes[i] = a + (b-a)*transformed_nodes[i] 120 elif type(distribution).__name__ == "Normal": 121 a = distribution._parameters['shift'] #mu 122 b = distribution._parameters['scale'] #sigma 123 transformed_nodes[i] = a + b*transformed_nodes[i] 124 125 if do_transpose: 126 transformed_nodes = transformed_nodes.T 127 nodes = nodes.T # need to transpose back (args. are passed by reference) 128 129 # Tested & implemented only with the point collocation! 130 # For spectral projection we need to work also with 131 # the node weights, which requires some additional care 132 # assert(regression) 133 if not regression: 134 transformed_weights = None 135 return (transformed_weights, transformed_nodes) 136 137 return transformed_nodes
class
Transformations:
29class Transformations: 30 def __init__(self): 31 pass 32 33 # Applies Rosenblatt transformation 34 # to the independent nodes. 35 # Returns: The transformed nodes or transformed (weights, nodes) 36 # Args: 37 # @Nodes - Independent nodes to be transformed 38 # @Distribution - PDF of the independent nodes 39 # @Distribution_dep - PDF of the correlated nodes 40 # @regression - see PCESampler(regression) parameter 41 @staticmethod 42 def rosenblatt(nodes, distribution, distribution_dep, regression=True): 43 44 # Input nodes are expected to be in sape (ndim x nsamples), 45 # but user might have have provided the transposed array 46 do_transpose = False 47 if not nodes.shape[0] == len(distribution): 48 do_transpose = True 49 nodes = nodes.T 50 if not nodes.shape[0] == len(distribution): 51 raise ValueError("Input nodes have wrong shape.") 52 53 transformed_nodes = [] 54 55 transformed_nodes = distribution_dep.inv(distribution.fwd(nodes)) 56 57 if do_transpose: 58 transformed_nodes = transformed_nodes.T 59 nodes = nodes.T # need to transpose back (args. are passed by reference) 60 61 # Transform node weights in the pseudo-spectral method 62 if not regression: 63 # The transformed weights are not used 64 transformed_weights = None 65 # TODO: need to add weights argument 66 # transformed_weights = weights * distribution_dep.pdf(transformed_nodes)/distribution.pdf(nodes) 67 return (transformed_weights, transformed_nodes) 68 69 return transformed_nodes 70 71 # Applies Cholesky transformation 72 # to the independent nodes. 73 # Returns: The transformed nodes or transformed (weights, nodes) 74 # Args: 75 # @Nodes - Independent nodes to be transformed 76 # @vary - Vary object containing the PDF of the parameters 77 # @correlation - correlation matrix 78 # @regression - see PCESampler(regression) parameter 79 @staticmethod 80 def cholesky(nodes, vary, correlation, regression=True): 81 82 if str(type(vary)) == "<class 'dict'>": 83 items = vary.items() # simple dictionary 84 else: 85 items = vary.get_items() # Vary object 86 nparams = len(items) 87 88 # Input nodes are expected to be in shape (ndim x nsamples), 89 # but user might have have provided the transposed array 90 do_transpose = False 91 if not nodes.shape[0] == nparams: 92 do_transpose = True 93 nodes = nodes.T 94 if not nodes.shape[0] == nparams: 95 raise ValueError("Input nodes have wrong shape.") 96 97 # Shift and stretch the nodes to a unit normal distribution 98 # Until now we have samples from a general non-unit normal distribution 99 nodes_unit = np.zeros(nodes.shape) 100 for i, (param, distribution) in enumerate(items): 101 if type(distribution).__name__ == "Uniform": 102 a = distribution._parameters['lower'] #lower 103 b = distribution._parameters['upper'] #upper 104 nodes_unit[i] = (nodes[i] - a) / (b-a) 105 elif type(distribution).__name__ == "Normal": 106 a = distribution._parameters['shift'] #mu 107 b = distribution._parameters['scale'] #sigma 108 nodes_unit[i] = (nodes[i] - a) / b 109 110 transformed_nodes = [] 111 L = np.linalg.cholesky(correlation) 112 transformed_nodes = np.matmul(L, nodes_unit) 113 114 # Shift and stretch the transformed nodes to the target distr. 115 # Until now we had samples from unit uniform (or normal) distributions 116 for i, (param, distribution) in enumerate(items): 117 if type(distribution).__name__ == "Uniform": 118 a = distribution._parameters['lower'] #lower 119 b = distribution._parameters['upper'] #upper 120 transformed_nodes[i] = a + (b-a)*transformed_nodes[i] 121 elif type(distribution).__name__ == "Normal": 122 a = distribution._parameters['shift'] #mu 123 b = distribution._parameters['scale'] #sigma 124 transformed_nodes[i] = a + b*transformed_nodes[i] 125 126 if do_transpose: 127 transformed_nodes = transformed_nodes.T 128 nodes = nodes.T # need to transpose back (args. are passed by reference) 129 130 # Tested & implemented only with the point collocation! 131 # For spectral projection we need to work also with 132 # the node weights, which requires some additional care 133 # assert(regression) 134 if not regression: 135 transformed_weights = None 136 return (transformed_weights, transformed_nodes) 137 138 return transformed_nodes
@staticmethod
def
rosenblatt(nodes, distribution, distribution_dep, regression=True):
41 @staticmethod 42 def rosenblatt(nodes, distribution, distribution_dep, regression=True): 43 44 # Input nodes are expected to be in sape (ndim x nsamples), 45 # but user might have have provided the transposed array 46 do_transpose = False 47 if not nodes.shape[0] == len(distribution): 48 do_transpose = True 49 nodes = nodes.T 50 if not nodes.shape[0] == len(distribution): 51 raise ValueError("Input nodes have wrong shape.") 52 53 transformed_nodes = [] 54 55 transformed_nodes = distribution_dep.inv(distribution.fwd(nodes)) 56 57 if do_transpose: 58 transformed_nodes = transformed_nodes.T 59 nodes = nodes.T # need to transpose back (args. are passed by reference) 60 61 # Transform node weights in the pseudo-spectral method 62 if not regression: 63 # The transformed weights are not used 64 transformed_weights = None 65 # TODO: need to add weights argument 66 # transformed_weights = weights * distribution_dep.pdf(transformed_nodes)/distribution.pdf(nodes) 67 return (transformed_weights, transformed_nodes) 68 69 return transformed_nodes
@staticmethod
def
cholesky(nodes, vary, correlation, regression=True):
79 @staticmethod 80 def cholesky(nodes, vary, correlation, regression=True): 81 82 if str(type(vary)) == "<class 'dict'>": 83 items = vary.items() # simple dictionary 84 else: 85 items = vary.get_items() # Vary object 86 nparams = len(items) 87 88 # Input nodes are expected to be in shape (ndim x nsamples), 89 # but user might have have provided the transposed array 90 do_transpose = False 91 if not nodes.shape[0] == nparams: 92 do_transpose = True 93 nodes = nodes.T 94 if not nodes.shape[0] == nparams: 95 raise ValueError("Input nodes have wrong shape.") 96 97 # Shift and stretch the nodes to a unit normal distribution 98 # Until now we have samples from a general non-unit normal distribution 99 nodes_unit = np.zeros(nodes.shape) 100 for i, (param, distribution) in enumerate(items): 101 if type(distribution).__name__ == "Uniform": 102 a = distribution._parameters['lower'] #lower 103 b = distribution._parameters['upper'] #upper 104 nodes_unit[i] = (nodes[i] - a) / (b-a) 105 elif type(distribution).__name__ == "Normal": 106 a = distribution._parameters['shift'] #mu 107 b = distribution._parameters['scale'] #sigma 108 nodes_unit[i] = (nodes[i] - a) / b 109 110 transformed_nodes = [] 111 L = np.linalg.cholesky(correlation) 112 transformed_nodes = np.matmul(L, nodes_unit) 113 114 # Shift and stretch the transformed nodes to the target distr. 115 # Until now we had samples from unit uniform (or normal) distributions 116 for i, (param, distribution) in enumerate(items): 117 if type(distribution).__name__ == "Uniform": 118 a = distribution._parameters['lower'] #lower 119 b = distribution._parameters['upper'] #upper 120 transformed_nodes[i] = a + (b-a)*transformed_nodes[i] 121 elif type(distribution).__name__ == "Normal": 122 a = distribution._parameters['shift'] #mu 123 b = distribution._parameters['scale'] #sigma 124 transformed_nodes[i] = a + b*transformed_nodes[i] 125 126 if do_transpose: 127 transformed_nodes = transformed_nodes.T 128 nodes = nodes.T # need to transpose back (args. are passed by reference) 129 130 # Tested & implemented only with the point collocation! 131 # For spectral projection we need to work also with 132 # the node weights, which requires some additional care 133 # assert(regression) 134 if not regression: 135 transformed_weights = None 136 return (transformed_weights, transformed_nodes) 137 138 return transformed_nodes