Source code for SALib.analyze.fast

import math
import numpy as np
from scipy.stats import norm

from . import common_args
from ..util import read_param_file, ResultDict


[docs] def analyze( problem, Y, M=4, num_resamples=100, conf_level=0.95, print_to_console=False, seed=None, ): """Perform extended Fourier Amplitude Sensitivity Test on model outputs. Returns a dictionary with keys 'S1' and 'ST', where each entry is a list of size D (the number of parameters) containing the indices in the same order as the parameter file. Notes ----- Compatible with: `fast_sampler` : :func:`SALib.sample.fast_sampler.sample` Examples -------- >>> X = fast_sampler.sample(problem, 1000) >>> Y = Ishigami.evaluate(X) >>> Si = fast.analyze(problem, Y, print_to_console=False) Parameters ---------- problem : dict The problem definition Y : numpy.array A NumPy array containing the model outputs M : int The interference parameter, i.e., the number of harmonics to sum in the Fourier series decomposition (default 4) print_to_console : bool Print results directly to console (default False) seed : int Seed to generate a random number References ---------- 1. Cukier, R. I., C. M. Fortuin, K. E. Shuler, A. G. Petschek, and J. H. Schaibly (1973). Study of the sensitivity of coupled reaction systems to uncertainties in rate coefficients. J. Chem. Phys., 59(8):3873-3878 doi:10.1063/1.1680571 2. Saltelli, A., S. Tarantola, and K. P.-S. Chan (1999). A Quantitative Model-Independent Method for Global Sensitivity Analysis of Model Output. Technometrics, 41(1):39-56, doi:10.1080/00401706.1999.10485594. 3. Pujol, G. (2006) fast99 - R `sensitivity` package https://github.com/cran/sensitivity/blob/master/R/fast99.R """ if seed: np.random.seed(seed) D = problem["num_vars"] if Y.size % (D) == 0: N = int(Y.size / D) else: msg = """ Error: Number of samples in model output file must be a multiple of D, where D is the number of parameters. """ raise ValueError(msg) # Recreate the vector omega used in the sampling omega_0 = math.floor((N - 1) / (2 * M)) # Calculate and Output the First and Total Order Values Si = ResultDict((k, [None] * D) for k in ["S1", "ST", "S1_conf", "ST_conf"]) Si["names"] = problem["names"] for i in range(D): z = np.arange(i * N, (i + 1) * N) Y_l = Y[z] S1, ST = compute_orders(Y_l, N, M, omega_0) Si["S1"][i] = S1 Si["ST"][i] = ST S1_d_conf, ST_d_conf = bootstrap(Y_l, M, num_resamples, conf_level) Si["S1_conf"][i] = S1_d_conf Si["ST_conf"][i] = ST_d_conf if print_to_console: print(Si.to_df()) return Si
[docs] def compute_orders(outputs: np.ndarray, N: int, M: int, omega: int): f = np.fft.fft(outputs) Sp = np.power(np.absolute(f[np.arange(1, math.ceil(N / 2))]) / N, 2) V = 2.0 * np.sum(Sp) # Calculate first and total order D1 = 2.0 * np.sum(Sp[np.arange(1, M + 1) * omega - 1]) Dt = 2.0 * np.sum(Sp[np.arange(math.floor(omega / 2.0))]) return (D1 / V), (1.0 - Dt / V)
[docs] def bootstrap(Y: np.ndarray, M: int, resamples: int, conf_level: float): """Compute CIs. Infers ``N`` from results of sub-sample ``Y`` and re-estimates omega (ω) for the above ``N``. """ # Use half of available data each time T_data = Y.shape[0] n_size = math.ceil(T_data * 0.5) res_S1 = np.zeros(resamples) res_ST = np.zeros(resamples) for i in range(resamples): sample_idx = np.random.choice(T_data, replace=True, size=n_size) Y_rs = Y[sample_idx] N = len(Y_rs) omega = math.floor((N - 1) / (2 * M)) S1, ST = compute_orders(Y_rs, N, M, omega) res_S1[i] = S1 res_ST[i] = ST bnd = norm.ppf(0.5 + conf_level / 2.0) S1_conf = bnd * res_S1.std(ddof=1) ST_conf = bnd * res_ST.std(ddof=1) return S1_conf, ST_conf
# No additional arguments required for FAST
[docs] def cli_parse(parser): """Add method specific options to CLI parser. Parameters ---------- parser : argparse object Returns ------- Updated argparse object """ parser.add_argument( "-M", "--M", type=int, required=False, default=4, help="Inference parameter" ) parser.add_argument( "-r", "--resamples", type=int, required=False, default=100, help="Number of bootstrap resamples for Sobol " "confidence intervals", ) return parser
[docs] def cli_action(args): problem = read_param_file(args.paramfile) Y = np.loadtxt( args.model_output_file, delimiter=args.delimiter, usecols=(args.column,) ) analyze( problem, Y, M=args.M, num_resamples=args.resamples, print_to_console=True, seed=args.seed, )
if __name__ == "__main__": common_args.run_cli(cli_parse, cli_action)