Source code for easyvvuq.sampling.grid_sampler

"""A grid sampler

Useful for e.g. hyperparameter search. The "vary" dict contains the values
that must be considered per (hyper)parameter, for instance:

    vary = {"x1": [0.0, 0.5, 0.1],
            "x2 = [1, 3],
            "x3" = [True, False]}

The sampler will create a tensor grid using all specified 1D parameter
values.
"""

__author__ = "Wouter Edeling"
__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"

from itertools import product
import numpy as np
from .base import BaseSamplingElement  # , Vary


[docs]class Grid_Sampler(BaseSamplingElement, sampler_name="grid_sampler"): def __init__(self, vary, count=0): """ Initialize the grid sampler. Parameters ---------- vary : dict, or list of dicts A dictionary containing all 1D values for each parameter. For instance vary = {"x1": [0.0, 0.5. 1.0], "x2": [True, False]}. This will create a 2D tensor product of all (x1, x2) parameter combinations. If a list of vary dicts is specified, each vary dict will be treated independently to generate points. These dicts do not have to contain the same parameters. The tensor product points are stored in the 'points' list, with one tensor product per vary dict. count : int, optional Internal counter used to count the number of samples that have been executed. The default is 0. Returns ------- None. """ # allways add vary to list, even if only a single dict is specified if not isinstance(vary, list): vary = [vary] self.vary = vary self.count = count self.points = [] # make sure all parameters are stored in a list or array, even # if they have only a single value for _vary in vary: for param in _vary.keys(): if not isinstance(_vary[param], list) and not isinstance(_vary[param], np.ndarray): vary[param] = [vary[param]] # use dtype=object to allow for multiple different type (float, boolean etc) self.points.append(np.array(list(product(*list(_vary.values()))), dtype=object)) # the cumulative sizes of all ensembles generated by the vary dicts self.cumul_sizes = np.cumsum([points.shape[0] for points in self.points]) # add a zero to the beginning (necessary in __next__ subroutine) self.cumul_sizes = np.insert(self.cumul_sizes, 0, 0)
[docs] def is_finite(self): return True
[docs] def n_samples(self): """Returns the number of samples in this sampler. """ # return self.points.shape[0] return self.cumul_sizes[-1]
[docs] def get_param_names(self): """ Get the names of all parameters that were varied. Returns ------- param_names : list List of parameter names. """ param_names = [] for _vary in self.vary: for name in _vary.keys(): if not name in param_names: param_names.append(name) return param_names
def __next__(self): """ Return the next sample from the input distributions. Raises ------ StopIteration Stop iteration when count >= n_samples. Returns ------- run_dict : dict A dictionary with the random input samples, e.g. {'x1': 0.5, 'x2': False}. """ if self.count < self.n_samples(): vary_idx = np.where(self.count < self.cumul_sizes[1:])[0][0] run_dict = {} i_par = 0 for param_name in self.vary[vary_idx].keys(): sample_idx = self.count - self.cumul_sizes[vary_idx] run_dict[param_name] = self.points[vary_idx][sample_idx][i_par] i_par += 1 self.count += 1 return run_dict else: raise StopIteration