Skip to content

Commit

Permalink
Merge pull request #282 from damar-wicaksono/dev-261
Browse files Browse the repository at this point in the history
Add an implementation of the 2D function from Webster et al. (1996).
  • Loading branch information
damar-wicaksono authored Jul 7, 2023
2 parents 6eb40b4 + cc919f5 commit 6fb4b8b
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 27 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New instance method for `UnivDist` and `ProbInput` classes
called `reset_rng()`. When called (optionally with a seed number),
a new instance of NumPy default RNG will be created and attached to the instance.
- The two-dimensional polynomial function with random inputs
from Webster et al. (1996) for metamodeling exercises.

### Changed

Expand Down
2 changes: 2 additions & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ parts:
title: Speed Reducer Shaft
- file: test-functions/sulfur
title: Sulfur
- file: test-functions/webster-2d
title: Webster et al. (1996) 2D
- file: test-functions/welch1992
title: Welch et al. (1992)
- file: test-functions/wing-weight
Expand Down
53 changes: 27 additions & 26 deletions docs/fundamentals/metamodeling.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,33 @@ kernelspec:
The table below listed the available test functions typically used
in the comparison of metamodeling approaches.

| Name | Spatial Dimension | Constructor |
|:--------------------------------------------------------------:|:-----------------:|:--------------------:|
| {ref}`Ackley <test-functions:ackley>` | M | `Ackley()` |
| {ref}`Borehole <test-functions:borehole>` | 8 | `Borehole()` |
| {ref}`Damped Cosine <test-functions:damped-cosine>` | 1 | `DampedCosine()` |
| {ref}`Damped Oscillator <test-functions:damped-oscillator>` | 7 | `DampedOscillator()` |
| {ref}`Flood <test-functions:flood>` | 8 | `Flood()` |
| {ref}`Forrester et al. (2008) <test-functions:forrester>` | 1 | `Forrester2008()` |
| {ref}`(1st) Franke <test-functions:franke-1>` | 2 | `Franke1()` |
| {ref}`(2nd) Franke <test-functions:franke-2>` | 2 | `Franke2()` |
| {ref}`(3rd) Franke <test-functions:franke-3>` | 2 | `Franke3()` |
| {ref}`(4th) Franke <test-functions:franke-4>` | 2 | `Franke4()` |
| {ref}`(5th) Franke <test-functions:franke-5>` | 2 | `Franke5()` |
| {ref}`(6th) Franke <test-functions:franke-6>` | 2 | `Franke6()` |
| {ref}`Gramacy (2007) 1D Sine <test-functions:gramacy-1d-sine>` | 1 | `Gramacy1DSine()` |
| {ref}`McLain S1 <test-functions:mclain-s1>` | 2 | `McLainS1()` |
| {ref}`McLain S2 <test-functions:mclain-s2>` | 2 | `McLainS2()` |
| {ref}`McLain S3 <test-functions:mclain-s3>` | 2 | `McLainS3()` |
| {ref}`McLain S4 <test-functions:mclain-s4>` | 2 | `McLainS4()` |
| {ref}`McLain S5 <test-functions:mclain-s5>` | 2 | `McLainS5()` |
| {ref}`Oakley and O'Hagan (2002) 1D <test-functions:oakley-1d>` | 1 | `Oakley1D()` |
| {ref}`OTL Circuit <test-functions:otl-circuit>` | 6 / 20 | `OTLCircuit()` |
| {ref}`Piston Simulation <test-functions:piston>` | 7 / 20 | `Piston()` |
| {ref}`Sulfur <test-functions:sulfur>` | 9 | `Sulfur()` |
| {ref}`Welch1992 <test-functions:welch1992>` | 20 | `Welch1992()` |
| {ref}`Wing Weight <test-functions:wing-weight>` | 10 | `WingWeight()` |
| Name | Spatial Dimension | Constructor |
|:--------------------------------------------------------------:|:-----------------:|:---------------------------:|
| {ref}`Ackley <test-functions:ackley>` | M | `Ackley()` |
| {ref}`Borehole <test-functions:borehole>` | 8 | `Borehole()` |
| {ref}`Damped Cosine <test-functions:damped-cosine>` | 1 | `DampedCosine()` |
| {ref}`Damped Oscillator <test-functions:damped-oscillator>` | 7 | `DampedOscillator()` |
| {ref}`Flood <test-functions:flood>` | 8 | `Flood()` |
| {ref}`Forrester et al. (2008) <test-functions:forrester>` | 1 | `Forrester2008()` |
| {ref}`(1st) Franke <test-functions:franke-1>` | 2 | `Franke1()` |
| {ref}`(2nd) Franke <test-functions:franke-2>` | 2 | `Franke2()` |
| {ref}`(3rd) Franke <test-functions:franke-3>` | 2 | `Franke3()` |
| {ref}`(4th) Franke <test-functions:franke-4>` | 2 | `Franke4()` |
| {ref}`(5th) Franke <test-functions:franke-5>` | 2 | `Franke5()` |
| {ref}`(6th) Franke <test-functions:franke-6>` | 2 | `Franke6()` |
| {ref}`Gramacy (2007) 1D Sine <test-functions:gramacy-1d-sine>` | 1 | `Gramacy1DSine()` |
| {ref}`McLain S1 <test-functions:mclain-s1>` | 2 | `McLainS1()` |
| {ref}`McLain S2 <test-functions:mclain-s2>` | 2 | `McLainS2()` |
| {ref}`McLain S3 <test-functions:mclain-s3>` | 2 | `McLainS3()` |
| {ref}`McLain S4 <test-functions:mclain-s4>` | 2 | `McLainS4()` |
| {ref}`McLain S5 <test-functions:mclain-s5>` | 2 | `McLainS5()` |
| {ref}`Oakley and O'Hagan (2002) 1D <test-functions:oakley-1d>` | 1 | `Oakley1D()` |
| {ref}`OTL Circuit <test-functions:otl-circuit>` | 6 / 20 | `OTLCircuit()` |
| {ref}`Piston Simulation <test-functions:piston>` | 7 / 20 | `Piston()` |
| {ref}`Webster et al. (1996) 2D <test-functions:webster-2d>` | 2 | `Webster2D()` |
| {ref}`Sulfur <test-functions:sulfur>` | 9 | `Sulfur()` |
| {ref}`Welch1992 <test-functions:welch1992>` | 20 | `Welch1992()` |
| {ref}`Wing Weight <test-functions:wing-weight>` | 10 | `WingWeight()` |

