Repository: damar-wicaksono/uqtestfuns (language: python)

Issue:
Add the Eight-Dimensional Function from Dette and Pepelyshev (2010)
The eight-dimensional function from Dette and Pepelyshev (2010)[^dette] is a test function used in the context of metamodeling (specifically, design of computer experiments to construct metamodels). The function reads as follows:

$$
\mathcal{M}(\boldsymbol{x}) = 4 \left( x_1 - 2 + 8 x_2 - 8 x_2^2 \right) + \left( 3 - 4 x_2 \right)^2 + 16 \left( x_3 + 1 \right)^{0.5} \left( 2 x_3 - 1 \right)^2 + \sum_{k = 4}^8 k \ln{\left( 1 + \sum_{i=3}^k x_i \right)}
$$

where $\boldsymbol{x} = \\{ x_1, \ldots, x_8 \\}$ is the vector of input variables modeled as an independent uniform random variable in $[0, 1]^8$. The function is highly curved with respect to some input variables and less so with respect to some others.

[^dette]: H. Dette and A. Pepelyshev, "Generalized latin hypercube design for computer experiments," Technometrics, vol. 52, no. 4, pp. 421-429, 2010. doi: [10.1198/TECH.2010.09157](https://doi.org/10.1198/TECH.2010.09157). See Eq. (6), Section 3.3.



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e413c11..46c99ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+- The eight-dimensional function from Dette and Pepelyshev (2010) featuring
+  curved and logarithm terms for metamodeling exercises.
 - The three-dimensional highly-curved function from Dette and Pepelyshev (2010)
   for metamodeling exercises.
 - The three-dimensional exponential function from Dette and Pepelyshev (2010)
diff --git a/docs/_toc.yml b/docs/_toc.yml
index c0a405f..f714cde 100644
--- a/docs/_toc.yml
+++ b/docs/_toc.yml
@@ -70,6 +70,8 @@ parts:
             title: Damped Oscillator
           - file: test-functions/damped-oscillator-reliability
             title: Damped Oscillator Reliability
+          - file: test-functions/dette-8d
+            title: Dette & Pepelyshev (2010) 8D
           - file: test-functions/dette-curved
             title: Dette & Pepelyshev (2010) Curved
           - file: test-functions/dette-exp
diff --git a/docs/fundamentals/metamodeling.md b/docs/fundamentals/metamodeling.md
index 0fe2b06..c69fe16 100644
--- a/docs/fundamentals/metamodeling.md
+++ b/docs/fundamentals/metamodeling.md
@@ -29,6 +29,7 @@ in the comparison of metamodeling approaches.
 |      {ref}`Currin et al. (1988) Sine <test-functions:currin-sine>`      |        1        |     `CurrinSine()`     |
 |           {ref}`Damped Cosine <test-functions:damped-cosine>`           |        1        |    `DampedCosine()`    |
 |       {ref}`Damped Oscillator <test-functions:damped-oscillator>`       |        7        |  `DampedOscillator()`  |
+|      {ref}`Dette & Pepelyshev (2010) 8D <test-functions:dette-8d>`      |        3        |      `Dette8D()`       |
 |  {ref}`Dette & Pepelyshev (2010) Curved <test-functions:dette-curved>`  |        3        |    `DetteCurved()`     |
 | {ref}`Dette & Pepelyshev (2010) Exponential <test-functions:dette-exp>` |        3        |      `DetteExp()`      |
 |                   {ref}`Flood <test-functions:flood>`                   |        8        |       `Flood()`        |


Test patch:
diff --git a/docs/test-functions/available.md b/docs/test-functions/available.md
index 6530e6b..c36d971 100644
--- a/docs/test-functions/available.md
+++ b/docs/test-functions/available.md
@@ -38,6 +38,7 @@ regardless of their typical applications.
 |                 {ref}`Damped Cosine <test-functions:damped-cosine>`                 |        1        |        `DampedCosine()`         |
 |             {ref}`Damped Oscillator <test-functions:damped-oscillator>`             |        7        |      `DampedOscillator()`       |
 | {ref}`Damped Oscillator Reliability <test-functions:damped-oscillator-reliability>` |        8        | `DampedOscillatorReliability()` |
+|            {ref}`Dette & Pepelyshev (2010) 8D <test-functions:dette-8d>`            |        3        |           `Dette8D()`           |
 |        {ref}`Dette & Pepelyshev (2010) Curved <test-functions:dette-curved>`        |        3        |         `DetteCurved()`         |
 |       {ref}`Dette & Pepelyshev (2010) Exponential <test-functions:dette-exp>`       |        3        |          `DetteExp()`           |
 |                         {ref}`Flood <test-functions:flood>`                         |        8        |            `Flood()`            |
diff --git a/docs/test-functions/dette-8d.md b/docs/test-functions/dette-8d.md
new file mode 100644
index 0000000..6cc8660
--- /dev/null
+++ b/docs/test-functions/dette-8d.md
@@ -0,0 +1,100 @@
+---
+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:dette-8d)=
+# Eight-Dimensional Function from Dette and Pepelyshev (2010)
+
+```{code-cell} ipython3
+import numpy as np
+import matplotlib.pyplot as plt
+import uqtestfuns as uqtf
+```
+
+The function is a three-dimensional, scalar-valued function.
+The function include the curved term from the {ref}`curved function <test-functions:dette-curved>`
+and an additional logarithm term. It is highly curved with respect to some
+input variables and less so with respect to the others.
+
+The function appeared in {cite}`Dette2010` as a test function for comparing
+different experimental designs in the construction of metamodels.
+
+## Test function instance
+
+To create a default instance of the test function:
+
+```{code-cell} ipython3
+my_testfun = uqtf.Dette8D()
+```
+
+Check if it has been correctly instantiated:
+
+```{code-cell} ipython3
+print(my_testfun)
+```
+
+## Description
+
+The test function is defined as[^location]:
+
+$$
+\mathcal{M}(\boldsymbol{x}) = 4 \left( x_1 - 2 + 8 x_2 - 8 x_2^2 \right) + \left( 3 - 4 x_2 \right)^2 + 16 \left( x_3 + 1\right)^{0.5} \left( 2 x_3 - 1\right)^2
++ \sum_{k = 4}^8 k \, \ln{\left( 1 + \sum_{i = 3}^k \right)},
+$$
+
+where $\boldsymbol{x} = \left( x_1, x_2, x_3 \right)$ is the three-dimensional
+vector of input variables further defined below. Notice that the term before
+the logarithm term is the terms from the {ref}`curved function <test-functions:dette-curved>`.
+
+## Probabilistic input
+
+The probabilistic input model for the test function is shown below.
+
+```{code-cell} ipython3
+:tags: [hide-input]
+
+print(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]
+
+my_testfun.prob_input.reset_rng(42)
+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}(X)$");
+plt.gcf().tight_layout(pad=3.0)
+plt.gcf().set_dpi(150);
+```
+
+## References
+
+```{bibliography}
+:style: unsrtalpha
+:filter: docname in docnames
+```
+
+[^location]: see Eq. (6), Section 3.3, p. 424, in {cite}`Dette2010`.
diff --git a/src/uqtestfuns/test_functions/__init__.py b/src/uqtestfuns/test_functions/__init__.py
index ee39ed0..b17f8c4 100644
--- a/src/uqtestfuns/test_functions/__init__.py
+++ b/src/uqtestfuns/test_functions/__init__.py
@@ -14,7 +14,7 @@
 from .currin_sine import CurrinSine
 from .damped_cosine import DampedCosine
 from .damped_oscillator import DampedOscillator, DampedOscillatorReliability
