Sensityivity analysis for the Ishigama function with noise using PCE
Run an EasyVVUQ campaign to analyze the sensitivity for the Ishigami function with noise
This is done with PCE providing a normal distributed noise value
[1]:
# Run an EasyVVUQ campaign to analyze the sensitivity for the Ishigami function with noise
# This is done with PCE providing a normal distributed noise value.
%matplotlib inline
import os
import easyvvuq as uq
import chaospy as cp
import pickle
import numpy as np
import matplotlib.pylab as plt
import time
import pandas as pd
[2]:
# Define the Ishigami function
def ishigamiSA(a,b):
'''Exact sensitivity indices of the Ishigami function for given a and b.
From https://openturns.github.io/openturns/master/examples/meta_modeling/chaos_ishigami.html
'''
var = 1.0/2 + a**2/8 + b*np.pi**4/5 + b**2*np.pi**8/18
S1 = (1.0/2 + b*np.pi**4/5+b**2*np.pi**8/50)/var
S2 = (a**2/8)/var
S3 = 0
S13 = b**2*np.pi**8/2*(1.0/9-1.0/25)/var
exact = {
'expectation' : a/2,
'variance' : var,
'S1' : (1.0/2 + b*np.pi**4/5+b**2*np.pi**8.0/50)/var,
'S2' : (a**2/8)/var,
'S3' : 0,
'S12' : 0,
'S23' : 0,
'S13' : S13,
'S123' : 0,
'ST1' : S1 + S13,
'ST2' : S2,
'ST3' : S3 + S13
}
return exact
Ishigami_a = 7.0
Ishigami_b = 0.1
exact = ishigamiSA(Ishigami_a, Ishigami_b)
[3]:
# define a model to run the Ishigami code directly from python, expecting a dictionary and returning a dictionary
def run_ishigami_model(input):
import Ishigami
qois = ["Ishigami"]
del input['out_file']
N = input['N']
del input['N']
return {qois[0]: Ishigami.evaluate(**input)+N}
[4]:
# Define parameter space
def define_params():
return {
"x1": {"type": "float", "min": -np.pi, "max": np.pi, "default": 0.0},
"x2": {"type": "float", "min": -np.pi, "max": np.pi, "default": 0.0},
"x3": {"type": "float", "min": -np.pi, "max": np.pi, "default": 0.0},
"a": {"type": "float", "min": Ishigami_a, "max": Ishigami_a, "default": Ishigami_a},
"b": {"type": "float", "min": Ishigami_b, "max": Ishigami_b, "default": Ishigami_b},
"N": {"type": "float", "min": -100.0, "max": 100.0, "default": 0.0},
"out_file": {"type": "string", "default": "output.csv"}
}
[5]:
# Define varying space
def define_vary():
return {
"x1": cp.Uniform(-np.pi, np.pi),
"x2": cp.Uniform(-np.pi, np.pi),
"x3": cp.Uniform(-np.pi, np.pi),
"N": cp.Normal(0, 10.0)
}
[6]:
# Set up and run a campaign
def run_campaign(pce_order=2, use_files=False):
times = np.zeros(7)
time_start = time.time()
time_start_whole = time_start
# Set up a fresh campaign called "Ishigami_pce."
my_campaign = uq.Campaign(name='Ishigami_pce.')
# Create an encoder and decoder for PCE test app
if use_files:
encoder = uq.encoders.GenericEncoder(template_fname='Ishigami.template',
delimiter='$',
target_filename='Ishigami_in.json')
decoder = uq.decoders.SimpleCSV(target_filename="output.csv",
output_columns=["Ishigami"])
execute = uq.actions.ExecuteLocal('python3 %s/Ishigami.py Ishigami_in.json' % (os.getcwd()))
actions = uq.actions.Actions(uq.actions.CreateRunDirectory('/tmp'),
uq.actions.Encode(encoder), execute, uq.actions.Decode(decoder))
else:
actions = uq.actions.Actions(uq.actions.ExecutePython(run_ishigami_model))
# Add the app (automatically set as current app)
my_campaign.add_app(name="Ishigami", params=define_params(), actions=actions)
# Create the sampler
time_end = time.time()
times[1] = time_end-time_start
print('Time for phase 1 = %.3f' % (times[1]))
time_start = time.time()
# Associate a sampler with the campaign
Sampler_PCE = uq.sampling.PCESampler(vary=define_vary(), polynomial_order=pce_order)
my_campaign.set_sampler(Sampler_PCE)
# Will draw all (of the finite set of samples)
my_campaign.draw_samples()
print('PCE order = %s' % pce_order)
print('Number of samples = %s' % my_campaign.get_active_sampler().count)
time_end = time.time()
times[2] = time_end-time_start
print('Time for phase 2 = %.3f' % (times[2]))
time_start = time.time()
# Run the cases
my_campaign.execute(sequential=True).collate(progress_bar=True)
time_end = time.time()
times[3] = time_end-time_start
print('Time for phase 3 = %.3f' % (times[3]))
time_start = time.time()
# Get the results
results_df = my_campaign.get_collation_result()
time_end = time.time()
times[4] = time_end-time_start
print('Time for phase 4 = %.3f' % (times[4]))
time_start = time.time()
# Post-processing analysis
results = my_campaign.analyse(qoi_cols=["Ishigami"])
time_end = time.time()
times[5] = time_end-time_start
print('Time for phase 5 = %.3f' % (times[5]))
time_start = time.time()
# Save the results
pickle.dump(results, open('Ishigami_results.pickle','bw'))
time_end = time.time()
times[6] = time_end-time_start
print('Time for phase 6 = %.3f' % (times[6]))
times[0] = time_end - time_start_whole
return results_df, results, times, pce_order, my_campaign.get_active_sampler().count
[7]:
# Calculate the polynomial chaos expansion for a range of orders
R = {}
for pce_order in range(1, 11):
R[pce_order] = {}
(R[pce_order]['results_df'],
R[pce_order]['results'],
R[pce_order]['times'],
R[pce_order]['order'],
R[pce_order]['number_of_samples']) = run_campaign(pce_order=pce_order, use_files=False)
Time for phase 1 = 0.034
PCE order = 1
Number of samples = 16
Time for phase 2 = 0.056
100%|█████████████████████████████████████████| 16/16 [00:00<00:00, 3128.04it/s]
Time for phase 3 = 0.030
Time for phase 4 = 0.004
Time for phase 5 = 0.041
Time for phase 6 = 0.002
Time for phase 1 = 0.007
Traceback (most recent call last):
File "/Volumes/UserData/dpc/GIT/EasyVVUQ/env_3.12/lib/python3.12/site-packages/easyvvuq/analysis/pce_analysis.py", line 495, in analyse
dY_hat = build_surrogate_der(fit, verbose=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Volumes/UserData/dpc/GIT/EasyVVUQ/env_3.12/lib/python3.12/site-packages/easyvvuq/analysis/pce_analysis.py", line 335, in build_surrogate_der
assert(n1 == n2)
^^^^^^^^
AssertionError
PCE order = 2
Number of samples = 81
Time for phase 2 = 0.088
100%|█████████████████████████████████████████| 81/81 [00:00<00:00, 6073.81it/s]
Time for phase 3 = 0.019
Time for phase 4 = 0.002
Time for phase 5 = 0.133
Time for phase 6 = 0.001
Time for phase 1 = 0.007
PCE order = 3
Number of samples = 256
Time for phase 2 = 0.167
100%|███████████████████████████████████████| 256/256 [00:00<00:00, 6319.55it/s]
Time for phase 3 = 0.049
Time for phase 4 = 0.004
Time for phase 5 = 0.265
Time for phase 6 = 0.001
Time for phase 1 = 0.006
PCE order = 4
Number of samples = 625
Time for phase 2 = 0.370
100%|███████████████████████████████████████| 625/625 [00:00<00:00, 6280.84it/s]
Time for phase 3 = 0.115
Time for phase 4 = 0.008
Time for phase 5 = 0.642
Time for phase 6 = 0.011
Time for phase 1 = 0.007
PCE order = 5
Number of samples = 1296
Time for phase 2 = 0.520
100%|█████████████████████████████████████| 1296/1296 [00:00<00:00, 6776.62it/s]
Time for phase 3 = 0.220
Time for phase 4 = 0.016
Time for phase 5 = 1.636
Time for phase 6 = 0.001
Time for phase 1 = 0.006
PCE order = 6
Number of samples = 2401
Time for phase 2 = 0.989
100%|█████████████████████████████████████| 2401/2401 [00:00<00:00, 6610.41it/s]
Time for phase 3 = 0.419
Time for phase 4 = 0.026
Time for phase 5 = 5.114
Time for phase 6 = 0.002
Time for phase 1 = 0.018
PCE order = 7
Number of samples = 4096
Time for phase 2 = 1.599
100%|█████████████████████████████████████| 4096/4096 [00:00<00:00, 6753.46it/s]
Time for phase 3 = 0.753
Time for phase 4 = 0.040
Time for phase 5 = 15.794
Time for phase 6 = 0.004
Time for phase 1 = 0.014
PCE order = 8
Number of samples = 6561
Time for phase 2 = 3.132
100%|█████████████████████████████████████| 6561/6561 [00:01<00:00, 5595.64it/s]
Time for phase 3 = 1.328
Time for phase 4 = 0.134
Time for phase 5 = 34.943
Time for phase 6 = 0.006
Time for phase 1 = 0.022
PCE order = 9
Number of samples = 10000
Time for phase 2 = 3.888
100%|███████████████████████████████████| 10000/10000 [00:01<00:00, 5809.02it/s]
Time for phase 3 = 1.941
Time for phase 4 = 0.200
Time for phase 5 = 79.951
Time for phase 6 = 0.009
Time for phase 1 = 0.015
PCE order = 10
Number of samples = 14641
Time for phase 2 = 5.835
100%|███████████████████████████████████| 14641/14641 [00:02<00:00, 5799.49it/s]
Time for phase 3 = 2.988
Time for phase 4 = 0.259
Time for phase 5 = 182.045
Time for phase 6 = 0.016
[8]:
# save the results
pickle.dump(R, open('collected_results.pickle','bw'))
[9]:
# produce a table of the time taken for various phases
# the phases are:
# 1: creation of campaign
# 2: creation of samples
# 3: running the cases
# 4: calculation of statistics including Sobols
# 5: returning of analysed results
# 6: saving campaign and pickled results
Timings = pd.DataFrame(np.array([R[r]['times'] for r in list(R.keys())]),
columns=['Total', 'Phase 1', 'Phase 2', 'Phase 3', 'Phase 4', 'Phase 5', 'Phase 6'],
index=[R[r]['order'] for r in list(R.keys())])
Timings.to_csv(open('Timings.csv', 'w'))
display(Timings)
| Total | Phase 1 | Phase 2 | Phase 3 | Phase 4 | Phase 5 | Phase 6 | |
|---|---|---|---|---|---|---|---|
| 1 | 0.166434 | 0.034119 | 0.056325 | 0.029776 | 0.003714 | 0.040676 | 0.001628 |
| 2 | 0.250982 | 0.007045 | 0.088102 | 0.019497 | 0.002389 | 0.133038 | 0.000772 |
| 3 | 0.494265 | 0.007188 | 0.167473 | 0.049170 | 0.004417 | 0.264831 | 0.001052 |
| 4 | 1.152005 | 0.006146 | 0.369653 | 0.115196 | 0.008033 | 0.642317 | 0.010516 |
| 5 | 2.399874 | 0.006595 | 0.520325 | 0.220133 | 0.015788 | 1.635806 | 0.001044 |
| 6 | 6.555950 | 0.006280 | 0.988502 | 0.419108 | 0.025505 | 5.114128 | 0.002055 |
| 7 | 18.209050 | 0.017612 | 1.598959 | 0.753175 | 0.040242 | 15.793751 | 0.004482 |
| 8 | 39.557551 | 0.014352 | 3.131594 | 1.327839 | 0.134381 | 34.942869 | 0.005903 |
| 9 | 86.011995 | 0.022007 | 3.888345 | 1.941350 | 0.199872 | 79.950801 | 0.009002 |
| 10 | 191.162857 | 0.014980 | 5.835231 | 2.987610 | 0.258682 | 182.044722 | 0.016074 |
[10]:
R[10]['results'].plot_sobols_treemap('Ishigami')
/Volumes/UserData/dpc/GIT/EasyVVUQ/env_3.12/lib/python3.12/site-packages/easyvvuq/analysis/results.py:467: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
fig.show()
[11]:
R[10]['results'].sobols_first(), R[10]['results'].sobols_total()
[11]:
({'Ishigami': {'x1': array([0.03817368]),
'x2': array([0.05380603]),
'x3': array([1.28773622e-29]),
'N': array([0.87838626])}},
{'Ishigami': {'x1': array([0.06780771]),
'x2': array([0.05380603]),
'x3': array([0.02963403]),
'N': array([0.87838626])}})
[12]:
# plot the convergence of the mean and standard deviation to the analytic result
mean_analytic = exact['expectation']
std_analytic = np.sqrt(exact['variance'])
O = [R[r]['order'] for r in list(R.keys())]
plt.figure()
plt.semilogy([o for o in O],
[np.abs(R[o]['results'].describe('Ishigami', 'mean') - mean_analytic) for o in O],
'o-', label='mean')
plt.semilogy([o for o in O],
[np.abs(R[o]['results'].describe('Ishigami', 'std') - std_analytic) for o in O],
'o-', label='std')
plt.xlabel('PCE order')
plt.ylabel('RMSerror compared to analytic')
plt.legend(loc=0)
plt.savefig('Convergence_mean_std.png')
plt.savefig('Convergence_mean_std.pdf')
[13]:
# plot the first Sobols as a function of PCE order
O = [R[r]['order'] for r in list(R.keys())]
plt.figure()
for v in list(R[O[0]]['results'].sobols_first('Ishigami').keys()):
plt.semilogy([o for o in O],
[R[o]['results'].sobols_first('Ishigami')[v] for o in O],
'o-',
label=v)
plt.xlabel('PCE order')
plt.ylabel('1st sobol')
plt.ylim(1e-2,10)
plt.legend(loc=0)
plt.savefig('Convergence_sobol_first.png')
plt.savefig('Convergence_sobol_first.pdf')
[14]:
# plot the total Sobols as a function of PCE order
O = [R[r]['order'] for r in list(R.keys())]
plt.figure()
for v in list(R[O[0]]['results'].sobols_total('Ishigami').keys()):
plt.semilogy([o for o in O],
[R[o]['results'].sobols_total('Ishigami')[v] for o in O],
'o-',
label=v)
plt.xlabel('PCE order')
plt.ylabel('total sobol')
plt.ylim(1e-2,10)
plt.legend(loc=0)
plt.savefig('Convergence_sobol_total.png')
plt.savefig('Convergence_sobol_total.pdf')
[15]:
# plot the distribution function
results_df = R[O[-1]]['results_df']
results = R[O[-1]]['results']
Ishigami_dist = results.raw_data['output_distributions']['Ishigami']
plt.figure()
plt.hist(results_df.Ishigami[0], density=True, bins=50, label='histogram of raw samples', alpha=0.25)
if hasattr(Ishigami_dist, 'samples'):
plt.hist(Ishigami_dist.samples[0], density=True, bins=50, label='histogram of kde samples', alpha=0.25)
t1 = Ishigami_dist[0]
plt.plot(np.linspace(t1.lower, t1.upper), t1.pdf(np.linspace(t1.lower, t1.upper)), label='PDF')
plt.legend(loc=0)
plt.xlabel('Ishigami')
plt.savefig('Ishigami_distribution_function.png')
[16]:
results_df.Ishigami[0]
[16]:
0 -52.525277
1 -40.006931
2 -29.296497
3 -19.405615
4 -9.933955
...
14636 9.999347
14637 19.471007
14638 29.361888
14639 40.072323
14640 52.590669
Name: 0, Length: 14641, dtype: float64
[17]:
# plot the RMS surrogate error at the PCE sample points
_o = []
_RMS = []
for r in R.values():
results_df = r['results_df']
results = r['results']
Ishigami_surrogate = np.squeeze(np.array(results.surrogate()(results_df[results.inputs])['Ishigami']))
Ishigami_samples = np.squeeze(np.array(results_df['Ishigami']))
_RMS.append((np.sqrt((((Ishigami_surrogate - Ishigami_samples))**2).mean())))
_o.append(r['order'])
plt.figure()
plt.semilogy(_o, _RMS, 'o-')
plt.xlabel('PCE order')
plt.ylabel('RMS error for the PCE surrogate')
plt.legend(loc=0)
plt.savefig('Convergence_surrogate.png')
plt.savefig('Convergence_surrogate.pdf')
/var/folders/6f/rn14629n60j16dc99dtk7bs4000ctx/T/ipykernel_79781/2720742683.py:16: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
plt.legend(loc=0)
[18]:
# prepare the test data
test_campaign = uq.Campaign(name='Ishigami.')
test_campaign.add_app(name="Ishigami", params=define_params(),
actions=uq.actions.Actions(uq.actions.ExecutePython(run_ishigami_model)))
test_campaign.set_sampler(uq.sampling.quasirandom.LHCSampler(vary=define_vary(), count=100))
test_campaign.execute(nsamples=1000, sequential=True).collate(progress_bar=True)
test_df = test_campaign.get_collation_result()
100%|█████████████████████████████████████| 1000/1000 [00:00<00:00, 6513.72it/s]
[19]:
# calculate the PCE surrogates
test_points = test_df[test_campaign.get_active_sampler().vary.get_keys()]
test_results = np.squeeze(test_df['Ishigami'].values)
test_predictions = {}
for i in list(R.keys()):
test_predictions[i] = np.squeeze(np.array(R[i]['results'].surrogate()(test_points)['Ishigami']))
[20]:
# plot the convergence of the surrogate
_o = []
_RMS = []
for r in R.values():
_RMS.append((np.sqrt((((test_predictions[r['order']] - test_results))**2).mean())))
_o.append(r['order'])
plt.figure()
plt.semilogy(_o, _RMS, 'o-')
plt.xlabel('PCE order')
plt.ylabel('RMS error for the PCE surrogate')
plt.legend(loc=0)
plt.savefig('Convergence_PCE_surrogate.png')
plt.savefig('Convergence_PCE_surrogate.pdf')
/var/folders/6f/rn14629n60j16dc99dtk7bs4000ctx/T/ipykernel_79781/3799094119.py:12: UserWarning: No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
plt.legend(loc=0)
[ ]: