fpex0.InitialDistribution API documentation

# fpex0.InitialDistribution

View Source

      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

class InitialDistribution: View Source

      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.

InitialDistribution(funcExprList, supportList, psymbols, xsymbol=x, name='unnamed') View Source

    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

@staticmethod
def getValuesWithinInterval(x, interval): View Source

    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.

@staticmethod
def evalSupport(support, p): View Source

    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.

@staticmethod
def sympy2numpyXP(fun, xsymbol, psymbols): View Source

     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.

@staticmethod
def sympy2numpyP(fun, symbols): View Source

    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.

@staticmethod
def functionalizeSupportDescriptor(thing, symbols): View Source

    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.

def f(self, x, p): View Source

    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

def dfdp(self, x, p): View Source

    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].