Skip to content

Commit d0f9a48

Browse files
authored
Merge pull request #78 from jab0707/jake_demos
adding simple demo
2 parents 8d64228 + d763f0e commit d0f9a48

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,4 @@ docs/_build/
141141
.DS_Store
142142

143143
data_set/*
144+
._.DS_Store

demos/basic_uq_example.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#exec(open('basic_uq_example.py').read())
2+
import sys
3+
from itertools import chain, combinations
4+
5+
import numpy as np
6+
from matplotlib import pyplot as plt
7+
8+
from UncertainSCI.distributions import BetaDistribution
9+
from UncertainSCI.model_examples import laplace_grid_x, laplace_ode, KLE_exponential_covariance_1d
10+
from UncertainSCI.indexing import TotalDegreeSet
11+
from UncertainSCI.pce import PolynomialChaosExpansion
12+
13+
'''
14+
#########################
15+
Basic UQ example
16+
The purpose of this file is to demonstrate the UncerstainSCI UQ tool on a simple function with predictable output.
17+
This example uses a simple sinusoidal function with 4 parameters: amplitude, offset, phase, and frequency.
18+
Uncertainty quantification is performed on each of these 4 parameters and their interactions, and the resulting parameter
19+
sensitivities, model standard deviation and mean outputs are calculated and displayed.
20+
21+
#########################
22+
'''
23+
# # We start by defining our model:
24+
#
25+
# f(x) = p0 sin(p1 * x + p2) +p3
26+
#
27+
#
28+
def modelFunction(p,x,paramBounds):
29+
'''
30+
31+
:param p: Parameters of our model:
32+
p[0]: amplitude
33+
p[1]: frequency
34+
p[2]: phase
35+
p[3]: offset
36+
:param x:
37+
:param paramBounds:
38+
:return:
39+
'''
40+
#first we scale our input parameters according to the bounds we defined for them
41+
#UncertainSCI treats all parameters as 0 to 1, so we scale to our desired range
42+
p0Range = paramBounds[1] - paramBounds[0]
43+
p1Range = paramBounds[3] - paramBounds[2]
44+
p2Range = paramBounds[5] - paramBounds[4]
45+
p3Range = paramBounds[7] - paramBounds[6]
46+
47+
p0_offset = paramBounds[0]
48+
p1_offset = paramBounds[2]
49+
p2_offset = paramBounds[4]
50+
p3_offset = paramBounds[6]
51+
52+
p0 = float(p0Range * p[0] + p0_offset)
53+
p1 = float(p1Range * p[1] + p1_offset)
54+
p2 = float(p2Range * p[2] + p2_offset)
55+
p3 = float(p3Range * p[3] + p3_offset)
56+
#we then calculate the output value(S) from our model function and return this output
57+
fx = p0 * np.sin(p1 * x + p2) + p3
58+
return fx
59+
60+
#This model has four input parameters so we set the dimensionality to 4
61+
dimension = 4
62+
63+
# # Next we specify the distribution we expect for our input parameters.
64+
# In this case we assume a uniform distribution from 0 to 1 for each parameter
65+
# (alpha=beta=1 ---> uniform)
66+
alpha = 1.
67+
beta = 1.
68+
dist = BetaDistribution(alpha=alpha, beta=beta, dim=dimension)
69+
70+
# # Expressivity setup
71+
# Expressivity determines what order of polynomial to use when emulating
72+
# our model function. This is a tuneable hyper parameter, however UncertainSCI
73+
# also has the cabability to auto determine this value.
74+
order = 5
75+
index_set = TotalDegreeSet(dim=dimension, order=order)
76+
77+
78+
# # Next we want to define the specific values for this instance of our model
79+
# this step will depend ont he specific model and what it takes as inputs. In our case
80+
# our model needs to know wthat the x value(s) is/are and what are the bounds on our
81+
# parameters
82+
# first we define our input range x
83+
xVals = np.linspace(-1*np.pi,1*np.pi,100)
84+
#define our parameter bounds
85+
#p0: amplitude
86+
#p1: frequency
87+
#p2: phase
88+
#p3: offset
89+
bounds = [0.5, 1,\
90+
1, 1,\
91+
1, 1,\
92+
-1, 1]
93+
94+
# next we create a model function that takes parameter values and returns the model output
95+
# a lambda function is used to pass in the parameter bounds, however if parameter
96+
# bounds are fixed then they can be hard coded into the model function and a
97+
# function handle for the model can be used instead of a lambda function
98+
model = lambda p: modelFunction(p,x = xVals,paramBounds=bounds)
99+
100+
# # Building the PCE
101+
# First provide the indicies and distribution
102+
pce = PolynomialChaosExpansion(index_set, dist)
103+
# Next generate the samples that you want to query
104+
pce.generate_samples()
105+
print('This will query the model {0:d} times'.format(pce.samples.shape[0]))
106+
# Finally compute the polnomial chaos expansion of our model function
107+
# this next step can be run in a number of ways, the simplest is done here
108+
# by providing the pce with our model function. the pce will then query the model
109+
# function at the samples and generate the appropriate statsitcs
110+
pce.build(model)
111+
112+
# The parameter samples and model evaluations are accessible:
113+
114+
parameter_samples = pce.samples
115+
model_evaluations = pce.model_output
116+
# this means you could run the samples through a model function offline, and return
117+
# the outputs to the pce seperatly. See the example file ??.py for more information
118+
119+
# # Postprocess PCE: mean, stdev, sensitivities, quantiles
120+
mean = pce.mean()
121+
stdev = pce.stdev()
122+
123+
# Power set of [0, 1, ..., dimension-1]
124+
variable_interactions = list(chain.from_iterable(combinations(range(dimension), r) for r in range(1, dimension+1)))
125+
126+
127+
# "Global sensitivity" is a partitive relative sensitivity measure per set of parameters.
128+
global_sensitivity = pce.global_sensitivity(variable_interactions)
129+
130+
131+
# # Visualization
132+
V = 100 # Generate Monte Carlo samples for comparison
133+
p_mc = dist.MC_samples(V)
134+
output = np.zeros([V, len(xVals)])
135+
136+
for j in range(V):
137+
output[j, :] = model(p_mc[j, :])
138+
139+
# mean +/- stdev plot
140+
plt.plot(xVals, output.T, 'k', alpha=0.8, linewidth=0.2)
141+
plt.plot(xVals, mean, 'b', label='PCE mean')
142+
plt.fill_between(xVals, mean-stdev, mean+stdev, interpolate=True, facecolor='red', alpha=0.5, label='PCE 1 stdev range')
143+
144+
plt.xlabel('x')
145+
plt.title('Mean $\\pm$ standard deviation')
146+
147+
plt.legend(loc='lower right')
148+
plt.show()
149+
150+
151+
# Sensitivity pie chart, averaged over all model degrees of freedom
152+
average_global_SI = np.sum(global_sensitivity, axis=1)
153+
154+
labels = ['[' + ' '.join(str(elem) for elem in [i+1 for i in item]) + ']' for item in variable_interactions]
155+
_, ax = plt.subplots()
156+
ax.pie(average_global_SI*100, labels=labels, autopct='%1.1f%%',
157+
startangle=90)
158+
ax.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
159+
plt.title('Sensitivity due to variable interactions')
160+
plt.show()
161+

0 commit comments

Comments
 (0)