Author: Daniele E. Schiavazzi
Date: June 28th, 2017
References
Objectives of this tutorial:
In [1]:
# Imports
import sys,math
sys.path.insert(0, '..') # path to ../common.py
import numpy as np
import matplotlib.pyplot as plt
from common import *
Let's first define a simple function $$ f(x) = \frac{1.0}{\vert 0.25 - x^2\vert + 0.1} $$ defined in the $[0,1]$ interval, with discontinuous slope at 0.5. Let's plot it to see what it looks like.
In [2]:
# Model Function
def runModel(xVals):
return 1.0/(np.fabs(0.25 - np.square(xVals))+0.1)
# Plot a stochastic response with steep gradients
xExact = np.arange(500)/499.0
yExact = runModel(xExact)
# Plot Function
plt.figure(figsize=(20,5))
plt.subplot(1,1,1)
plt.plot(xExact,yExact,'r-')
plt.xlabel('x',fontsize=14)
plt.ylabel('y',fontsize=14)
plt.tick_params(axis='both', which='major', labelsize=12)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.show()
Define a flag to switch between random and equally spaced sampling points. This allows to quickly check what happens using random instead of equally spaced points and by changing their number.
In [3]:
# Flag for random points
useRandomLocs = False
numberOfSampels = 41
# Random Polynomial OLS Regression
np.random.seed(0)
if(useRandomLocs):
xVals = np.sort(np.random.rand(numberOfSampels))
else:
xVals = np.arange(numberOfSampels)/float(numberOfSampels-1)
yVals = runModel(xVals)
Let's visualize the selected sampling points
In [4]:
# Plot Function
plt.figure(figsize=(20,5))
plt.subplot(1,1,1)
plt.plot(xExact,yExact,'k--')
plt.plot(xVals,yVals,'ro',markersize=6,alpha=0.6)
plt.xlabel('x',fontsize=14)
plt.ylabel('y',fontsize=14)
plt.tick_params(axis='both', which='major', labelsize=12)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.show()
Since we would like to perform regression with Legendre polynomials first, we need to scale xVals to fit in the $[-1,1]$ interval.
In [5]:
# Normalize Inputs
xTrain = np.resize(2.0*xVals - 1.0,(len(xVals),1))
We want to perform regression using Relevance Vector Machine regression. So once again we initialize the associated python class as for the previous tutorial.
In [6]:
# Init RVM Object
rvm = tulipRVM()
In [7]:
# Make Polynomial Regressions with RVM using polynomials of order 5
measMat = buildRegressionMatrix(xTrain,5)
rvmCoeffs,rvmCoeffsCov,rvmNoise = rvm.train(measMat,yVals)
# Compute the average reconstructed Qtys
rvmY = np.dot(np.array(measMat.getMatrix()),rvmCoeffs)
# Compute the uncertainty region
rvmAux = np.dot(np.dot(np.array(measMat.getMatrix()),rvmCoeffsCov),np.array(measMat.getMatrix()).transpose())
rvmSTDQ = rvmNoise + np.sqrt(np.diag(rvmAux))
plt.figure(figsize=(20,5))
plt.subplot(1,1,1)
plt.plot(xExact,yExact,'k-',alpha=0.8,label='Exact')
plt.plot(xVals,rvmY,'r-',alpha=0.6,lw=1.0,label='Legendre RVM - Degree 5')
plt.fill_between(xVals,rvmY+rvmSTDQ, rvmY-rvmSTDQ,facecolor='gray',interpolate=True,alpha=0.6)
plt.xlabel('x',fontsize=14)
plt.ylabel('y',fontsize=14)
plt.tick_params(axis='both', which='major', labelsize=12)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.legend(fontsize=12)
plt.show()
Let's increase the order of the polynomials regression to 30.
In [8]:
# Make Polynomial Regressions with RVM using polynomials of order 30
measMat = buildRegressionMatrix(xTrain,30)
rvmCoeffs,rvmCoeffsCov,rvmNoise = rvm.train(measMat,yVals)
# Compute the average reconstructed Qtys
rvmY = np.dot(np.array(measMat.getMatrix()),rvmCoeffs)
# Compute the uncertainty region
rvmAux = np.dot(np.dot(np.array(measMat.getMatrix()),rvmCoeffsCov),np.array(measMat.getMatrix()).transpose())
rvmSTDQ = rvmNoise + np.sqrt(np.diag(rvmAux))
plt.figure(figsize=(20,5))
plt.subplot(1,1,1)
plt.plot(xExact,yExact,'k-',alpha=0.8,label='Exact')
plt.plot(xVals,rvmY,'r-',alpha=0.6,lw=1.0,label='Legendre RVM - Degree 30')
plt.fill_between(xVals,rvmY+rvmSTDQ, rvmY-rvmSTDQ,facecolor='gray',interpolate=True,alpha=0.6)
plt.xlabel('x',fontsize=14)
plt.ylabel('y',fontsize=14)
plt.tick_params(axis='both', which='major', labelsize=12)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.legend(fontsize=12)
plt.show()
A multiresolution approximation of $\mathbf{L}^2([0,1])$ is expressed by means of a nested sequence of closed subspaces $\mathbf{V}_{0}\subset\mathbf{V}_{1}\subset\dots\subset\mathbf{V}_{j}\subset\dots\subset\mathbf{L}^2([0,1])$, where each subspace $\mathbf{V}_j$ corresponding to resolution $j$ is given by $\mathbf{V}_j = \mathrm{span}\{\phi_{j,k}(y): k = 0,\dots,2^{j}-1\}$.
Here, the functions
$$ \phi_{j,k}(y)=2^{j/2}\phi(2^{j}y-k) $$are dilations and translations of a scaling function $\phi(y):[0,1]\rightarrow\mathbb{R}$, and $\phi(y)$ is such that the closure of the union of $\mathbf{V}_{j}$, $\overline{\bigcup_{k=0}^{\infty}\mathbf V_k}$, is dense in $\mathbf{L}^2([0,1])$.
Let the wavelet subspace $\mathbf W_j$ denote the orthogonal complement of $\mathbf{V}_{j}$ in $\mathbf{V}_{j+1}$, that is $\mathbf V_{j+1} = \mathbf{V}_{j} \oplus \mathbf W_j$ and $\mathbf{V}_{j}\bot \mathbf W_j$. It can be shown that $\mathbf W_j = \mathrm{span}\{\varphi_{j,k}(y):k = 0,\dots,2^{j}-1\}$ where $\varphi_{j,k}(y)$ is generated from dilation and translation of a so-called mother wavelet function $\varphi(y):[0,1]\rightarrow\mathbb{R}$,
$$ \varphi_{j,k}(y) = 2^{j/2}\varphi(2^{j}y-k). $$By the construction of wavelet subspaces $\mathbf W_j$, it is straightforward to see that $\mathbf V_{j} = \mathbf V_0 \oplus\left(\bigoplus_{k=0}^{j-1}\mathbf W_k\right)$, and consequently $\mathbf V_0 \oplus\left(\bigoplus_{k=0}^{\infty}\mathbf W_k\right)=\mathbf{L}^2([0,1])$. Therefore, any function $u(y)\in \mathbf{L}^2([0,1])$ admits an orthogonal decomposition of the form
$$ u(y) = \tilde{\alpha}_{0,0}\phi_{0,0}(y) + \sum_{j=0}^{\infty}\sum_{k=0}^{2^{j}-1} \alpha_{j,k} \varphi_{j,k}(y), $$where $\tilde{\alpha}_{0,0} = \langle u,\phi_{0,0} \rangle_{\mathbf{L}^2([0,1])}$, $\alpha_{j,k}= \langle u,\varphi_{j,k} \rangle_{\mathbf{L}^2([0,1])}$, and the inner-product $\langle u,v \rangle_{\mathbf{L}^2([0,1])} = \int_{0}^{1}u(y)\,v(y)\,dy$.
For the interest of notation, we rewrite the above expansion of $u(y)$ as
$$ u(y) = \sum_{i=1}^{\infty}\alpha_{i} \psi_{i}(y), $$in which we establish a one-to-one correspondence between elements of the basis sets $\{\phi_{0,0},\varphi_{j,k}:k = 0,\dots,2^{j}-1, j = 0,\dots,\infty \}$ and $\{\psi_i:i = 1,\dots,\infty\}$.
In the present study, we adopt the multiresolution basis of Alpert, where multiple scaling functions $\{\phi_i(y): i = 0,\dots,m-1\}$ are used to construct the polynomial space $\mathbf{V}^{m}_{0}$.
Specifically, we choose $\phi_i(y)$ as the Legendre polynomial of degree $i$ defined on $[0,1]$. An orthonormal piecewise polynomial basis $\{\varphi_i(y)\in\mathbf{L}^2([0,1]): i = 0,\dots,m-1\}$ of $\mathbf{W}^{m}_{0}$ is also established such that
$$ \int_{0}^{1}\varphi_i(y)y^l dy = 0,\qquad i,l=0,\cdots,m-1. $$Let $\mathbf{U}_z = \{u(y)\in\mathbf{L}^2([-1,1]): \int^{1}_{-1}u(y)\,y^{z}\,dy=0\}$ represent the subspace of functions supported on the interval $[-1,1]$ with $z$ vanishing moments.
Given a maximum polynomial degree $m$, we construct $\{f_i:\,i=0,\dots,m-1\}$ in steps:
Start from monomials of increasing degree, discontinuous and opposite in sign on the sub-intervals $[-1,0)$ and $[0,1]$.
Require that $f_i\in\mathbf{U}_{j}$, $j=0,\dots,i+m-1$.
Enforce orthogonality, i.e., $\langle f_i,f_j\rangle_{\mathbf{L}^2[(0,1)]}=\delta_{ij}$, $i,j = 0,\dots,m-1$, where $\delta_{ij}$ is the Kronecker delta.
Finally compute $\{\varphi_i(y): i = 0,\dots,m-1\}$ by normalization $\{f_i:\,i=0,\dots,m-1\}$ on the interval $[0,1]$.
The multiwavelet basis functions $\varphi_{j,i,k}(y)$, hence the multiwavelet subspaces $\mathbf{W}^{m}_j$, are generated by dilations and translations of $\{\varphi_i(y): i = 0,\dots,m-1\}$, that is
$$ \varphi_{j,i,k}(y) = 2^{j/2}\varphi_i(2^j y-k),\quad i=0,\cdots,m-1,\; k=0,\cdots,2^j-1. $$With certain additional constraints on $\varphi_i(y)$, the resulting basis is unique (up to the sign) and provides a generalization of the Legendre and Haar basis. In particular, the Legendre polynomials may be obtained by limiting the resolution to $j=0$, while Haar wavelets are obtained by setting $m=1$.
In [9]:
# Make Polynomial Regressions with RVM
maxOrder = 3
maxDetailLevel = 2
measMat = buildMultiresolutionMatrix(np.resize(xVals,(len(xVals),1)),maxOrder,maxDetailLevel)
rvmCoeffs,rvmCoeffsCov,rvmNoise = rvm.train(measMat,yVals)
# Compute the average reconstructed Qtys
rvmY = np.dot(np.array(measMat.getMatrix()),rvmCoeffs)
# Compute the uncertainty region
rvmAux = np.dot(np.dot(np.array(measMat.getMatrix()),rvmCoeffsCov),np.array(measMat.getMatrix()).transpose())
rvmSTDQ = rvmNoise + np.sqrt(np.diag(rvmAux))
plt.figure(figsize=(20,5))
plt.subplot(1,1,1)
plt.plot(xExact,yExact,'k-',alpha=0.8,label='Exact')
plt.plot(xVals,rvmY,'r-',alpha=0.6,lw=1.0,label='Multiresolution RVM')
plt.fill_between(xVals,rvmY+rvmSTDQ, rvmY-rvmSTDQ,facecolor='gray',interpolate=True,alpha=0.6)
plt.xlabel('x',fontsize=14)
plt.ylabel('y',fontsize=14)
plt.tick_params(axis='both', which='major', labelsize=12)
plt.tick_params(axis='both', which='minor', labelsize=12)
plt.legend(fontsize=12)
plt.show()
You can play with the maxOrder, maxDetailLevel, use random or equally spaced sampling to see how they affect the generated approximant.