Source code for easyvvuq.sampling.qmc

"""This sampler is meant to be used with the QMC Analysis module.
"""

import chaospy as cp
from SALib.sample import saltelli
from .base import BaseSamplingElement, Vary
import logging

__author__ = "Jalal Lakhlili"
__copyright__ = """

    Copyright 2018 Robin A. Richardson, David W. Wright

    This file is part of EasyVVUQ

    EasyVVUQ is free software: you can redistribute it and/or modify
    it under the terms of the Lesser GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    EasyVVUQ 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
    Lesser GNU General Public License for more details.

    You should have received a copy of the Lesser GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

"""
__license__ = "LGPL"


[docs]class QMCSampler(BaseSamplingElement, sampler_name="QMC_sampler"): def __init__(self, vary, n_mc_samples, count=0): """Create a Quasi Monte Carlo sampler. Parameters ---------- vary: dict Expects a dictionary where the keys are variable names (inputs for your simulation that you want to vary during sampling) and values are ChaosPy distributions you want to sample from. n_mc_samples : int An estimate for how many samples the monte carlo run will need. count : int This is used to resume sampling. It will skip the first count samples if this parameter is not zero. """ if not isinstance(vary, dict): msg = ("'vary' must be a dictionary of the names of the " "parameters you want to vary, and their corresponding " "distributions.") raise RuntimeError(msg) if len(vary) == 0: msg = "'vary' cannot be empty." raise RuntimeError(msg) self.vary = Vary(vary) self.n_mc_samples = n_mc_samples # List of the probability distributions of uncertain parameters params_distribution = list(vary.values()) # Multivariate distribution self.distribution = cp.J(*params_distribution) # Generate samples self.n_params = len(vary) dist_U = [] for i in range(self.n_params): dist_U.append(cp.Uniform()) dist_U = cp.J(*dist_U) problem = { "num_vars": self.n_params, "names": list(vary.keys()), "bounds": [[0, 1]] * self.n_params } nodes = saltelli.sample(problem, n_mc_samples, calc_second_order=False) self._samples = self.distribution.inv(dist_U.fwd(nodes.transpose())) self._n_samples = n_mc_samples * (self.n_params + 2) # Fast forward to specified count, if possible self.count = 0 if count >= self._n_samples: msg = (f"Attempt to start sampler fastforwarded to count {count}, " f"but sampler only has {self._n_samples} samples, therefore" f"this sampler will not provide any more samples.") logging.debug(msg) else: for _ in range(count): self.__next__()
[docs] def is_finite(self): """Can this sampler produce only a finite number of samples.""" return True
@property def n_samples(self): """Returns the number of samples in this sampler. Returns ------- This computed with the formula (d + 2) * N, where d is the number of uncertain parameters and N is the (estimated) number of samples for the Monte Carlo method. """ return self._n_samples @property def analysis_class(self): """Return a corresponding analysis class. """ from easyvvuq.analysis import QMCAnalysis return QMCAnalysis def __next__(self): if self.count < self.n_samples: run_dict = {} i_par = 0 for param_name in self.vary.get_keys(): run_dict[param_name] = self._samples.T[self.count][i_par] i_par += 1 self.count += 1 return run_dict else: raise StopIteration