Source code for experiment_design.random_sampling

import logging
from functools import partial

import numpy as np
from scipy.stats import uniform

from experiment_design.covariance_modification import (
    iman_connover_transformation,
    second_moment_transformation,
)
from experiment_design.experiment_designer import ExperimentDesigner
from experiment_design.optimize import random_search
from experiment_design.scorers import Scorer, ScorerFactory
from experiment_design.variable import ParameterSpace


[docs] class RandomSamplingDesigner(ExperimentDesigner): """ Create or extend a |DoE| by randomly sampling from the variable distributions. :param exact_correlation: If True, the correlation matrix of the resulting design will match the target correlation exactly using a second moment transformation. This may lead variables with finite bounds to generate values that are out of bounds. Otherwise, Iman-Connover method will be used, where the values will be kept as is for each variable as they are generated from the marginal distribution. This may lead to some imprecision of the correlation matrix. :param scorer_factory: A factory that creates scorers for the given variables, sample_size and in the cast of an extension, old sampling points. If not passed, a default one will be created, that evaluates the maximum correlation error and minimum pairwise distance.See `experiment_design.scorers.create_default_scorer_factory <#experiment_design.scorers.create_default_scorer_factory>`_ for more details. Examples -------- >>> from experiment_design import create_continuous_uniform_space, RandomSamplingDesigner >>> space = create_continuous_uniform_space([-2., -2.], [2., 2.]) >>> designer = RandomSamplingDesigner() >>> doe1 = designer.design(space, sample_size=20) >>> doe1.shape (20, 2) >>> doe2 = designer.design(space, sample_Size=4, old_sample=doe1) >>> doe2.shape (4, 2) """ def __init__( self, exact_correlation: bool = False, scorer_factory: ScorerFactory | None = None, ) -> None: self.exact_correlation = exact_correlation super(RandomSamplingDesigner, self).__init__(scorer_factory=scorer_factory) def _create( self, space: ParameterSpace, sample_size: int, scorer: Scorer, initial_steps: int, final_steps: int, ) -> np.ndarray: steps = initial_steps + final_steps if steps <= 1: return sample_from(space, sample_size, self.exact_correlation) return random_search( creator=partial( sample_from, space, sample_size, self.exact_correlation, ), scorer=scorer, steps=steps, ) def _extend( self, old_sample: np.ndarray, space: ParameterSpace, sample_size: int, scorer: Scorer, initial_steps: int, final_steps: int, ) -> np.ndarray: steps = initial_steps + final_steps if steps <= 1: return sample_from(space, sample_size, self.exact_correlation) logging.warning( "If the design space changes, " "random sampling may not handle correlation modification properly!" ) return random_search( creator=partial( sample_from, space, sample_size, self.exact_correlation, ), scorer=scorer, steps=steps, )
def sample_from( space: ParameterSpace, sample_size: int, exact_correlation: bool = False, ) -> np.ndarray: """ Sample from the distributions of the variables. :param space: Determines the dimensions of the resulting sample. :param sample_size: The number of points to be created. :param exact_correlation: If True, second moment transformation will be used, which may not respect the finite bounds of the marginal distributions. Otherwise, Iman-Connover method will be used, which may yield imprecise correlation matrices. :return: Sample matrix with shape (len(variables), samples_size). :meta private: """ # Sometimes, we may randomly generate probabilities with # singular correlation matrices. Try 3 times to avoid issue until we give up error_text = "" transformer = ( second_moment_transformation if exact_correlation else iman_connover_transformation ) for k in range(3): doe = uniform(0, 1).rvs((sample_size, space.dimensions)) doe = space.value_of(doe) if ( np.isclose(space.correlation, np.eye(space.dimensions)).all() and not exact_correlation ): return doe try: return transformer(doe, space.correlation) except np.linalg.LinAlgError as exc: error_text = str(exc) pass raise np.linalg.LinAlgError(error_text)