Skip to content

Commit bad219f

Browse files
ocba var handling
1 parent 53afcb2 commit bad219f

5 files changed

Lines changed: 230 additions & 13 deletions

File tree

notebooks/testKriging.ipynb

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1392,12 +1392,161 @@
13921392
"S.min_y"
13931393
]
13941394
},
1395+
{
1396+
"cell_type": "markdown",
1397+
"metadata": {},
1398+
"source": [
1399+
"## test ocba"
1400+
]
1401+
},
13951402
{
13961403
"cell_type": "code",
13971404
"execution_count": null,
13981405
"metadata": {},
13991406
"outputs": [],
1400-
"source": []
1407+
"source": [
1408+
"\n",
1409+
"import copy\n",
1410+
"import numpy as np\n",
1411+
"from spotPython.fun.objectivefunctions import analytical\n",
1412+
"from spotPython.spot import spot\n",
1413+
"from spotPython.budget.ocba import get_ocba\n",
1414+
"\n",
1415+
"# Test based on the example from the book:\n",
1416+
"# Chun-Hung Chen and Loo Hay Lee:\n",
1417+
"# Stochastic Simulation Optimization: An Optimal Computer Budget Allocation,\n",
1418+
"# pp. 49 and pp. 215\n",
1419+
"# p. 49:\n",
1420+
"# mean_y = np.array([1,2,3,4,5])\n",
1421+
"# var_y = np.array([1,1,9,9,4])\n",
1422+
"# get_ocba(mean_y, var_y, 50)\n",
1423+
"# [11 9 19 9 2]\n",
1424+
"\n",
1425+
"fun = analytical().fun_linear\n",
1426+
"fun_control = {\"sigma\": 0.001,\n",
1427+
" \"seed\": 123}\n",
1428+
"spot_1_noisy = spot.Spot(fun=fun,\n",
1429+
" lower = np.array([-1]),\n",
1430+
" upper = np.array([1]),\n",
1431+
" fun_evals = 20,\n",
1432+
" fun_repeats = 2,\n",
1433+
" noise = True,\n",
1434+
" ocba_delta=1,\n",
1435+
" seed=123,\n",
1436+
" show_models=False,\n",
1437+
" fun_control = fun_control,\n",
1438+
" design_control={\"init_size\": 3,\n",
1439+
" \"repeats\": 2},\n",
1440+
" surrogate_control={\"noise\": True})\n",
1441+
"spot_1_noisy.run()\n",
1442+
"spot_2 = copy.deepcopy(spot_1_noisy)\n",
1443+
"spot_2.mean_y = np.array([1,2,3,4,5])\n",
1444+
"spot_2.var_y = np.array([1,1,9,9,4])\n",
1445+
"n = 50\n",
1446+
"o = get_ocba(spot_2.mean_y, spot_2.var_y, n)\n",
1447+
"assert sum(o) == 50\n",
1448+
"assert (o == np.array([[11, 9, 19, 9, 2]])).all()\n",
1449+
"o"
1450+
]
1451+
},
1452+
{
1453+
"cell_type": "code",
1454+
"execution_count": 1,
1455+
"metadata": {},
1456+
"outputs": [
1457+
{
1458+
"name": "stderr",
1459+
"output_type": "stream",
1460+
"text": [
1461+
"Seed set to 123\n"
1462+
]
1463+
},
1464+
{
1465+
"name": "stdout",
1466+
"output_type": "stream",
1467+
"text": [
1468+
"S.X: [[ 0. 1. ]\n",
1469+
" [ 1. 0. ]\n",
1470+
" [ 1. 1. ]\n",
1471+
" [ 1. 1. ]\n",
1472+
" [ 0.54509876 -0.36921401]\n",
1473+
" [ 0.54509876 -0.36921401]\n",
1474+
" [ 0.18642675 0.87708546]\n",
1475+
" [ 0.18642675 0.87708546]\n",
1476+
" [-0.45060393 -0.208063 ]\n",
1477+
" [-0.45060393 -0.208063 ]]\n",
1478+
"S.y: [0.98021757 0.99264427 2.02575851 2.00387949 0.45185626 0.44499372\n",
1479+
" 0.79130456 0.81487288 0.24000221 0.23988634]\n",
1480+
"X_shape_before: (10, 2)\n",
1481+
"y_size_before: 10\n",
1482+
"S.X: [[ 0. 1. ]\n",
1483+
" [ 1. 0. ]\n",
1484+
" [ 1. 1. ]\n",
1485+
" [ 1. 1. ]\n",
1486+
" [ 0.54509876 -0.36921401]\n",
1487+
" [ 0.54509876 -0.36921401]\n",
1488+
" [ 0.18642675 0.87708546]\n",
1489+
" [ 0.18642675 0.87708546]\n",
1490+
" [-0.45060393 -0.208063 ]\n",
1491+
" [-0.45060393 -0.208063 ]\n",
1492+
" [-0.39841465 -0.21105872]\n",
1493+
" [-0.39841465 -0.21105872]]\n",
1494+
"S.y: [0.98021757 0.99264427 2.02575851 2.00387949 0.45185626 0.44499372\n",
1495+
" 0.79130456 0.81487288 0.24000221 0.23988634 0.18349759 0.19592429]\n",
1496+
"S.n_points: 1\n",
1497+
"S.ocba_delta: 1\n",
1498+
"X_shape_after: (12, 2)\n",
1499+
"y_size_after: 12\n"
1500+
]
1501+
}
1502+
],
1503+
"source": [
1504+
"import numpy as np\n",
1505+
"from spotPython.fun.objectivefunctions import analytical\n",
1506+
"from spotPython.spot import spot\n",
1507+
"from spotPython.utils.init import fun_control_init\n",
1508+
"# number of initial points:\n",
1509+
"ni = 3\n",
1510+
"X_start = np.array([[0, 1], [1, 0], [1, 1], [1, 1]])\n",
1511+
"\n",
1512+
"fun = analytical().fun_sphere\n",
1513+
"fun_control = fun_control_init(\n",
1514+
" sigma=0.02,\n",
1515+
" seed=123,)\n",
1516+
"lower = np.array([-1, -1])\n",
1517+
"upper = np.array([1, 1])\n",
1518+
"design_control={\"init_size\": ni,\n",
1519+
" \"repeats\": 2}\n",
1520+
"\n",
1521+
"S = spot.Spot(fun=fun,\n",
1522+
" noise=True,\n",
1523+
" fun_repeats=2,\n",
1524+
" n_points=1,\n",
1525+
" ocba_delta=1,\n",
1526+
" log_level = 10,\n",
1527+
" lower = lower,\n",
1528+
" upper= upper,\n",
1529+
" show_progress=False,\n",
1530+
" design_control=design_control,\n",
1531+
" fun_control=fun_control\n",
1532+
")\n",
1533+
"S.initialize_design(X_start=X_start)\n",
1534+
"print(f\"S.X: {S.X}\")\n",
1535+
"print(f\"S.y: {S.y}\")\n",
1536+
"X_shape_before = S.X.shape\n",
1537+
"print(f\"X_shape_before: {X_shape_before}\")\n",
1538+
"print(f\"y_size_before: {S.y.size}\")\n",
1539+
"y_size_before = S.y.size\n",
1540+
"S.update_stats()\n",
1541+
"S.fit_surrogate()\n",
1542+
"S.update_design()\n",
1543+
"print(f\"S.X: {S.X}\")\n",
1544+
"print(f\"S.y: {S.y}\")\n",
1545+
"print(f\"S.n_points: {S.n_points}\")\n",
1546+
"print(f\"S.ocba_delta: {S.ocba_delta}\")\n",
1547+
"print(f\"X_shape_after: {S.X.shape}\")\n",
1548+
"print(f\"y_size_after: {S.y.size}\")\n"
1549+
]
14011550
},
14021551
{
14031552
"cell_type": "code",

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[project]
99
name = "spotPython"
10-
version = "0.8.15"
10+
version = "0.8.16"
1111
authors = [
1212
{ name="T. Bartz-Beielstein", email="tbb@bartzundbartz.de" }
1313
]

src/spotPython/budget/ocba.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
from spotPython.utils.aggregate import get_ranks
66
from numpy import int32, float64
77
from numpy import argpartition, repeat
8-
from numpy import zeros, square, sqrt, full, around
8+
from numpy import zeros, square, sqrt, full, around, array
99

1010

11-
def get_ocba(means, vars, delta) -> int32:
11+
def get_ocba(means, vars, delta, verbose=False) -> array:
1212
"""
1313
Optimal Computer Budget Allocation (OCBA)
1414
@@ -25,9 +25,14 @@ def get_ocba(means, vars, delta) -> int32:
2525
https://github.com/TomMonks/sim-tools
2626
2727
Args:
28-
means (numpy.array): An array of means.
29-
vars (numpy.array): An array of variances.
30-
delta (int): The incremental budget.
28+
means (numpy.array):
29+
An array of means.
30+
vars (numpy.array):
31+
An array of variances.
32+
delta (int):
33+
The incremental budget.
34+
verbose (bool):
35+
If True, print the results.
3136
3237
Returns:
3338
(numpy.array): An array of budget recommendations.
@@ -36,11 +41,11 @@ def get_ocba(means, vars, delta) -> int32:
3641
The implementation is based on the pseudo-code in the Chen et al. (p. 49), see [1].
3742
3843
Examples:
39-
From the Chen et al. book (p. 49):
44+
>>> # From the Chen et al. book (p. 49):
4045
>>> mean_y = np.array([1,2,3,4,5])
4146
var_y = np.array([1,1,9,9,4])
4247
get_ocba(mean_y, var_y, 50)
43-
[11 9 19 9 2]
48+
array([11, 9, 19, 9, 2])
4449
"""
4550
n_designs = means.shape[0]
4651
allocations = zeros(n_designs, int32)
@@ -58,6 +63,23 @@ def get_ocba(means, vars, delta) -> int32:
5863
more_runs = full(n_designs, True, dtype=bool)
5964
add_budget = zeros(n_designs, dtype=float)
6065
more_alloc = True
66+
if verbose:
67+
print("\nIn get_ocba():")
68+
print(f"means: {means}")
69+
print(f"vars: {vars}")
70+
print(f"delta: {delta}")
71+
print(f"n_designs: {n_designs}")
72+
print(f"Allocations: {allocations}")
73+
print(f"Ratios: {ratios}")
74+
print(f"Budget: {budget}")
75+
print(f"Ranks: {ranks}")
76+
print(f"Best: {best}")
77+
print(f"Second best: {second_best}")
78+
print(f"Select: {select}")
79+
print(f"Temp: {temp}")
80+
print(f"More runs: {more_runs}")
81+
print(f"Add budget: {add_budget}")
82+
print(f"More allocations: {more_alloc}")
6183
while more_alloc:
6284
more_alloc = False
6385
ratio_s = (more_runs * ratios).sum()
@@ -66,6 +88,11 @@ def get_ocba(means, vars, delta) -> int32:
6688
mask = add_budget < allocations
6789
add_budget[mask] = allocations[mask]
6890
more_runs[mask] = 0
91+
if verbose:
92+
print("\nIn more_alloc:")
93+
print(f"ratio_s: {ratio_s}")
94+
print(f"more_runs: {more_runs}")
95+
print(f"add_budget: {add_budget}")
6996
if mask.sum() > 0:
7097
more_alloc = True
7198
if more_alloc:

src/spotPython/spot/spot.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ def __init__(
249249
# specified once:
250250
if len(self.var_type) < self.k:
251251
self.var_type = self.var_type * self.k
252-
logger.warning("Warning: All variable types forced to 'num'.")
252+
logger.warning("All variable types forced to 'num'.")
253253
self.infill_criterion = infill_criterion
254254
# Bounds
255255
de_bounds = []
@@ -749,14 +749,19 @@ def update_design(self) -> None:
749749
"""
750750
# OCBA (only if noise). Determination of the OCBA points depends on the
751751
# old X and y values.
752-
if self.noise and self.ocba_delta > 0:
752+
if self.noise and self.ocba_delta > 0 and not np.all(self.var_y > 0):
753+
logger.warning(
754+
"self.var_y <= 0. OCBA points are not generated, because there are points with no variance information."
755+
)
756+
logger.debug("In update_design(): self.var_y: %s", self.var_y)
757+
if self.noise and self.ocba_delta > 0 and np.all(self.var_y > 0):
753758
X_ocba = get_ocba_X(self.mean_X, self.mean_y, self.var_y, self.ocba_delta)
754759
else:
755760
X_ocba = None
756761
# Determine the new X0 values based on the old X and y values:
757762
X0 = self.get_new_X0()
758763
# Append OCBA points to the new design points:
759-
if self.noise and self.ocba_delta > 0:
764+
if self.noise and self.ocba_delta > 0 and np.all(self.var_y > 0):
760765
X0 = append(X_ocba, X0, axis=0)
761766
X_all = self.to_all_dim_if_needed(X0)
762767
logger.debug(

test/test_update_design.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_update_design():
3131
def test_update_design_with_repeats_and_ocba():
3232
# number of initial points:
3333
ni = 3
34-
X_start = np.array([[0, 1], [1, 0], [1, 1], [1, 1]])
34+
X_start = np.array([[1, 0], [1, 0], [1, 1], [1, 1]])
3535
fun = analytical().fun_sphere
3636
fun_control = fun_control_init(
3737
sigma=0.02,
@@ -61,3 +61,39 @@ def test_update_design_with_repeats_and_ocba():
6161
assert X_shape_before[0] + S.n_points * S.fun_repeats + S.ocba_delta == S.X.shape[0]
6262
assert X_shape_before[1] == S.X.shape[1]
6363
assert y_size_before + S.n_points * S.fun_repeats + S.ocba_delta == S.y.size
64+
65+
def test_update_design_with_repeats_and_ocba_no_var():
66+
# number of initial points:
67+
ni = 3
68+
# The first two X_start points have no repeats and therefore no variance
69+
X_start = np.array([[0, 1], [1, 0], [1, 1], [1, 1]])
70+
fun = analytical().fun_sphere
71+
fun_control = fun_control_init(
72+
sigma=0.02,
73+
seed=123,)
74+
lower = np.array([-1, -1])
75+
upper = np.array([1, 1])
76+
design_control={"init_size": ni,
77+
"repeats": 2}
78+
S = spot.Spot(fun=fun,
79+
noise=True,
80+
fun_repeats=2,
81+
n_points=1,
82+
ocba_delta=1,
83+
lower = lower,
84+
upper= upper,
85+
show_progress=True,
86+
design_control=design_control,
87+
fun_control=fun_control
88+
)
89+
S.initialize_design(X_start=X_start)
90+
X_shape_before = S.X.shape
91+
y_size_before = S.y.size
92+
S.update_stats()
93+
S.fit_surrogate()
94+
S.update_design()
95+
# compare the shapes of the X and y values before and after the update_design method
96+
# no ocba points are added because the first two points have no variance
97+
assert X_shape_before[0] + S.n_points * S.fun_repeats == S.X.shape[0]
98+
assert X_shape_before[1] == S.X.shape[1]
99+
assert y_size_before + S.n_points * S.fun_repeats == S.y.size

0 commit comments

Comments
 (0)