def test_raises(self): seed = np.random.RandomState(12345) message = r"'toto' is not a valid optimization method" with pytest.raises(ValueError, match=message): qmc.LatinHypercube(1, seed=seed, optimization="toto") message = r"not a valid strength" with pytest.raises(ValueError, match=message): qmc.LatinHypercube(1, strength=3, seed=seed) message = r"n is not the square of a prime number" with pytest.raises(ValueError, match=message): engine = qmc.LatinHypercube(d=2, strength=2, seed=seed) engine.random(16) message = r"n is not the square of a prime number" with pytest.raises(ValueError, match=message): engine = qmc.LatinHypercube(d=2, strength=2, seed=seed) engine.random(5) # because int(sqrt(5)) would result in 2 message = r"n is too small for d" with pytest.raises(ValueError, match=message): engine = qmc.LatinHypercube(d=5, strength=2, seed=seed) engine.random(9)
def test_discrepancy_hierarchy(self): seed = 68756348576543 lhs = qmc.LatinHypercube(d=2, seed=seed) sample_ref = lhs.random(n=20) disc_ref = qmc.discrepancy(sample_ref) optimal_ = qmc.LatinHypercube(d=2, seed=seed, optimization="random-CD") sample_ = optimal_.random(n=20) disc_ = qmc.discrepancy(sample_) assert disc_ < disc_ref
def test_discrepancy_hierarchy(self): seed = np.random.RandomState(123456) lhs = qmc.LatinHypercube(d=2, seed=seed) sample_ref = lhs.random(n=20) disc_ref = qmc.discrepancy(sample_ref) seed = np.random.RandomState(123456) optimal_ = qmc.LatinHypercube(d=2, seed=seed, optimization="random-CD") sample_ = optimal_.random(n=20) disc_ = qmc.discrepancy(sample_) assert disc_ < disc_ref
def test_sample_stratified(self, optimization, centered, strength): seed = np.random.default_rng(37511836202578819870665127532742111260) p = 5 n = p**2 d = 6 expected1d = (np.arange(n) + 0.5) / n expected = np.broadcast_to(expected1d, (d, n)).T engine = qmc.LatinHypercube(d=d, centered=centered, strength=strength, optimization=optimization, seed=seed) sample = engine.random(n=n) assert sample.shape == (n, d) assert engine.num_generated == n sorted_sample = np.sort(sample, axis=0) assert np.any(sample != expected) assert_allclose(sorted_sample, expected, atol=0.5 / n) assert np.any(sample - expected > 0.5 / n) if strength == 2 and optimization is None: unique_elements = np.arange(p) desired = set(product(unique_elements, unique_elements)) for i, j in combinations(range(engine.d), 2): samples_2d = sample[:, [i, j]] res = (samples_2d * p).astype(int) res_set = set((tuple(row) for row in res)) assert_equal(res_set, desired)
def test_raises(self): message = r"not a valid strength" with pytest.raises(ValueError, match=message): qmc.LatinHypercube(1, strength=3) message = r"n is not the square of a prime number" with pytest.raises(ValueError, match=message): engine = qmc.LatinHypercube(d=2, strength=2) engine.random(16) message = r"n is not the square of a prime number" with pytest.raises(ValueError, match=message): engine = qmc.LatinHypercube(d=2, strength=2) engine.random(5) # because int(sqrt(5)) would result in 2 message = r"n is too small for d" with pytest.raises(ValueError, match=message): engine = qmc.LatinHypercube(d=5, strength=2) engine.random(9)
def test_perm_discrepancy(self): rng = np.random.default_rng(46449423132557934943847369749645759997) qmc_gen = qmc.LatinHypercube(5, seed=rng) sample = qmc_gen.random(10) disc = qmc.discrepancy(sample) for i in range(100): row_1 = rng.integers(10) row_2 = rng.integers(10) col = rng.integers(5) disc = _perturb_discrepancy(sample, row_1, row_2, col, disc) sample[row_1, col], sample[row_2, col] = ( sample[row_2, col], sample[row_1, col]) disc_reference = qmc.discrepancy(sample) assert_allclose(disc, disc_reference)
def test_perm_discrepancy(self): seed = np.random.RandomState(123456) qmc_gen = qmc.LatinHypercube(5, seed=seed) sample = qmc_gen.random(10) disc = qmc.discrepancy(sample) for i in range(100): row_1 = np.random.randint(10) row_2 = np.random.randint(10) col = np.random.randint(5) disc = _perturb_discrepancy(sample, row_1, row_2, col, disc) sample[row_1, col], sample[row_2, col] = (sample[row_2, col], sample[row_1, col]) disc_reference = qmc.discrepancy(sample) assert_allclose(disc, disc_reference)
def draw_exploration_sample( x, lower, upper, n_samples, sampling_distribution, sampling_method, seed, ): """Get a sample of parameter values for the first stage of the tiktak algorithm. The sample is created randomly or using a low discrepancy sequence. Different distributions are available. Args: x (np.ndarray): Internal parameter vector of shape (n_params,). lower (np.ndarray): Vector of internal lower bounds of shape (n_params,). upper (np.ndarray): Vector of internal upper bounds of shape (n_params,). n_samples (int): Number of sample points on which one function evaluation shall be performed. Default is 10 * n_params. sampling_distribution (str): One of "uniform", "triangular". Default is "uniform", as in the original tiktak algorithm. sampling_method (str): One of "sobol", "halton", "latin_hypercube" or "random". Default is sobol for problems with up to 200 parameters and random for problems with more than 200 parameters. seed (int): Random seed. Returns: np.ndarray: Numpy array of shape (n_samples, n_params). Each row represents a vector of parameter values. """ valid_rules = ["sobol", "halton", "latin_hypercube", "random"] valid_distributions = ["uniform", "triangular"] if sampling_method not in valid_rules: raise ValueError( f"Invalid rule: {sampling_method}. Must be one of\n\n{valid_rules}\n\n" ) if sampling_distribution not in valid_distributions: raise ValueError(f"Unsupported distribution: {sampling_distribution}") for name, bound in zip(["lower", "upper"], [lower, upper]): if not np.isfinite(bound).all(): raise ValueError( f"multistart optimization requires finite {name}_bounds or " f"soft_{name}_bounds for all parameters.") if sampling_method == "sobol": # Draw `n` points from the open interval (lower, upper)^d. # Note that scipy uses the half-open interval [lower, upper)^d internally. # We apply a burn-in phase of 1, i.e. we skip the first point in the sequence # and thus exclude the lower bound. sampler = qmc.Sobol(d=len(lower), scramble=False, seed=seed) _ = sampler.fast_forward(1) sample_unscaled = sampler.random(n=n_samples) elif sampling_method == "halton": sampler = qmc.Halton(d=len(lower), scramble=False, seed=seed) sample_unscaled = sampler.random(n=n_samples) elif sampling_method == "latin_hypercube": sampler = qmc.LatinHypercube(d=len(lower), strength=1, seed=seed) sample_unscaled = sampler.random(n=n_samples) elif sampling_method == "random": rng = get_rng(seed) sample_unscaled = rng.uniform(size=(n_samples, len(lower))) if sampling_distribution == "uniform": sample_scaled = qmc.scale(sample_unscaled, lower, upper) elif sampling_distribution == "triangular": sample_scaled = triang.ppf( sample_unscaled, c=(x - lower) / (upper - lower), loc=lower, scale=upper - lower, ) return sample_scaled