-from .dette import DetteCurved, DetteExp
+from .dette import Dette8D, DetteCurved, DetteExp
 from .flood import Flood
 from .forrester import Forrester2008
 from .four_branch import FourBranch
@@ -76,6 +76,7 @@
     "DampedCosine",
     "DampedOscillator",
     "DampedOscillatorReliability",
+    "Dette8D",
     "DetteCurved",
     "DetteExp",
     "Flood",
diff --git a/src/uqtestfuns/test_functions/dette.py b/src/uqtestfuns/test_functions/dette.py
index 699738e..91b3d82 100644
--- a/src/uqtestfuns/test_functions/dette.py
+++ b/src/uqtestfuns/test_functions/dette.py
@@ -18,7 +18,7 @@
 from uqtestfuns.core.custom_typing import ProbInputSpecs
 from uqtestfuns.core.uqtestfun_abc import UQTestFunFixDimABC
 
-__all__ = ["DetteCurved", "DetteExp"]
+__all__ = ["Dette8D", "DetteCurved", "DetteExp"]
 
 
 def evaluate_exp(xx: np.ndarray) -> np.ndarray:
@@ -63,6 +63,29 @@ def evaluate_curved(xx: np.ndarray) -> np.ndarray:
     return term_1 + term_2 + term_3
 
 
+def evaluate_8d(xx: np.ndarray) -> np.ndarray:
+    """Evaluate the 8D function from Dette and Pepelyshev (2010).
+
+    Parameters
+    ----------
+    xx : np.ndarray
+        M-Dimensional input values given by an N-by-8 array where
+        N is the number of input values.
+
+    Returns
+    -------
+    np.ndarray
+        The output of the test function evaluated on the input values.
+        The output is a 1-dimensional array of length N.
+    """
+    term_1 = evaluate_curved(xx)
+    term_2 = np.zeros(len(xx))
+    for j in range(3, xx.shape[1]):
+        term_2 += (j + 1) * np.log(1 + np.sum(xx[:, 2:j], axis=1))
+
+    return term_1 + term_2
+
+
 class DetteExp(UQTestFunFixDimABC):
     """A concrete implementation of the exponential function."""
 
@@ -119,3 +142,32 @@ class DetteCurved(UQTestFunFixDimABC):
     _available_parameters = None
 
     evaluate = staticmethod(evaluate_curved)  # type: ignore
+
+
+class Dette8D(UQTestFunFixDimABC):
+    """A concrete implementation of the 8D function."""
+
+    _tags = ["metamodeling"]
+    _description = "8D function from Dette and Pepelyshev (2010)"
+    _available_inputs: ProbInputSpecs = {
+        "Dette2010": {
+            "function_id": "DetteCurved",
+            "description": (
+                "Input specification for the 8D test function "
+                "from Dette and Pepelyshev (2010)"
+            ),
+            "marginals": [
+                {
+                    "name": f"x_{i + 1}",
+                    "distribution": "uniform",
+                    "parameters": [0, 1],
+                    "description": None,
+                }
+                for i in range(8)
+            ],
+            "copulas": None,
+        },
+    }
+    _available_parameters = None
+
+    evaluate = staticmethod(evaluate_8d)  # type: ignore