fpex0.InitialDistribution
1import numpy as np 2import sympy 3import numbers 4 5 6class InitialDistribution: 7 """Class holding initial distribution. 8 9 ## Parameters 10 11 **functionExpressionList** : list 12 <br> list of piecewise function definitions 13 14 **supportList** : list of (tuples or functions) 15 <br> List of (open) support intervals, use numpy.inf and -numpy.inf for positive/negative infinity 16 if a function is given, it will be evaluated with current parameters and must return a tuple for the support. 17 18 **psymbols** : tuple of sympy symbols 19 <br> Tuple of parameter set (used in all support intervals). 20 21 **xsymbol** : sympy symbol 22 <br> Symbol used as x variable (default: x). 23 24 **name** : str 25 <br> A handy name for the distribution. 26 """ 27 28 def __init__(self, funcExprList, supportList, psymbols, xsymbol = sympy.symbols("x"), name = "unnamed"): 29 30 # ensure the list variables are indeed lists (wrap singletons) 31 if not(isinstance(funcExprList, list)): funcExprList = [funcExprList] 32 if not(isinstance( supportList, list)): supportList = [supportList] 33 34 # ensure we have as many functions as supports 35 assert( len(funcExprList) == len(supportList) ) 36 37 # dimensions 38 supportCount = len(supportList) 39 paramCount = len(psymbols) 40 41 # generate functions and derivatives w.r.t. p (i.e. dfdp) 42 dfdp_sym = np.empty( (supportCount,paramCount) , dtype=object ) 43 dfdp_fun = np.empty( (supportCount,paramCount) , dtype=object ) 44 f_sym = np.empty( supportCount , dtype=object ) 45 f_fun = np.empty( supportCount , dtype=object ) 46 for (j , fExpr) in enumerate(funcExprList): 47 f_sym[j] = funcExprList[j] # store symbolic function 48 f_fun[j] = self.sympy2numpyXP(f_sym[j], xsymbol, psymbols) # transform to function 49 for (i, p) in enumerate(psymbols): 50 symfun = sympy.diff(fExpr, p) # derivative w.r.t. p_i 51 dfdp_sym[j,i] = symfun # store symbolic function 52 dfdp_fun[j,i] = self.sympy2numpyXP(symfun, xsymbol, psymbols) # transform to function 53 54 # transform the support into support functions 55 for (j, support) in enumerate(supportList): 56 (l,r) = support # extract left and right 57 lfun = self.functionalizeSupportDescriptor(l, psymbols) 58 rfun = self.functionalizeSupportDescriptor(r, psymbols) 59 supportList[j] = (lfun,rfun) # store support functions 60 61 62 # store quantities (mostly for debugging) 63 self.name = name 64 self.funcExprList = funcExprList 65 self.supportList = supportList 66 self.paramList = psymbols 67 self.xsymbol = xsymbol 68 self.supportCount = supportCount 69 self.paramCount = paramCount 70 self.f_fun = f_fun 71 self.f_sym = f_sym 72 self.dfdp_fun = dfdp_fun 73 self.dfdp_sym = dfdp_sym 74 75 # generate evaluator functions 76 return None 77 78 79 # some helpers 80 81 @staticmethod 82 def getValuesWithinInterval(x, interval): 83 """Returns values and indices of numpy array x that are inside given interval.""" 84 # NOTE: Boolean vs. integer indexing depends on size and count 85 (l,r) = interval # extract interval boundaries 86 idx = np.where((x>l) & (x<r)) # returns a 1-element tuple 87 values = x[idx] # extract values 88 return values, idx 89 90 91 @staticmethod 92 def evalSupport(support, p): 93 """Evaluates support functions at given parameter vector.""" 94 (l,r) = support 95 return (l(p), r(p)) 96 97 @staticmethod 98 def sympy2numpyXP(fun, xsymbol, psymbols): 99 """Lambdify specified function as a function of x and p.""" 100 xpsymbols = (xsymbol,) + tuple(psymbols) # collect all symbols 101 multiargfun = sympy.lambdify(xpsymbols, fun, 'numpy') # function of x and individual parameters 102 xpfun = lambda x,p : multiargfun(x, *p) # function that decollates vector p in multiple args 103 return xpfun 104 105 106 @staticmethod 107 def sympy2numpyP(fun, symbols): 108 """Lambdify specified function.""" 109 multiargfun = sympy.lambdify(tuple(symbols), fun, 'numpy') # function of individual parameters 110 pfun = lambda p : multiargfun(*p) # function that decollated vector p in multiple args 111 return pfun 112 113 114 @staticmethod 115 def functionalizeSupportDescriptor(thing, symbols): 116 """Transforms a support descriptor into a callable function.""" 117 fun = None 118 if isinstance(thing, sympy.Expr): fun = InitialDistribution.sympy2numpyP(thing, symbols) 119 if isinstance(thing, numbers.Number): fun = lambda *ignore: thing 120 if (fun == None): raise Exception("Invalid support descriptor", thing) 121 return fun 122 123 # @staticmethod 124 # def compileFunction(fun): 125 # debugdir = "./codegen" 126 # cfun = autowrap(fun, tempdir=debugdir) 127 # return cfun 128 129 130 def f(self, x, p): 131 """Evaluates nominal function at x with parameter vector p. 132 ## Takes 133 **x**: numpy vector <br> 134 **p**: numpy vector 135 136 137 ## Returns: 138 **f**: numpy vector 139 """ 140 fvals = np.zeros_like(x) 141 for (j,support) in enumerate( self.supportList ): # walk through the individual functions 142 support = self.evalSupport(support, p) # evaluate support functions 143 (xx, idx) = self.getValuesWithinInterval( x , support ) # get x-values in current support 144 fvals[idx] = self.f_fun[j]( xx , p ) # evaluate function 145 return fvals 146 147 148 def dfdp(self, x, p): 149 """Evaluates jacobian at x with parameter vector p. 150 ## Takes 151 **x**: numpy vector <br> 152 **p**: numpy vector 153 154 155 ## Returns 156 **J**: Numpy matrix dfdp, J[i][...] contains sensitivity w.r.t. p[i]. 157 """ 158 dfdpvals = np.zeros_like( x , shape = (len(p),len(x)) ) # preallocate 159 for (j,support) in enumerate( self.supportList ): # walk through supports 160 support = self.evalSupport(support, p) # determine support 161 (x, idx) = self.getValuesWithinInterval( x , support ) # extract x values within support 162 for i in range(0, self.paramCount): # walk through derivative funcions 163 dfdpvals[i][idx] = self.dfdp_fun[j,i](x,p) # evaluate derivative functions 164 return dfdpvals
7class InitialDistribution: 8 """Class holding initial distribution. 9 10 ## Parameters 11 12 **functionExpressionList** : list 13 <br> list of piecewise function definitions 14 15 **supportList** : list of (tuples or functions) 16 <br> List of (open) support intervals, use numpy.inf and -numpy.inf for positive/negative infinity 17 if a function is given, it will be evaluated with current parameters and must return a tuple for the support. 18 19 **psymbols** : tuple of sympy symbols 20 <br> Tuple of parameter set (used in all support intervals). 21 22 **xsymbol** : sympy symbol 23 <br> Symbol used as x variable (default: x). 24 25 **name** : str 26 <br> A handy name for the distribution. 27 """ 28 29 def __init__(self, funcExprList, supportList, psymbols, xsymbol = sympy.symbols("x"), name = "unnamed"): 30 31 # ensure the list variables are indeed lists (wrap singletons) 32 if not(isinstance(funcExprList, list)): funcExprList = [funcExprList] 33 if not(isinstance( supportList, list)): supportList = [supportList] 34 35 # ensure we have as many functions as supports 36 assert( len(funcExprList) == len(supportList) ) 37 38 # dimensions 39 supportCount = len(supportList) 40 paramCount = len(psymbols) 41 42 # generate functions and derivatives w.r.t. p (i.e. dfdp) 43 dfdp_sym = np.empty( (supportCount,paramCount) , dtype=object ) 44 dfdp_fun = np.empty( (supportCount,paramCount) , dtype=object ) 45 f_sym = np.empty( supportCount , dtype=object ) 46 f_fun = np.empty( supportCount , dtype=object ) 47 for (j , fExpr) in enumerate(funcExprList): 48 f_sym[j] = funcExprList[j] # store symbolic function 49 f_fun[j] = self.sympy2numpyXP(f_sym[j], xsymbol, psymbols) # transform to function 50 for (i, p) in enumerate(psymbols): 51 symfun = sympy.diff(fExpr, p) # derivative w.r.t. p_i 52 dfdp_sym[j,i] = symfun # store symbolic function 53 dfdp_fun[j,i] = self.sympy2numpyXP(symfun, xsymbol, psymbols) # transform to function 54 55 # transform the support into support functions 56 for (j, support) in enumerate(supportList): 57 (l,r) = support # extract left and right 58 lfun = self.functionalizeSupportDescriptor(l, psymbols) 59 rfun = self.functionalizeSupportDescriptor(r, psymbols) 60 supportList[j] = (lfun,rfun) # store support functions 61 62 63 # store quantities (mostly for debugging) 64 self.name = name 65 self.funcExprList = funcExprList 66 self.supportList = supportList 67 self.paramList = psymbols 68 self.xsymbol = xsymbol 69 self.supportCount = supportCount 70 self.paramCount = paramCount 71 self.f_fun = f_fun 72 self.f_sym = f_sym 73 self.dfdp_fun = dfdp_fun 74 self.dfdp_sym = dfdp_sym 75 76 # generate evaluator functions 77 return None 78 79 80 # some helpers 81 82 @staticmethod 83 def getValuesWithinInterval(x, interval): 84 """Returns values and indices of numpy array x that are inside given interval.""" 85 # NOTE: Boolean vs. integer indexing depends on size and count 86 (l,r) = interval # extract interval boundaries 87 idx = np.where((x>l) & (x<r)) # returns a 1-element tuple 88 values = x[idx] # extract values 89 return values, idx 90 91 92 @staticmethod 93 def evalSupport(support, p): 94 """Evaluates support functions at given parameter vector.""" 95 (l,r) = support 96 return (l(p), r(p)) 97 98 @staticmethod 99 def sympy2numpyXP(fun, xsymbol, psymbols): 100 """Lambdify specified function as a function of x and p.""" 101 xpsymbols = (xsymbol,) + tuple(psymbols) # collect all symbols 102 multiargfun = sympy.lambdify(xpsymbols, fun, 'numpy') # function of x and individual parameters 103 xpfun = lambda x,p : multiargfun(x, *p) # function that decollates vector p in multiple args 104 return xpfun 105 106 107 @staticmethod 108 def sympy2numpyP(fun, symbols): 109 """Lambdify specified function.""" 110 multiargfun = sympy.lambdify(tuple(symbols), fun, 'numpy') # function of individual parameters 111 pfun = lambda p : multiargfun(*p) # function that decollated vector p in multiple args 112 return pfun 113 114 115 @staticmethod 116 def functionalizeSupportDescriptor(thing, symbols): 117 """Transforms a support descriptor into a callable function.""" 118 fun = None 119 if isinstance(thing, sympy.Expr): fun = InitialDistribution.sympy2numpyP(thing, symbols) 120 if isinstance(thing, numbers.Number): fun = lambda *ignore: thing 121 if (fun == None): raise Exception("Invalid support descriptor", thing) 122 return fun 123 124 # @staticmethod 125 # def compileFunction(fun): 126 # debugdir = "./codegen" 127 # cfun = autowrap(fun, tempdir=debugdir) 128 # return cfun 129 130 131 def f(self, x, p): 132 """Evaluates nominal function at x with parameter vector p. 133 ## Takes 134 **x**: numpy vector <br> 135 **p**: numpy vector 136 137 138 ## Returns: 139 **f**: numpy vector 140 """ 141 fvals = np.zeros_like(x) 142 for (j,support) in enumerate( self.supportList ): # walk through the individual functions 143 support = self.evalSupport(support, p) # evaluate support functions 144 (xx, idx) = self.getValuesWithinInterval( x , support ) # get x-values in current support 145 fvals[idx] = self.f_fun[j]( xx , p ) # evaluate function 146 return fvals 147 148 149 def dfdp(self, x, p): 150 """Evaluates jacobian at x with parameter vector p. 151 ## Takes 152 **x**: numpy vector <br> 153 **p**: numpy vector 154 155 156 ## Returns 157 **J**: Numpy matrix dfdp, J[i][...] contains sensitivity w.r.t. p[i]. 158 """ 159 dfdpvals = np.zeros_like( x , shape = (len(p),len(x)) ) # preallocate 160 for (j,support) in enumerate( self.supportList ): # walk through supports 161 support = self.evalSupport(support, p) # determine support 162 (x, idx) = self.getValuesWithinInterval( x , support ) # extract x values within support 163 for i in range(0, self.paramCount): # walk through derivative funcions 164 dfdpvals[i][idx] = self.dfdp_fun[j,i](x,p) # evaluate derivative functions 165 return dfdpvals
Class holding initial distribution.
Parameters
functionExpressionList : list
list of piecewise function definitions
supportList : list of (tuples or functions)
List of (open) support intervals, use numpy.inf and -numpy.inf for positive/negative infinity
if a function is given, it will be evaluated with current parameters and must return a tuple for the support.
psymbols : tuple of sympy symbols
Tuple of parameter set (used in all support intervals).
xsymbol : sympy symbol
Symbol used as x variable (default: x).
name : str
A handy name for the distribution.
29 def __init__(self, funcExprList, supportList, psymbols, xsymbol = sympy.symbols("x"), name = "unnamed"): 30 31 # ensure the list variables are indeed lists (wrap singletons) 32 if not(isinstance(funcExprList, list)): funcExprList = [funcExprList] 33 if not(isinstance( supportList, list)): supportList = [supportList] 34 35 # ensure we have as many functions as supports 36 assert( len(funcExprList) == len(supportList) ) 37 38 # dimensions 39 supportCount = len(supportList) 40 paramCount = len(psymbols) 41 42 # generate functions and derivatives w.r.t. p (i.e. dfdp) 43 dfdp_sym = np.empty( (supportCount,paramCount) , dtype=object ) 44 dfdp_fun = np.empty( (supportCount,paramCount) , dtype=object ) 45 f_sym = np.empty( supportCount , dtype=object ) 46 f_fun = np.empty( supportCount , dtype=object ) 47 for (j , fExpr) in enumerate(funcExprList): 48 f_sym[j] = funcExprList[j] # store symbolic function 49 f_fun[j] = self.sympy2numpyXP(f_sym[j], xsymbol, psymbols) # transform to function 50 for (i, p) in enumerate(psymbols): 51 symfun = sympy.diff(fExpr, p) # derivative w.r.t. p_i 52 dfdp_sym[j,i] = symfun # store symbolic function 53 dfdp_fun[j,i] = self.sympy2numpyXP(symfun, xsymbol, psymbols) # transform to function 54 55 # transform the support into support functions 56 for (j, support) in enumerate(supportList): 57 (l,r) = support # extract left and right 58 lfun = self.functionalizeSupportDescriptor(l, psymbols) 59 rfun = self.functionalizeSupportDescriptor(r, psymbols) 60 supportList[j] = (lfun,rfun) # store support functions 61 62 63 # store quantities (mostly for debugging) 64 self.name = name 65 self.funcExprList = funcExprList 66 self.supportList = supportList 67 self.paramList = psymbols 68 self.xsymbol = xsymbol 69 self.supportCount = supportCount 70 self.paramCount = paramCount 71 self.f_fun = f_fun 72 self.f_sym = f_sym 73 self.dfdp_fun = dfdp_fun 74 self.dfdp_sym = dfdp_sym 75 76 # generate evaluator functions 77 return None
82 @staticmethod 83 def getValuesWithinInterval(x, interval): 84 """Returns values and indices of numpy array x that are inside given interval.""" 85 # NOTE: Boolean vs. integer indexing depends on size and count 86 (l,r) = interval # extract interval boundaries 87 idx = np.where((x>l) & (x<r)) # returns a 1-element tuple 88 values = x[idx] # extract values 89 return values, idx
Returns values and indices of numpy array x that are inside given interval.
92 @staticmethod 93 def evalSupport(support, p): 94 """Evaluates support functions at given parameter vector.""" 95 (l,r) = support 96 return (l(p), r(p))
Evaluates support functions at given parameter vector.
98 @staticmethod 99 def sympy2numpyXP(fun, xsymbol, psymbols): 100 """Lambdify specified function as a function of x and p.""" 101 xpsymbols = (xsymbol,) + tuple(psymbols) # collect all symbols 102 multiargfun = sympy.lambdify(xpsymbols, fun, 'numpy') # function of x and individual parameters 103 xpfun = lambda x,p : multiargfun(x, *p) # function that decollates vector p in multiple args 104 return xpfun
Lambdify specified function as a function of x and p.
107 @staticmethod 108 def sympy2numpyP(fun, symbols): 109 """Lambdify specified function.""" 110 multiargfun = sympy.lambdify(tuple(symbols), fun, 'numpy') # function of individual parameters 111 pfun = lambda p : multiargfun(*p) # function that decollated vector p in multiple args 112 return pfun
Lambdify specified function.
115 @staticmethod 116 def functionalizeSupportDescriptor(thing, symbols): 117 """Transforms a support descriptor into a callable function.""" 118 fun = None 119 if isinstance(thing, sympy.Expr): fun = InitialDistribution.sympy2numpyP(thing, symbols) 120 if isinstance(thing, numbers.Number): fun = lambda *ignore: thing 121 if (fun == None): raise Exception("Invalid support descriptor", thing) 122 return fun
Transforms a support descriptor into a callable function.
131 def f(self, x, p): 132 """Evaluates nominal function at x with parameter vector p. 133 ## Takes 134 **x**: numpy vector <br> 135 **p**: numpy vector 136 137 138 ## Returns: 139 **f**: numpy vector 140 """ 141 fvals = np.zeros_like(x) 142 for (j,support) in enumerate( self.supportList ): # walk through the individual functions 143 support = self.evalSupport(support, p) # evaluate support functions 144 (xx, idx) = self.getValuesWithinInterval( x , support ) # get x-values in current support 145 fvals[idx] = self.f_fun[j]( xx , p ) # evaluate function 146 return fvals
Evaluates nominal function at x with parameter vector p.
Takes
x: numpy vector
p: numpy vector
Returns:
f: numpy vector
149 def dfdp(self, x, p): 150 """Evaluates jacobian at x with parameter vector p. 151 ## Takes 152 **x**: numpy vector <br> 153 **p**: numpy vector 154 155 156 ## Returns 157 **J**: Numpy matrix dfdp, J[i][...] contains sensitivity w.r.t. p[i]. 158 """ 159 dfdpvals = np.zeros_like( x , shape = (len(p),len(x)) ) # preallocate 160 for (j,support) in enumerate( self.supportList ): # walk through supports 161 support = self.evalSupport(support, p) # determine support 162 (x, idx) = self.getValuesWithinInterval( x , support ) # extract x values within support 163 for i in range(0, self.paramCount): # walk through derivative funcions 164 dfdpvals[i][idx] = self.dfdp_fun[j,i](x,p) # evaluate derivative functions 165 return dfdpvals
Evaluates jacobian at x with parameter vector p.
Takes
x: numpy vector
p: numpy vector
Returns
J: Numpy matrix dfdp, J[i][...] contains sensitivity w.r.t. p[i].