Source code for klus.observables

import math
import numpy as _np
from scipy.spatial import distance
'''
Routines for observables
'''

[docs] def identity(x): ''' Identity function. ''' return x
[docs] class monomials(object): ''' Computation of monomials in d dimensions. ''' def __init__(self, p): ''' The parameter p defines the maximum order of the monomials. ''' self.p = p def __call__(self, x): ''' Evaluate all monomials of order up to p for all data points in x. ''' [d, m] = x.shape # d = dimension of state space, m = number of test points c = allMonomialPowers(d, self.p) # matrix containing all powers for the monomials n = c.shape[1] # number of monomials y = _np.ones([n, m]) for i in range(n): for j in range(d): y[i, :] = y[i, :] * _np.power(x[j, :], c[j, i]) return y
[docs] def diff(self, x): ''' Compute partial derivatives for all data points in x. ''' [d, m] = x.shape # d = dimension of state space, m = number of test points c = allMonomialPowers(d, self.p) # matrix containing all powers for the monomials n = c.shape[1] # number of monomials y = _np.zeros([n, d, m]) for i in range(n): # for all monomials for j in range(d): # for all dimensions e = c[:, i].copy() # exponents of ith monomial a = e[j] e[j] = e[j] - 1 # derivative w.r.t. j if _np.any(e < 0): continue # nothing to do, already zero y[i, j, :] = a*_np.ones([1, m]) for k in range(d): y[i, j, :] = y[i, j, :] * _np.power(x[k, :], e[k]) return y
[docs] def ddiff(self, x): ''' Compute second order derivatives for all data points in x. ''' [d, m] = x.shape # d = dimension of state space, m = number of test points c = allMonomialPowers(d, self.p) # matrix containing all powers for the monomials n = c.shape[1] # number of monomials y = _np.zeros([n, d, d, m]) for i in range(n): # for all monomials for j1 in range(d): # for all dimensions for j2 in range(d): # for all dimensions e = c[:, i].copy() # exponents of ith monomial a = e[j1] e[j1] = e[j1] - 1 # derivative w.r.t. j1 a *= e[j2]; e[j2] = e[j2] - 1 # derivative w.r.t. j2 if _np.any(e < 0): continue # nothing to do, already zero y[i, j1, j2, :] = a*_np.ones([1, m]) for k in range(d): y[i, j1, j2, :] = y[i, j1, j2, :] * _np.power(x[k, :], e[k]) return y
def __repr__(self): return 'Monomials of order up to %d.' % self.p
[docs] def display(self, alpha, d, name = None, eps = 1e-6): ''' Display the polynomial with coefficients alpha. ''' c = allMonomialPowers(d, self.p) # matrix containing all powers for the monomials if name != None: print(name + ' = ', end = '') ind, = _np.where(abs(alpha) > eps) k = ind.shape[0] if k == 0: # no nonzero coefficients print('0') return for i in range(k): if i == 0: print('%.5f' % alpha[ind[i]], end = '') else: if alpha[ind[i]] > 0: print(' + %.5f' % alpha[ind[i]], end = '') else: print(' - %.5f' % -alpha[ind[i]], end = '') self._displayMonomial(c[:, ind[i]]) print('')
def _displayMonomial(self, p): d = p.shape[0] if _np.all(p == 0): print('1', end = '') else: for j in range(d): if p[j] == 0: continue; if p[j] == 1: print(' x_%d' % (j+1), end = '') else: print(' x_%d^%d' % (j+1, p[j]), end = '')
[docs] class indicators(object): ''' Indicator functions for box discretization Omega. ''' def __init__(self, Omega): self.Omega = Omega def __call__(self, x): [d, m] = x.shape # d = dimension of state space, m = number of test points n = self.Omega.numBoxes() y = _np.zeros([n, m]) for i in range(m): ind = self.Omega.index(x[:, i]) pass if ind == -1: continue y[ind, i] = 1 return y def __repr__(self): return 'Indicator functions for box discretization.'
[docs] class gaussians(object): ''' Gaussians whose centers are the centers of the box discretization Omega. sigma: width of Gaussians ''' def __init__(self, Omega, sigma=1): self.Omega = Omega self.sigma = sigma def __call__(self, x): ''' Evaluate Gaussians for all data points in x. ''' c = self.Omega.midpointGrid() D = distance.cdist(c.T, x.T, 'sqeuclidean') y = _np.exp(-1/(2*self.sigma**2)*D) return y
[docs] def diff(self, x): ''' Compute partial derivatives for all data points in x. ''' [d, m] = x.shape # d = dimension of state space, m = number of test points n = self.Omega.numBoxes() # number of basis functions c = self.Omega.midpointGrid() D = distance.cdist(c.T, x.T, 'sqeuclidean') y = _np.zeros([n, d, m]) for i in range(n): # for all Gaussians for j in range(d): # for all dimensions y[i, j, :] = -2/(2*self.sigma**2) * (x[j, :] - c[j, i]) * _np.exp(-1/(2*self.sigma**2)*D[i, :]) return y
[docs] def ddiff(self, x): ''' Compute second order derivatives for all data points in x. ''' [d, m] = x.shape # d = dimension of state space, m = number of test points n = self.Omega.numBoxes() # number of basis functions c = self.Omega.midpointGrid() D = distance.cdist(c.T, x.T, 'sqeuclidean') y = _np.zeros([n, d, d, m]) for i in range(n): # for all monomials for j1 in range(d): # for all dimensions for j2 in range(d): # for all dimensions if j1 == j2: y[i, j1, j2, :] = ( -2/(2*self.sigma**2) + 4/(4*self.sigma**4) * (x[j1, :] - c[j1, i])**2 ) * _np.exp(-1/(2*self.sigma**2)*D[i, :]) else: y[i, j1, j2, :] = ( 4/(4*self.sigma**4) * (x[j1, :] - c[j1, i]) * (x[j2, :] - c[j2, i]) ) * _np.exp(-1/(2*self.sigma**2)*D[i, :]) return y
def __repr__(self): return 'Gaussian functions for box discretization with bandwidth %f.' % self.sigma
# auxiliary functions
[docs] def nchoosek(n, k): ''' Computes binomial coefficients. ''' return math.factorial(n)//math.factorial(k)//math.factorial(n-k) # integer division operator
[docs] def nextMonomialPowers(x): ''' Returns powers for the next monomial. Implementation based on John Burkardt's MONOMIAL toolbox, see http://people.sc.fsu.edu/~jburkardt/m_src/monomial/monomial.html. ''' m = len(x) j = 0 for i in range(1, m): # find the first index j > 1 s.t. x[j] > 0 if x[i] > 0: j = i break if j == 0: t = x[0] x[0] = 0 x[m - 1] = t + 1 elif j < m - 1: x[j] = x[j] - 1 t = x[0] + 1 x[0] = 0 x[j-1] = x[j-1] + t elif j == m - 1: t = x[0] x[0] = 0 x[j - 1] = t + 1 x[j] = x[j] - 1 return x
[docs] def allMonomialPowers(d, p): ''' All monomials in d dimensions of order up to p. ''' # Example: For d = 3 and p = 2, we obtain # [[ 0 1 0 0 2 1 1 0 0 0] # [ 0 0 1 0 0 1 0 2 1 0] # [ 0 0 0 1 0 0 1 0 1 2]] n = nchoosek(p + d, p) # number of monomials x = _np.zeros(d) # vector containing powers for the monomials, initially zero c = _np.zeros([d, n]) # matrix containing all powers for the monomials for i in range(1, n): c[:, i] = nextMonomialPowers(x) c = _np.flipud(c) # flip array in the up/down direction return c