Skip to content

Commit

Permalink
Add new sampling methods and corresponding tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Antoine-Averland committed Nov 23, 2023
1 parent 7c15217 commit 7c5fc3e
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 112 deletions.
129 changes: 90 additions & 39 deletions smt/sampling_methods/pydoe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,122 @@
"""
from pyDOE3 import doe_box_behnken
from pyDOE3 import doe_gsd
from pyDOE3 import doe_factorial
from pyDOE3 import doe_plackett_burman
import numpy as np

from smt.sampling_methods.sampling_method import SamplingMethod

class BoxBehnken(SamplingMethod):

def _compute (self, nt):
nlevels = 3
class PyDoeSamplingMethod(SamplingMethod):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.nx = self.options["xlimits"].shape[0]
self.levels = None

def _compute(self, nt: int = None):
xlimits = self.options["xlimits"]
nx = xlimits.shape[0]
levels = self.levels

box_behnken = doe_box_behnken.bbdesign(nx)
indices = np.array(box_behnken + 1, dtype=int)
doe = self._compute_doe()
indices = np.array(doe, dtype=int)
print(indices)

values = np.zeros((nx, nlevels))
for i in range(nx):
values[i, :] = np.linspace(xlimits[i, 0], xlimits[i, 1], num=nlevels)
values = np.zeros((self.nx, max(levels)))
for i in range(self.nx):
values[i, 0 : levels[i]] = np.linspace(
xlimits[i, 0], xlimits[i, 1], num=levels[i]
)
print(values)

res = np.zeros(box_behnken.shape)
res = np.zeros(doe.shape)
i = 0
for idx in indices:
for j in range(nx):
for idx in indices:
for j in range(self.nx):
res[i, j] = values[j, idx[j]]
i = i+1
i = i + 1

return res

def _compute_doe():
raise NotImplementedError(
"You have to implement DOE generation method _compute_doe()"
)


class BoxBehnken(PyDoeSamplingMethod):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.levels = [3] * self.nx # for box behnken the number of levels is fixed

def _compute_doe(self):
box_behnken_doe = (
doe_box_behnken.bbdesign(self.nx) + 1
) # We have to increment the elements of doe_box_behnken to have the indices

return box_behnken_doe


class Gsd(PyDoeSamplingMethod):
def __init__(self, **kwargs):
super().__init__(**kwargs)

class Gsd(SamplingMethod):
self.levels = self.options["levels"]

def _initialize(self, **kwargs):
self.options.declare(
"levels",
types= list,
desc="number of factor levels per factor in design",
)
"levels",
types=list,
desc="number of factor levels per factor in design",
)
self.options.declare(
"reduction",
types=int,
desc="Reduction factor (bigger than 1). Larger `reduction` means fewer experiments in the design and more possible complementary designs",
)
"reduction",
types=int,
desc="Reduction factor (bigger than 1). Larger `reduction` means fewer experiments in the design and more possible complementary designs",
)

def _compute (self, nt):
def _compute_doe(self):
levels = self.options["levels"]
reduction = self.options["reduction"]
xlimits = self.options["xlimits"]
nx = xlimits.shape[0]
gsd_doe = doe_gsd.gsd(levels, reduction)

gsd = doe_gsd.gsd(levels, reduction)
indices = np.array(gsd, dtype=int)
print(indices)
return gsd_doe

values = np.zeros((nx, max(levels)))
for i in range(nx):
values[i, 0:levels[i]] = np.linspace(xlimits[i, 0], xlimits[i, 1], num=levels[i])
print(values)

res = np.zeros(gsd.shape)
i = 0
for idx in indices:
for j in range(nx):
res[i, j] = values[j, idx[j]]
i = i+1
class Factorial(PyDoeSamplingMethod):
def __init__(self, **kwargs):
super().__init__(**kwargs)

return res
self.levels = self.options["levels"]

def _initialize(self, **kwargs):
self.options.declare(
"levels",
types=list,
desc="number of factor levels per factor in design",
)

def _compute_doe(self):
levels = self.options["levels"]
factorial_doe = doe_factorial.fullfact(levels)

return factorial_doe


class PlackettBurman(PyDoeSamplingMethod):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.levels = [2] * self.nx # for plackett burman the number of levels is fixed

def _compute_doe(self):
plackett_burman_doe = doe_plackett_burman.pbdesign(self.nx)
ny = plackett_burman_doe.shape[1]
nb_rows = 4 * (
int(self.nx / 4) + 1
) # calculate the correct number of rows (multiple of 4)
for i in range(nb_rows):
for j in range(ny):
if plackett_burman_doe[i, j] == -1:
plackett_burman_doe[i, j] = 0

return plackett_burman_doe
167 changes: 94 additions & 73 deletions smt/sampling_methods/tests/test_pydoe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,94 +3,115 @@

from smt.sampling_methods import pydoe


class TestPyDOE3(unittest.TestCase):
def test_bbdesign(self):
xlimits = np.array([[2., 5], [-5, 1], [0, 3]])
xlimits = np.array([[2.0, 5], [-5, 1], [0, 3]])
sampling = pydoe.BoxBehnken(xlimits=xlimits)

num = 10
actual = sampling()
self.assertEqual((15, 3), actual.shape)
print(actual)
expected = [[ 2., -5., 1.5],
[ 5., -5., 1.5],
[ 2., 1., 1.5],
[ 5., 1., 1.5],
[ 2., -2., 0.],
[ 5., -2., 0.],
[ 2., -2., 3.],
[ 5., -2., 3.],
[ 3.5, -5., 0.],
[ 3.5, 1., 0.],
[ 3.5, -5., 3.],
[ 3.5, 1., 3.],
[ 3.5, -2., 1.5],
[ 3.5, -2., 1.5],
[ 3.5, -2., 1.5]
]


expected = [
[2.0, -5.0, 1.5],
[5.0, -5.0, 1.5],
[2.0, 1.0, 1.5],
[5.0, 1.0, 1.5],
[2.0, -2.0, 0.0],
[5.0, -2.0, 0.0],
[2.0, -2.0, 3.0],
[5.0, -2.0, 3.0],
[3.5, -5.0, 0.0],
[3.5, 1.0, 0.0],
[3.5, -5.0, 3.0],
[3.5, 1.0, 3.0],
[3.5, -2.0, 1.5],
[3.5, -2.0, 1.5],
[3.5, -2.0, 1.5],
]

np.testing.assert_allclose(actual, expected)


def test_gsd1(self):
xlimits = np.array([[5, 11], [0,6], [-3, 4]])
sampling = pydoe.Gsd(xlimits = xlimits, levels = [3, 4, 6], reduction = 4)
xlimits = np.array([[5, 11], [0, 6], [-3, 4]])
sampling = pydoe.Gsd(xlimits=xlimits, levels=[3, 4, 6], reduction=4)
actual = sampling()
self.assertEqual((18, 3), actual.shape)
print(actual)
expected = [
[5, 0, -3],
[5, 0, 2.6],
[5, 2, -1.6],
[5, 2, 4],
[5, 4, -0.2],
[5, 6, 1.2],
[8, 0, -1.6],
[8, 0, 4],
[8, 2, -0.2],
[8, 4, 1.2],
[8, 6, -3],
[8, 6, 2.6],
[11, 0, -0.2],
[11, 2, 1.2],
[11, 4, -3],
[11, 4, 2.6],
[11, 6, -1.6],
[11, 6, 4],
]

np.testing.assert_allclose(actual, expected)

def test_factorial(self):
xlimits = np.array([[4, 10], [-3, 3], [5, 7]])
sampling = pydoe.Factorial(xlimits=xlimits, levels=[2, 4, 3])
actual = sampling()
self.assertEqual((18,3), actual.shape)
self.assertEqual((24, 3), actual.shape)
print(actual)
expected = [[5, 0, -3],
[5, 0, 2.6],
[5, 2, -1.6],
[5, 2, 4],
[5, 4, -0.2],
[5, 6, 1.2],
[8, 0, -1.6],
[8, 0, 4],
[8, 2, -0.2],
[8, 4, 1.2],
[8, 6, -3],
[8, 6, 2.6],
[11, 0, -0.2],
[11, 2, 1.2],
[11, 4, -3],
[11, 4, 2.6],
[11, 6, -1.6],
[11, 6, 4]
]

expected = [
[4.0, -3.0, 5.0],
[10.0, -3.0, 5.0],
[4.0, -1.0, 5.0],
[10.0, -1.0, 5.0],
[4.0, 1.0, 5.0],
[10.0, 1.0, 5.0],
[4.0, 3.0, 5.0],
[10.0, 3.0, 5.0],
[4.0, -3.0, 6.0],
[10.0, -3.0, 6.0],
[4.0, -1.0, 6.0],
[10.0, -1.0, 6.0],
[4.0, 1.0, 6.0],
[10.0, 1.0, 6.0],
[4.0, 3.0, 6.0],
[10.0, 3.0, 6.0],
[4.0, -3.0, 7.0],
[10.0, -3.0, 7.0],
[4.0, -1.0, 7.0],
[10.0, -1.0, 7.0],
[4.0, 1.0, 7.0],
[10.0, 1.0, 7.0],
[4.0, 3.0, 7.0],
[10.0, 3.0, 7.0],
]
np.testing.assert_allclose(actual, expected)

# def test_gsd2(self):
# xlimits = np.array([[1, 7], [-5,1]])
# sampling = pydoe.Gsd(xlimits = xlimits)
#
# actual = sampling()
# self.assertEqual((18,3), actual.shape)
# print(actual)
# expected = [
# [
# [1, -5],
# [1, -1],
# [7, -5],
# [7, -1],
# [4, -3],
# [4, 1]
# ]
#
# [
# [1, -3],
# [1, 1],
# [7, -3],
# [7, 1],
# [4, -5],
# [4, -1]
# ]
# ]
#
# np.testing.assert_allclose(actual, expected)
def test_plackett_burman(self):
xlimits = np.array([[2, 5], [-5, 1], [0, 3], [4, 8], [-1, 2]])
sampling = pydoe.PlackettBurman(xlimits=xlimits)
actual = sampling()
self.assertEqual((8, 5), actual.shape)
print(actual)
expected = [
[2.0, -5.0, 3.0, 4.0, 2.0],
[5.0, -5.0, 0.0, 4.0, -1.0],
[2.0, 1.0, 0.0, 4.0, 2.0],
[5.0, 1.0, 3.0, 4.0, -1.0],
[2.0, -5.0, 3.0, 8.0, -1.0],
[5.0, -5.0, 0.0, 8.0, 2.0],
[2.0, 1.0, 0.0, 8.0, -1.0],
[5.0, 1.0, 3.0, 8.0, 2.0],
]

np.testing.assert_allclose(actual, expected)


if __name__ == "__main__":
Expand Down

0 comments on commit 7c5fc3e

Please sign in to comment.