Source code for example_simulator_functions.park91a

#
# SPDX-License-Identifier: LGPL-3.0-or-later
# Copyright (c) 2024-2025, QUEENS contributors.
#
# This file is part of QUEENS.
#
# QUEENS is free software: you can redistribute it and/or modify it under the terms of the GNU
# Lesser General Public License as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version. QUEENS is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You
# should have received a copy of the GNU Lesser General Public License along with QUEENS. If not,
# see <https://www.gnu.org/licenses/>.
#
"""Park91a function."""

import numpy as np

# x3 and x4 grid
X = np.linspace(0, 1, 4)
X3, X4 = np.meshgrid(X, X)
X3 = X3.flatten()
X4 = X4.flatten()


[docs] def unit_bounding(*args): """Bounding function. This should be avoided... """ args = list(args) for i, arg in enumerate(args): if arg <= 0: args[i] = 0.01 elif arg >= 1: args[i] = 0.99 return tuple(args)
[docs] def x3_x4_grid_eval(park_function, x1, x2, gradient_bool=False): """Evaluate a park function on a *x3* and *x4* grid. Args: park_function (func): Function to be evaluated x1 (int): Input parameter 1 x2 (int): Input parameter 2 gradient_bool (bool, optional): Switch to return gradient value w.r.t. *x1* and *x2* Returns: np.ndarray: *park_function* evaluated on the grid """ # Loop over the coordinates y_vec = [] dy_dx1_vec = [] dy_dx2_vec = [] if gradient_bool: for x3, x4 in zip(X3, X4): # Bound the arguments args = unit_bounding(x1, x2, x3, x4) y, gradient = park_function(*args, gradient_bool=True) dy_dx1_vec.append(gradient[0]) dy_dx2_vec.append(gradient[1]) y_vec.append(y) return np.array(y_vec), (np.array(dy_dx1_vec), np.array(dy_dx2_vec)) for x3, x4 in zip(X3, X4): # Bound the arguments args = unit_bounding(x1, x2, x3, x4) y_vec.append(park_function(*args)) return np.array(y_vec)
[docs] def park91a_lofi(x1, x2, x3, x4, gradient_bool=False): r"""Low-fidelity Park91a function. Simple four-dimensional benchmark function as proposed in [1] to mimic a computer model. For the purpose of multi-fidelity simulation, [3] defined a corresponding lower fidelity function, which is defined as: :math:`f_{lofi}({\bf x})= \left[1 + \frac{\sin(x_1)}{10} \right] f_{hifi}({\bf x}) - 2x_1 + x_2^2 + x_3^2 + 0.5` The high-fidelity version is defined as is implemented in *park91a_hifi*. Args: x1 (float): Input parameter 1 [0,1) x2 (float): Input parameter 2 [0,1) x3 (float): Input parameter 3 [0,1) x4 (float): Input parameter 4 [0,1) gradient_bool (bool, optional): Switch to return gradient value w.r.t. x1 and x2 Returns: y (float): Value of function at parameters dy_dx1 (float): Gradient of the function w.r.t. x1 dy_dx2 (float): Gradient of the function w.r.t. x2 References: [1] Park, J.-S.(1991). Tuning complex computer codes to data and optimal designs, Ph.D Thesis [2] Cox, D. D., Park, J.-S., & Singer, C. E. (2001). A statistical method for tuning a computer code to a data base. Computational Statistics & Data Analysis, 37(1), 77-92. http://doi.org/10.1016/S0167-9473(00)00057-8 [3] Xiong, S., Qian, P., & Wu, C. (2013). Sequential design and analysis of high-accuracy and low-accuracy computer codes. Technometrics. http://doi.org/10.1080/00401706.2012.723572 """ if gradient_bool: yh, (dyh_dx1, dyh_dx2) = park91a_hifi(x1, x2, x3, x4, gradient_bool=True) term1 = (1 + np.sin(x1) / 10) * yh term2 = -2 * x1 + x2**2 + x3**2 y = term1 + term2 + 0.5 dy_dx1_term1 = dyh_dx1 * (1 + np.sin(x1) / 10) + np.cos(x1) / 10 * yh dy_dx1_term2 = -2 dy_dx2_term1 = dyh_dx2 * (1 + np.sin(x1) / 10) dy_dx2_term2 = 2 * x2 dy_dx1 = dy_dx1_term1 + dy_dx1_term2 dy_dx2 = dy_dx2_term1 + dy_dx2_term2 return y, (dy_dx1, dy_dx2) yh = park91a_hifi(x1, x2, x3, x4) term1 = (1 + np.sin(x1) / 10) * yh term2 = -2 * x1 + x2**2 + x3**2 y = term1 + term2 + 0.5 return y
[docs] def park91a_hifi(x1, x2, x3, x4, gradient_bool=False): r"""High-fidelity Park91a function. Simple four-dimensional benchmark function as proposed in [1] to mimic a computer model. For the purpose of multi-fidelity simulation, [3] defined a corresponding lower fidelity function, which is implemented in *park91a_lofi*. The high-fidelity version is defined as: .. math:: f({\bf x}) = \frac{x_1}{2}\left[ \sqrt{1+(x_2+x_3^2)\frac{x_4}{x_1^2}}-1 \right] +(x_1+3x_4)\exp \left[1-\sin(x_3) \right] Args: x1 (float): Input parameter 1 [0,1) x2 (float): Input parameter 2 [0,1) x3 (float): Input parameter 3 [0,1) x4 (float): Input parameter 4 [0,1) gradient_bool (bool, optional): Switch to return gradient value w.r.t. `x1` and `x2` Returns: y (float): Value of function at parameters dy_dx1 (float): Gradient of the function w.r.t. *x1* dy_dx2 (float): Gradient of the function w.r.t. *x2* References: [1] Park, J.-S.(1991). Tuning complex computer codes to data and optimal designs, Ph.D. Thesis [2] Cox, D. D., Park, J.-S., & Singer, C. E. (2001). A statistical method for tuning a computer code to a database. Computational Statistics & Data Analysis, 37(1), 77?92. http://doi.org/10.1016/S0167-9473(00)00057-8 [3] Xiong, S., Qian, P., & Wu, C. (2013). Sequential design and analysis of high-accuracy and low-accuracy computer codes. Technometrics. http://doi.org/10.1080/00401706.2012.723572 """ term1a = x1 / 2 term1b = np.sqrt(1 + (x2 + x3**2) * x4 / (x1**2)) - 1 term1 = term1a * term1b term2a = x1 + 3 * x4 term2b = np.exp(1 + np.sin(x3)) term2 = term2a * term2b y = term1 + term2 if gradient_bool: # ---- d_term1a_dx1 = 1 / 2 d_term1b_dx1 = ( 1 / (2 * np.sqrt(1 + (x2 + x3**2) * x4 / (x1**2))) * (-2 * (x2 + x3**2) * x4 * x1 ** (-3)) ) d_term1_dx1 = d_term1a_dx1 * term1b + term1a * d_term1b_dx1 d_term2a_dx1 = 1 d_term2b_dx1 = 0 d_term2_dx1 = d_term2a_dx1 * term2b + term2a * d_term2b_dx1 dy_dx1 = d_term1_dx1 + d_term2_dx1 # ---- d_term1a_dx2 = 0 d_term1b_dx2 = 1 / (2 * np.sqrt(1 + (x2 + x3**2) * x4 / (x1**2))) * x4 / (x1**2) d_term1_dx2 = d_term1a_dx2 * term1b + term1a * d_term1b_dx2 d_term2_dx2 = 0 dy_dx2 = d_term1_dx2 + d_term2_dx2 return y, (dy_dx1, dy_dx2) return y
[docs] def park91a_hifi_on_grid(x1, x2): r"""High-fidelity Park91a function on *x3* and *x4* grid. Args: x1 (float): Input parameter 1 [0,1) x2 (float): Input parameter 2 [0,1) Returns: np.ndarray: Value of the function at the parameters """ y = x3_x4_grid_eval(park91a_hifi, x1, x2) return y
[docs] def park91a_hifi_on_grid_with_gradients(x1, x2): r"""High-fidelity Park91a function on *x3* and *x4* grid with gradients. Args: x1 (float): Input parameter 1 [0,1) x2 (float): Input parameter 2 [0,1) Returns: y (float): Value of function at parameters dy_dx1 (float): Gradient of the function w.r.t. *x1* dy_dx2 (float): Gradient of the function w.r.t. *x2* """ y, (dy_dx1, dy_dx2) = x3_x4_grid_eval(park91a_hifi, x1, x2, gradient_bool=True) return y, (dy_dx1, dy_dx2)
[docs] def park91a_lofi_on_grid(x1, x2): r"""Low-fidelity Park91a function on fixed *x3* and *x4* grid. Args: x1 (float): Input parameter 1 [0,1) x2 (float): Input parameter 2 [0,1) Returns: np.ndarray: Value of the function at the parameters """ y = x3_x4_grid_eval(park91a_lofi, x1, x2) return y
[docs] def park91a_lofi_on_grid_with_gradients(x1, x2): r"""Low-fidelity Park91a function on x3 and x4 grid with gradients. Args: x1 (float): Input parameter 1 [0,1) x2 (float): Input parameter 2 [0,1) Returns: y (float): Value of function at parameters dy_dx1 (float): Gradient of the function w.r.t. x1 dy_dx2 (float): Gradient of the function w.r.t. x2 """ y, (dy_dx1, dy_dx2) = x3_x4_grid_eval(park91a_lofi, x1, x2, gradient_bool=True) return y, (dy_dx1, dy_dx2)