Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions emukit/core/initial_designs/latin_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@


import numpy as np

try:
import pyDOE
except ImportError:
raise ImportError("pyDOE needs to be installed in order to use latin design")
from scipy.stats import qmc

from .. import ParameterSpace
from .base import InitialDesignBase
Expand All @@ -20,7 +16,8 @@ class LatinDesign(InitialDesignBase):
"""
Latin hypercube experiment design.

Based on pyDOE implementation. For further reference see https://pythonhosted.org/pyDOE/randomized.html#latin-hypercube
Based on scipy implementation. For further reference see
https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.qmc.LatinHypercube.html
"""

def __init__(self, parameter_space: ParameterSpace) -> None:
Expand All @@ -37,15 +34,14 @@ def get_samples(self, point_count: int) -> np.ndarray:
:return: A numpy array of generated samples, shape (point_count x space_dim)
"""
bounds = self.parameter_space.get_bounds()
X_design_aux = pyDOE.lhs(len(bounds), point_count, criterion="center")
ones = np.ones((X_design_aux.shape[0], 1))

lower_bound = np.asarray(bounds)[:, 0].reshape(1, len(bounds))
upper_bound = np.asarray(bounds)[:, 1].reshape(1, len(bounds))
diff = upper_bound - lower_bound
d = len(bounds)
lower_bounds = [x[0] for x in bounds]
upper_bounds = [x[1] for x in bounds]

X_design = np.dot(ones, lower_bound) + X_design_aux * np.dot(ones, diff)
sampler = qmc.LatinHypercube(d)
samples = sampler.random(n=point_count)
samples = qmc.scale(samples, lower_bounds, upper_bounds)

samples = self.parameter_space.round(X_design)
X_design = self.parameter_space.round(samples)

return samples
return X_design
29 changes: 12 additions & 17 deletions emukit/core/initial_designs/sobol_design.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
# Copyright 2020-2024 The Emukit Authors. All Rights Reserved.
# Copyright 2020-2026 The Emukit Authors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

# Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0


# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0


import numpy as np

try:
from sobol_seq import i4_sobol_generate
except ImportError:
raise ImportError("sobol_seq needs to be installed in order to use sobol design")
from scipy.stats import qmc

from .. import ParameterSpace
from .base import InitialDesignBase
Expand All @@ -23,7 +15,8 @@
class SobolDesign(InitialDesignBase):
"""
Sobol experiment design.
Based on sobol_seq implementation. For further reference see https://github.com/naught101/sobol_seq
Based on scipy implementation. For further reference see
https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.qmc.Sobol.html
"""

def __init__(self, parameter_space: ParameterSpace) -> None:
Expand All @@ -40,12 +33,14 @@ def get_samples(self, point_count: int) -> np.ndarray:
:return: A numpy array of generated samples, shape (point_count x space_dim)
"""
bounds = self.parameter_space.get_bounds()
lower_bound = np.asarray(bounds)[:, 0].reshape(1, len(bounds))
upper_bound = np.asarray(bounds)[:, 1].reshape(1, len(bounds))
diff = upper_bound - lower_bound
d = len(bounds)
lower_bounds = [x[0] for x in bounds]
upper_bounds = [x[1] for x in bounds]

X_design = np.dot(i4_sobol_generate(len(bounds), point_count), np.diag(diff[0, :])) + lower_bound
sampler = qmc.Sobol(d)
samples = sampler.random(n=point_count)
samples = qmc.scale(samples, lower_bounds, upper_bounds)

samples = self.parameter_space.round(X_design)
X_design = self.parameter_space.round(samples)

return samples
return X_design
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ dependencies = [
"scipy",
"matplotlib>=3.9",
"emcee>=2.2.1",
"pydoe<0.9.6",
"sobol_seq>=0.1.2",
]

[project.optional-dependencies]
Expand Down
2 changes: 0 additions & 2 deletions requirements/integration_test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
scikit-learn
# For BNN model
pybnn==0.0.5
# For Latin design
PyDOE>=0.3.0
# For notebook tests
jupyter==1.0.0
pandas>=1.0.5
2 changes: 0 additions & 2 deletions requirements/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ matplotlib>=3.9
# GPy>=1.13.0
emcee>=2.2.1
scipy
PyDOE>=0.3.0
sobol_seq>=0.1.2
21 changes: 18 additions & 3 deletions tests/emukit/core/test_model_free_designs.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Copyright 2020-2024 The Emukit Authors. All Rights Reserved.
# Copyright 2020-2026 The Emukit Authors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

# Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

import numpy as np

from emukit.core import CategoricalParameter, ContinuousParameter, DiscreteParameter, ParameterSpace
from emukit.core.initial_designs import RandomDesign
Expand All @@ -25,8 +26,22 @@ def test_design_returns_correct_number_of_points():
points = design.get_samples(points_count)

assert points_count == len(points)
columns_count = 1
assert all([len(p) == columns_count for p in points])
assert all([len(p) == 1 for p in points])


def test_design_returns_points_within_bounds():
p1 = ContinuousParameter("p1", 0.01, 0.05)
p2 = ContinuousParameter("p2", -100.0, -90.0)
space = ParameterSpace([p1, p2])
points_count = 5

designs = create_model_free_designs(space)
for design in designs:
points = design.get_samples(points_count)

for i, p in enumerate(space.parameters):
assert np.all(p.min <= points[:, i])
assert np.all(points[:, i] <= p.max)


def test_design_with_mixed_domain(encoding):
Expand Down