In a Python terminal, you can list all the available functions relevant
for metamodeling applications using ``list_functions()`` and filter the results
Expand Down
10 changes: 10 additions & 0 deletions docs/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -674,4 +674,14 @@ @Book{Santner2018
doi = {10.1007/978-1-4939-8847-1},
}

@TechReport{Webster1996,
author = {Webster, M. D. and Tatang, M. A. and McRae, G. J.},
institution = {Massachusetts Institute of Technology},
title = {Application of the probabilistic collocation method for an uncertainty analysis of a simple ocean model},
year = {1996},
address = {Cambridge, MA},
number = {Joint Program Report Series No. 4},
url = {http://globalchange.mit.edu/publication/15670},
}

@Comment{jabref-meta: databaseType:bibtex;}
1 change: 1 addition & 0 deletions docs/test-functions/available.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ available in the current UQTestFuns, regardless of their typical applications.
| {ref}`Sobol'-G <test-functions:sobol-g>` | M | `SobolG()` |
| {ref}`Speed Reducer Shaft <test-functions:speed-reducer-shaft>` | 5 | `SpeedReducerShaft()` |
| {ref}`Sulfur <test-functions:sulfur>` | 9 | `Sulfur()` |
| {ref}`Webster et al. (1996) 2D <test-functions:webster-2d>` | 2 | `Webster2D()` |
| {ref}`Welch1992 <test-functions:welch1992>` | 20 | `Welch1992()` |
| {ref}`Wing Weight <test-functions:wing-weight>` | 10 | `WingWeight()` |

Expand Down
152 changes: 152 additions & 0 deletions docs/test-functions/webster-2d.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
jupytext:
formats: ipynb,md:myst
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.14.1
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
---

(test-functions:webster-2d)=
# Two-Dimensional Function from Webster et al. (1996)

```{code-cell} ipython3
import numpy as np
import matplotlib.pyplot as plt
import uqtestfuns as uqtf
```

The 2D function introduced in {cite}`Webster1996` is a polynomial function.
It was used to illustrate the construction of a polynomial chaos expansion
metamodel (via stochastic collocation)
having uncertain (random) input variables.

```{code-cell} ipython3
:tags: [remove-input]
from mpl_toolkits.axes_grid1 import make_axes_locatable
my_fun = uqtf.Webster2D()
# --- Create 2D data
lb_1 = my_fun.prob_input.marginals[0].lower
ub_1 = my_fun.prob_input.marginals[0].upper
lb_2 = (
my_fun.prob_input.marginals[1].parameters[0]
- 3 * my_fun.prob_input.marginals[1].parameters[1]
)
ub_2 = (
my_fun.prob_input.marginals[1].parameters[0]
+ 3 * my_fun.prob_input.marginals[1].parameters[1]
)
xx_1 = np.linspace(lb_1, ub_1, 1000)[:, np.newaxis]
xx_2 = np.linspace(lb_2, ub_2, 1000)[:, np.newaxis]
mesh_2d = np.meshgrid(xx_1, xx_2)
xx_2d = np.array(mesh_2d).T.reshape(-1, 2)
yy_2d = my_fun(xx_2d)
# --- Create two-dimensional plots
fig = plt.figure(figsize=(10, 5))
# Surface
axs_1 = plt.subplot(121, projection='3d')
axs_1.plot_surface(
mesh_2d[0],
mesh_2d[1],
yy_2d.reshape(1000,1000).T,
linewidth=0,
cmap="plasma",
antialiased=False,
alpha=0.5
)
axs_1.set_xlabel("$x_1$", fontsize=14)
axs_1.set_ylabel("$x_2$", fontsize=14)
axs_1.set_zlabel("$\mathcal{M}(x_1, x_2)$", fontsize=14)
axs_1.set_title("Surface plot of Webster 2D", fontsize=14)
# Contour
axs_2 = plt.subplot(122)
cf = axs_2.contourf(
mesh_2d[0], mesh_2d[1], yy_2d.reshape(1000, 1000).T, cmap="plasma"
)
axs_2.set_xlim([lb_1, ub_1])
axs_2.set_ylim([lb_2, ub_2])
axs_2.set_xlabel("$x_1$", fontsize=14)
axs_2.set_ylabel("$x_2$", fontsize=14)
axs_2.set_title("Contour plot of Webster 2D", fontsize=14)
divider = make_axes_locatable(axs_2)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(cf, cax=cax, orientation='vertical')
fig.tight_layout(pad=4.0)
plt.gcf().set_dpi(75);
```

## Test function instance

To create a default instance of the test function:

```{code-cell} ipython3
my_testfun = uqtf.Webster2D()
```

Check if it has been correctly instantiated:

```{code-cell} ipython3
print(my_testfun)
```

## Description

The Webster 2D function is defined as follows[^location]:

$$
\mathcal{M}(\boldsymbol{x}) = A^2 + B^3,
$$
where $\boldsymbol{x} = \{ A, B \}$
is the two-dimensional vector of input variables further defined below.

## Probabilistic input

Based on {cite}`Webster1996`, the probabilistic input model
for the function consists of two independent random variables as shown below.

```{code-cell} ipython3
my_testfun.prob_input
```

## Reference results

This section provides several reference results of typical UQ analyses involving
the test function.

### Sample histogram

Shown below is the histogram of the output based on $100'000$ random points:

```{code-cell} ipython3
:tags: [hide-input]
xx_test = my_testfun.prob_input.get_sample(100000)
yy_test = my_testfun(xx_test)
plt.hist(yy_test, bins="auto", color="#8da0cb");
plt.grid();
plt.ylabel("Counts [-]");
plt.xlabel("$\mathcal{M}(\mathbf{X})$");
plt.gcf().set_dpi(150);
```

## References

```{bibliography}
:filter: docname in docnames
```

[^location]: see Eq. (8), Section 2.2, p. 4 in {cite}`Webster1996`.
2 changes: 2 additions & 0 deletions src/uqtestfuns/test_functions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .sobol_g import SobolG
from .speed_reducer_shaft import SpeedReducerShaft
from .sulfur import Sulfur
from .webster import Webster2D
from .welch1992 import Welch1992
from .wing_weight import WingWeight

Expand Down Expand Up @@ -71,6 +72,7 @@
"SobolG",
"SpeedReducerShaft",
"Sulfur",
"Webster2D",
"Welch1992",
"WingWeight",
]
2 changes: 2 additions & 0 deletions src/uqtestfuns/test_functions/rs_circular_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def evaluate(xx: np.ndarray, parameter: float) -> np.ndarray:
xx : np.ndarray
A two-dimensional input values given by an N-by-2 array
where N is the number of input values.
parameter : float
The parameter of the function (i.e., the diameter of the bar in [mm]).
Returns
-------
Expand Down
79 changes: 79 additions & 0 deletions src/uqtestfuns/test_functions/webster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Module with an implementation of the 2D function from Webster et al. (1996).
The two-dimensional function is polynomial with random input variables.
It was introduced in [1] and used to illustrate the construction of polynomial
chaos expansion metamodel.
References
----------
1. M. Webster, M. A. Tatang, and G. J. McRae, “Application of the probabilistic
collocation method for an uncertainty analysis of a simple ocean model,”
Massachusetts Institute of Technology, Cambridge, MA,
Joint Program Report Series 4, 1996.
[Online]. Available: http://globalchange.mit.edu/publication/15670
"""
import numpy as np

from ..core.prob_input.input_spec import UnivDistSpec, ProbInputSpecFixDim
from ..core.uqtestfun_abc import UQTestFunABC

__all__ = ["Webster2D"]


AVAILABLE_INPUT_SPECS = {
"Webster1996": ProbInputSpecFixDim(
name="Webster1996",
description=(
"Input specification for the 2D function "
"from Webster et al. (1996)"
),
marginals=[
UnivDistSpec(
name="A",
distribution="uniform",
parameters=[1.0, 10.0],
description="None",
),
UnivDistSpec(
name="B",
distribution="normal",
parameters=[2.0, 1.0],
description="None",
),
],
copulas=None,
),
}


def evaluate(xx: np.ndarray):
"""Evaluate the 2D Webster function on a set of input values.
Parameters
----------
xx : np.ndarray
Two-Dimensional input values given by N-by-2 arrays where
N is the number of input values.
Returns
-------
np.ndarray
The output of the 2D Webster function evaluated on the input values.
The output is a 1-dimensional array of length N.
"""
yy = xx[:, 0] ** 2 + xx[:, 1] ** 3

return yy


class Webster2D(UQTestFunABC):
"""A concrete implementation of the function from Webster et al. (1996)."""

_tags = ["metamodeling"]
_description = "2D polynomial function from Webster et al. (1996)."
_available_inputs = AVAILABLE_INPUT_SPECS
_available_parameters = None
_default_spatial_dimension = 2

eval_ = staticmethod(evaluate)
2 changes: 1 addition & 1 deletion tests/builtin_test_functions/test_sobol_g.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_compute_mean(spatial_dimension, params_selection):
assert my_fun.prob_input is not None

# Compute mean via Monte Carlo
xx = my_fun.prob_input.get_sample(500000)
xx = my_fun.prob_input.get_sample(1000000)
yy = my_fun(xx)

mean_mc = np.mean(yy)
Expand Down

0 comments on commit 6fb4b8b

Please sign in to comment.