Source code for queens.example_simulator_functions.sobol_g_function

#
# 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/>.
#
"""Sobol's G function.

A test function for sensitivity analysis.

References:
    [1]: Saltelli, A., Annoni, P., Azzini, I., Campolongo, F., Ratto, M., & Tarantola, S. (2010).
    Variance based sensitivity analysis of model output.
    Design and estimator for the total sensitivity index.
    Computer Physics Communications, 181(2), 259–270.
    https://doi.org/10.1016/j.cpc.2009.09.018
"""

import numpy as np

# values  of G*_6 in Table 5 of [1]
DIM = 10
A = np.array([0, 0.1, 0.2, 0.3, 0.4, 0.8, 1, 2, 3, 4])
ALPHA = np.array([2] * DIM)
DELTA = np.array([0] * DIM)


[docs] def sobol_g_function(a=A, alpha=ALPHA, delta=DELTA, **kwargs): """Compute generalized Sobol's G function. With variable dimension. See (33) in [1]. Default is a 10-dimensional version as defined in [1]. Args: a (ndarray): Vector of a parameter values alpha (ndarray): Vector of alpha parameter values delta (ndarray): Vector of delta parameter values kwargs (dict): Contains the input x_i ~ U(0,1) (uniformly from [0,1]) Returns: double: Value of the Sobol function """ # some safety checks: if not np.all(a >= 0): raise ValueError("a >= 0 not satisfied") if not np.all(alpha > 0): raise ValueError("alpha > 0 not satisfied") if not (np.all(delta >= 0) and np.all(delta <= 1)): raise ValueError("0 <= delta <= 1 not satisfied") # From kwargs get the x_i x = [] for key, value in kwargs.items(): if key.find("x") > -1: x.append(value) x = np.array(x) if not (x.shape == a.shape and x.shape == alpha.shape and x.shape == delta.shape): raise ValueError("shape mismatch") g = ((1 + alpha) * np.abs(2 * (x + delta - np.floor(x + delta)) - 1) ** alpha + a) / (1 + a) return np.prod(g)
[docs] def first_effect_variance(a=A, alpha=ALPHA): r"""Calculate first effect variance :math:`V_{x_{i}}[E_{x \sim i}[Y|x_i]]`. Args: a (ndarray): Vector of *a* parameter values alpha (ndarray): Vector of *alpha* parameter values Returns: ndarray: Vector of first order variances """ variance_i = alpha**2 / (1 + 2 * alpha) / (1 + a) ** 2 return variance_i
[docs] def variance(variance_i=first_effect_variance(a=A, alpha=ALPHA)): """Calculate variance of Sobol function :math:`V_x[Y]`. Args: variance_i (ndarray): Vector of first effect variances Returns: double: Variance of Sobol's G function """ total_variance = np.prod(1 + variance_i) - 1 return total_variance
[docs] def first_order_indices(a=A, alpha=ALPHA): """Compute first order indices of the Sobol test function. See (32) in [1]. Args: a (ndarray): Vector of *a* parameter values alpha (ndarray): Vector of *alpha* parameter values Returns: ndarray: Vector of first order indices """ variance_i = first_effect_variance(a, alpha) total_variance = variance(variance_i=variance_i) first_order_index_i = variance_i / total_variance return first_order_index_i
[docs] def total_order_indices(a=A, alpha=ALPHA): """Compute total indices of Sobol test function. See (31)-(32) in [1]. Args: a (ndarray): Vector of *a* parameter values alpha (ndarray): Vector of *alpha* parameter values Returns: ndarray: Vector of total order indices """ variance_i = first_effect_variance(a=a, alpha=alpha) total_variance = variance(variance_i=variance_i) total_index = np.empty(variance_i.shape) for i in range(variance_i.shape[0]): mask = np.ones(variance_i.shape, dtype=bool) mask[i] = False total_index[i] = variance_i[i] * np.prod(1 + variance_i[mask]) / total_variance return total_index