xopt 2.2.0__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xopt-2.2.0/xopt.egg-info → xopt-2.2.2}/PKG-INFO +1 -1
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_bax.py +33 -8
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_bayesian_exploration.py +10 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_model_constructor.py +34 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/test_neldermead.py +2 -2
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_vocs.py +34 -7
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_xopt.py +2 -2
- {xopt-2.2.0 → xopt-2.2.2}/xopt/_version.py +3 -3
- {xopt-2.2.0 → xopt-2.2.2}/xopt/asynchronous.py +6 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt/base.py +12 -4
- {xopt-2.2.0 → xopt-2.2.2}/xopt/evaluator.py +3 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/bax/acquisition.py +6 -3
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/bax/algorithms.py +1 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/bax_generator.py +9 -2
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/bayesian_exploration.py +8 -4
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/constrained_acquisition.py +4 -3
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/heteroskedastic.py +2 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/identity.py +5 -2
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/proximal.py +8 -4
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/models/prior_mean.py +2 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/models/standard.py +39 -12
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/multi_fidelity.py +1 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/objectives.py +2 -2
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/turbo.py +2 -2
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/visualize.py +43 -17
- {xopt-2.2.0 → xopt-2.2.2}/xopt/pydantic.py +4 -2
- xopt-2.2.2/xopt/resources/test_functions/sphere_100.py +13 -0
- xopt-2.2.2/xopt/resources/test_functions/sphere_50.py +13 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/testing.py +7 -2
- {xopt-2.2.0 → xopt-2.2.2}/xopt/vocs.py +131 -60
- {xopt-2.2.0 → xopt-2.2.2/xopt.egg-info}/PKG-INFO +1 -1
- {xopt-2.2.0 → xopt-2.2.2}/xopt.egg-info/SOURCES.txt +2 -0
- {xopt-2.2.0 → xopt-2.2.2}/LICENSE +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/MANIFEST.in +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/README.md +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/benchmarks/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/benchmarks/test_mo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/requirements.txt +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/setup.cfg +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/setup.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_bayesian_generator.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_constraints.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_custom_model.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_expected_improvement.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_hessian_kernel.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_high_level.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_mggpo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_mobo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_multi_fidelity.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_time_dependent_bo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_turbo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_upper_confidence_bound.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/bayesian/test_utils.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/test_extremum_seeking.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/test_latin_hypercube.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/test_random.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/generators/test_rcds.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_evaluator.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_generator.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_io.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_mpi.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_numerical_optimizer.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_perf.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_pydantic.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/tests/test_utils.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/versioneer.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/errors.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generator.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/base_model.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/bax/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/bayesian_generator.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/hessian_kernel.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/log_acquisition_function.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/multi_fidelity.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/expected_improvement.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/mggpo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/mobo.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/models/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/models/time_dependent.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/time_dependent.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/upper_confidence_bound.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/utils.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/es/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/es/extremumseeking.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/ga/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/ga/cnsga.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/ga/deap_creator.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/ga/deap_fitness_with_constraints.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/ga/fitness_with_constraints.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/random.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/rcds/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/rcds/rcds.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/scipy/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/scipy/latin_hypercube.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/scipy/neldermead.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/generators/utils.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/log.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/mpi/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/mpi/run.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/numerical_optimizer.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/benchmarking.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/__init__.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/ackley_20.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/modified_tnk.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/multi_objective.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/problem.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/rosenbrock.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/sinusoid_1d.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/sphere_20.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/resources/test_functions/tnk.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt/utils.py +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt.egg-info/dependency_links.txt +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt.egg-info/requires.txt +0 -0
- {xopt-2.2.0 → xopt-2.2.2}/xopt.egg-info/top_level.txt +0 -0
|
@@ -2,11 +2,15 @@ import pickle
|
|
|
2
2
|
from copy import deepcopy
|
|
3
3
|
from unittest.mock import patch
|
|
4
4
|
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
5
7
|
import torch
|
|
6
8
|
from botorch.models import SingleTaskGP
|
|
7
9
|
from botorch.models.model import ModelList
|
|
8
10
|
from botorch.models.transforms import Normalize, Standardize
|
|
9
11
|
|
|
12
|
+
from pydantic import ValidationError
|
|
13
|
+
|
|
10
14
|
from xopt.base import Xopt
|
|
11
15
|
from xopt.evaluator import Evaluator
|
|
12
16
|
from xopt.generators.bayesian.bax.algorithms import (
|
|
@@ -21,9 +25,12 @@ from xopt.resources.testing import TEST_VOCS_BASE, TEST_VOCS_DATA, xtest_callabl
|
|
|
21
25
|
class TestBaxGenerator:
|
|
22
26
|
@patch.multiple(Algorithm, __abstractmethods__=set())
|
|
23
27
|
def test_init(self):
|
|
28
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
29
|
+
test_vocs.objectives = {}
|
|
30
|
+
test_vocs.observables = ["y1"]
|
|
24
31
|
alg = Algorithm()
|
|
25
32
|
bax_gen = BaxGenerator(
|
|
26
|
-
vocs=
|
|
33
|
+
vocs=test_vocs,
|
|
27
34
|
algorithm=alg,
|
|
28
35
|
)
|
|
29
36
|
bax_gen.model_dump()
|
|
@@ -110,6 +117,8 @@ class TestBaxGenerator:
|
|
|
110
117
|
|
|
111
118
|
# test w/o constraints
|
|
112
119
|
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
120
|
+
test_vocs.objectives = {}
|
|
121
|
+
test_vocs.observables = ["y1"]
|
|
113
122
|
test_vocs.constraints = {}
|
|
114
123
|
gen = BaxGenerator(
|
|
115
124
|
vocs=test_vocs,
|
|
@@ -123,6 +132,8 @@ class TestBaxGenerator:
|
|
|
123
132
|
|
|
124
133
|
# test w/ constraints
|
|
125
134
|
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
135
|
+
test_vocs.objectives = {}
|
|
136
|
+
test_vocs.observables = ["y1"]
|
|
126
137
|
gen = BaxGenerator(
|
|
127
138
|
vocs=test_vocs,
|
|
128
139
|
algorithm=alg,
|
|
@@ -135,8 +146,11 @@ class TestBaxGenerator:
|
|
|
135
146
|
|
|
136
147
|
def test_cuda(self):
|
|
137
148
|
alg = GridMinimize()
|
|
149
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
150
|
+
test_vocs.objectives = {}
|
|
151
|
+
test_vocs.observables = ["y1"]
|
|
138
152
|
gen = BaxGenerator(
|
|
139
|
-
vocs=
|
|
153
|
+
vocs=test_vocs,
|
|
140
154
|
algorithm=alg,
|
|
141
155
|
)
|
|
142
156
|
|
|
@@ -151,13 +165,16 @@ class TestBaxGenerator:
|
|
|
151
165
|
evaluator = Evaluator(function=xtest_callable)
|
|
152
166
|
alg = GridMinimize()
|
|
153
167
|
|
|
168
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
169
|
+
test_vocs.objectives = {}
|
|
170
|
+
test_vocs.observables = ["y1"]
|
|
154
171
|
gen = BaxGenerator(
|
|
155
|
-
vocs=
|
|
172
|
+
vocs=test_vocs,
|
|
156
173
|
algorithm=alg,
|
|
157
174
|
)
|
|
158
175
|
gen.numerical_optimizer.n_restarts = 1
|
|
159
176
|
|
|
160
|
-
xopt = Xopt(generator=gen, evaluator=evaluator, vocs=
|
|
177
|
+
xopt = Xopt(generator=gen, evaluator=evaluator, vocs=test_vocs)
|
|
161
178
|
|
|
162
179
|
# initialize with single initial candidate
|
|
163
180
|
xopt.random_evaluate(3)
|
|
@@ -167,12 +184,13 @@ class TestBaxGenerator:
|
|
|
167
184
|
evaluator = Evaluator(function=xtest_callable)
|
|
168
185
|
alg = GridMinimize()
|
|
169
186
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
187
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
188
|
+
test_vocs.objectives = {}
|
|
189
|
+
test_vocs.observables = ["y1"]
|
|
190
|
+
gen = BaxGenerator(vocs=test_vocs, algorithm=alg, algorithm_results_file="test")
|
|
173
191
|
gen.numerical_optimizer.n_restarts = 1
|
|
174
192
|
|
|
175
|
-
xopt = Xopt(generator=gen, evaluator=evaluator, vocs=
|
|
193
|
+
xopt = Xopt(generator=gen, evaluator=evaluator, vocs=test_vocs)
|
|
176
194
|
|
|
177
195
|
# initialize with single initial candidate
|
|
178
196
|
xopt.random_evaluate(3)
|
|
@@ -191,3 +209,10 @@ class TestBaxGenerator:
|
|
|
191
209
|
|
|
192
210
|
os.remove("test_1.pkl")
|
|
193
211
|
os.remove("test_2.pkl")
|
|
212
|
+
|
|
213
|
+
def test_vocs_validation(self):
|
|
214
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
215
|
+
alg = GridMinimize()
|
|
216
|
+
|
|
217
|
+
with pytest.raises(ValidationError):
|
|
218
|
+
BaxGenerator(vocs=test_vocs, algorithm=alg)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
from copy import deepcopy
|
|
2
2
|
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
3
7
|
from xopt.base import Xopt
|
|
4
8
|
from xopt.evaluator import Evaluator
|
|
5
9
|
from xopt.generators.bayesian.bayesian_exploration import BayesianExplorationGenerator
|
|
@@ -76,3 +80,9 @@ class TestBayesianExplorationGenerator:
|
|
|
76
80
|
X.step()
|
|
77
81
|
X.step()
|
|
78
82
|
assert len(X.data) == 20
|
|
83
|
+
|
|
84
|
+
def test_vocs_validation(self):
|
|
85
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
86
|
+
|
|
87
|
+
with pytest.raises(ValidationError):
|
|
88
|
+
BayesianExplorationGenerator(vocs=test_vocs)
|
|
@@ -13,6 +13,7 @@ from botorch.models.transforms import Normalize, Standardize
|
|
|
13
13
|
from gpytorch import ExactMarginalLogLikelihood
|
|
14
14
|
from gpytorch.kernels import PeriodicKernel, PolynomialKernel, ScaleKernel
|
|
15
15
|
from gpytorch.likelihoods import GaussianLikelihood
|
|
16
|
+
from gpytorch.means import ConstantMean
|
|
16
17
|
from gpytorch.priors import GammaPrior
|
|
17
18
|
from pydantic import ValidationError
|
|
18
19
|
|
|
@@ -437,6 +438,23 @@ class TestModelConstructor:
|
|
|
437
438
|
assert torch.allclose(generated_pred, benchmark_pred, rtol=1e-3)
|
|
438
439
|
assert ~torch.allclose(generated_pred, old_prediction, rtol=1e-3)
|
|
439
440
|
|
|
441
|
+
def test_unused_modules_warning(self):
|
|
442
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
443
|
+
test_data = deepcopy(TEST_VOCS_DATA)
|
|
444
|
+
|
|
445
|
+
# test unused covariance or mean module
|
|
446
|
+
kwargs_covar = {"covar_modules": {"faulty_output_name": PeriodicKernel()}}
|
|
447
|
+
kwargs_mean = {"mean_modules": {"faulty_output_name": ConstantMean()}}
|
|
448
|
+
|
|
449
|
+
for kwargs in [kwargs_covar, kwargs_mean]:
|
|
450
|
+
gp_constructor = StandardModelConstructor(**kwargs)
|
|
451
|
+
generator = ExpectedImprovementGenerator(
|
|
452
|
+
vocs=test_vocs, gp_constructor=gp_constructor
|
|
453
|
+
)
|
|
454
|
+
generator.add_data(test_data)
|
|
455
|
+
with pytest.warns(UserWarning):
|
|
456
|
+
_ = generator.train_model()
|
|
457
|
+
|
|
440
458
|
def test_heteroskedastic(self):
|
|
441
459
|
test_data = deepcopy(TEST_VOCS_DATA)
|
|
442
460
|
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
@@ -487,6 +505,22 @@ class TestModelConstructor:
|
|
|
487
505
|
posterior.variance[..., 0].flatten(), bposterior.variance.flatten()
|
|
488
506
|
)
|
|
489
507
|
|
|
508
|
+
def test_custom_noise_prior(self):
|
|
509
|
+
test_data = deepcopy(TEST_VOCS_DATA)
|
|
510
|
+
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
511
|
+
|
|
512
|
+
noise_prior = GammaPrior(1.0, 1000.0)
|
|
513
|
+
|
|
514
|
+
gp_constructor = StandardModelConstructor(custom_noise_prior=noise_prior)
|
|
515
|
+
model = gp_constructor.build_model_from_vocs(test_vocs, test_data)
|
|
516
|
+
|
|
517
|
+
# check if created models have the correct noise priors
|
|
518
|
+
assert model.models[0].likelihood.noise_covar.noise_prior.rate == 1000.0
|
|
519
|
+
assert model.models[0].likelihood.noise_covar.noise_prior.concentration == 1.0
|
|
520
|
+
|
|
521
|
+
assert model.models[1].likelihood.noise_covar.noise_prior.rate == 1000.0
|
|
522
|
+
assert model.models[1].likelihood.noise_covar.noise_prior.concentration == 1.0
|
|
523
|
+
|
|
490
524
|
@pytest.fixture(autouse=True)
|
|
491
525
|
def clean_up(self):
|
|
492
526
|
yield
|
|
@@ -138,7 +138,7 @@ class TestNelderMeadGenerator:
|
|
|
138
138
|
data = X.vocs.variable_data(X.data).to_numpy()
|
|
139
139
|
assert np.array_equal(data, scipy_data)
|
|
140
140
|
|
|
141
|
-
idx, best = X.vocs.select_best(X.data)
|
|
141
|
+
idx, best, _ = X.vocs.select_best(X.data)
|
|
142
142
|
xbest = X.vocs.variable_data(X.data.loc[idx, :]).to_numpy().flatten()
|
|
143
143
|
assert np.array_equal(
|
|
144
144
|
xbest, result.x
|
|
@@ -216,7 +216,7 @@ class TestNelderMeadGenerator:
|
|
|
216
216
|
assert data.shape == scipy_data.shape
|
|
217
217
|
assert np.allclose(data, scipy_data, rtol=0, atol=1e-10)
|
|
218
218
|
|
|
219
|
-
idx, best = X.vocs.select_best(X.data)
|
|
219
|
+
idx, best, _ = X.vocs.select_best(X.data)
|
|
220
220
|
xbest = X.vocs.variable_data(X.data.loc[idx, :]).to_numpy().flatten()
|
|
221
221
|
assert np.array_equal(
|
|
222
222
|
xbest, result.x
|
|
@@ -3,6 +3,7 @@ from copy import deepcopy
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import pytest
|
|
6
|
+
from pydantic import ValidationError
|
|
6
7
|
|
|
7
8
|
from xopt.resources.testing import TEST_VOCS_BASE, TEST_VOCS_DATA
|
|
8
9
|
from xopt.vocs import ObjectiveEnum, VOCS
|
|
@@ -26,6 +27,28 @@ class TestVOCS(object):
|
|
|
26
27
|
variables={"x": [0, 1]},
|
|
27
28
|
)
|
|
28
29
|
|
|
30
|
+
def test_constraint_specification(self):
|
|
31
|
+
good_constraint_list = [
|
|
32
|
+
["LESS_THAN", 0],
|
|
33
|
+
["GREATER_THAN", 0],
|
|
34
|
+
["less_than", 0],
|
|
35
|
+
["greater_than", 0],
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
for ele in good_constraint_list:
|
|
39
|
+
VOCS(variables={"x": [0, 1]}, constraints={"c": ele})
|
|
40
|
+
|
|
41
|
+
bad_constraint_list = [
|
|
42
|
+
["LESS_THAN"],
|
|
43
|
+
["GREATER_TAN", 0],
|
|
44
|
+
[0, "less_than"],
|
|
45
|
+
["greater_than", "ahc"],
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
for ele in bad_constraint_list:
|
|
49
|
+
with pytest.raises(ValidationError):
|
|
50
|
+
VOCS(variables={"x": [0, 1]}, constraints={"c": ele})
|
|
51
|
+
|
|
29
52
|
def test_from_yaml(self):
|
|
30
53
|
Y = """
|
|
31
54
|
variables:
|
|
@@ -63,7 +86,11 @@ class TestVOCS(object):
|
|
|
63
86
|
data = pd.DataFrame(vocs.random_inputs(n_samples))
|
|
64
87
|
assert data.shape == (n_samples, vocs.n_inputs)
|
|
65
88
|
|
|
66
|
-
TEST_VOCS_BASE.random_inputs(5, include_constants=False)
|
|
89
|
+
test_inputs = TEST_VOCS_BASE.random_inputs(5, include_constants=False)
|
|
90
|
+
assert len(test_inputs) == 5
|
|
91
|
+
|
|
92
|
+
test_inputs = TEST_VOCS_BASE.random_inputs()
|
|
93
|
+
assert isinstance(test_inputs[0]["x1"], float)
|
|
67
94
|
|
|
68
95
|
def test_serialization(self):
|
|
69
96
|
vocs = deepcopy(TEST_VOCS_BASE)
|
|
@@ -149,7 +176,7 @@ class TestVOCS(object):
|
|
|
149
176
|
vocs.convert_dataframe_to_inputs(test_data)
|
|
150
177
|
|
|
151
178
|
res = vocs.convert_dataframe_to_inputs(test_data[vocs.variable_names])
|
|
152
|
-
assert "
|
|
179
|
+
assert "constant1" in res
|
|
153
180
|
|
|
154
181
|
def test_validate_input_data(self):
|
|
155
182
|
test_vocs = deepcopy(TEST_VOCS_BASE)
|
|
@@ -181,29 +208,29 @@ class TestVOCS(object):
|
|
|
181
208
|
|
|
182
209
|
# test maximization
|
|
183
210
|
vocs.objectives[vocs.objective_names[0]] = "MAXIMIZE"
|
|
184
|
-
idx, val = vocs.select_best(test_data)
|
|
211
|
+
idx, val, _ = vocs.select_best(test_data)
|
|
185
212
|
assert idx == [2]
|
|
186
213
|
assert val == [1.0]
|
|
187
214
|
|
|
188
215
|
vocs.constraints = {}
|
|
189
|
-
idx, val = vocs.select_best(test_data)
|
|
216
|
+
idx, val, _ = vocs.select_best(test_data)
|
|
190
217
|
assert idx == [3]
|
|
191
218
|
assert val == [1.5]
|
|
192
219
|
|
|
193
220
|
# test returning multiple best values -- sorted by best value
|
|
194
|
-
idx, val = vocs.select_best(test_data, 2)
|
|
221
|
+
idx, val, _ = vocs.select_best(test_data, 2)
|
|
195
222
|
assert np.allclose(idx, np.array([3, 2]))
|
|
196
223
|
assert np.allclose(val, np.array([1.5, 1.0]))
|
|
197
224
|
|
|
198
225
|
# test minimization
|
|
199
226
|
vocs.objectives[vocs.objective_names[0]] = "MINIMIZE"
|
|
200
227
|
vocs.constraints = {"c1": ["GREATER_THAN", 0.5]}
|
|
201
|
-
idx, val = vocs.select_best(test_data)
|
|
228
|
+
idx, val, _ = vocs.select_best(test_data)
|
|
202
229
|
assert idx == [0]
|
|
203
230
|
assert val == [0.5]
|
|
204
231
|
|
|
205
232
|
vocs.constraints = {}
|
|
206
|
-
idx, val = vocs.select_best(test_data)
|
|
233
|
+
idx, val, _ = vocs.select_best(test_data)
|
|
207
234
|
assert idx == 1
|
|
208
235
|
assert val == 0.1
|
|
209
236
|
|
|
@@ -208,7 +208,7 @@ class TestXopt:
|
|
|
208
208
|
xopt = Xopt(generator=generator, evaluator=evaluator, vocs=test_vocs)
|
|
209
209
|
|
|
210
210
|
test_vocs.variables = {"x2": [0, 1]}
|
|
211
|
-
test_vocs.constants
|
|
211
|
+
test_vocs.constants["x1"] = 2.0
|
|
212
212
|
|
|
213
213
|
out = xopt.evaluate({"x2": 0.2})
|
|
214
214
|
assert isinstance(out, dict)
|
|
@@ -259,7 +259,7 @@ class TestXopt:
|
|
|
259
259
|
val = str(xopt)
|
|
260
260
|
assert "Data size: 2" in val
|
|
261
261
|
assert (
|
|
262
|
-
"vocs:\n constants:\n
|
|
262
|
+
"vocs:\n constants:\n constant1: 1.0\n constraints:\n c1:\n - "
|
|
263
263
|
"GREATER_THAN\n - 0.5\n objectives:\n" in val
|
|
264
264
|
)
|
|
265
265
|
|
|
@@ -8,11 +8,11 @@ import json
|
|
|
8
8
|
|
|
9
9
|
version_json = '''
|
|
10
10
|
{
|
|
11
|
-
"date": "2024-
|
|
11
|
+
"date": "2024-04-15T14:51:35-0400",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "2.2.
|
|
14
|
+
"full-revisionid": "a613b00abdb1ff1809a10b82096947803834cec5",
|
|
15
|
+
"version": "2.2.2"
|
|
16
16
|
}
|
|
17
17
|
''' # END VERSION_JSON
|
|
18
18
|
|
|
@@ -31,7 +31,8 @@ class AsynchronousXopt(Xopt):
|
|
|
31
31
|
"""
|
|
32
32
|
Submit data to evaluator and return futures indexed to internal futures list.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
35
36
|
input_data: dataframe containing input data
|
|
36
37
|
|
|
37
38
|
"""
|
|
@@ -69,6 +70,10 @@ class AsynchronousXopt(Xopt):
|
|
|
69
70
|
"""
|
|
70
71
|
input_data = pd.DataFrame(input_data, copy=True) # copy for reindexing
|
|
71
72
|
|
|
73
|
+
# add constants to input data
|
|
74
|
+
for name, value in self.vocs.constants.items():
|
|
75
|
+
input_data[name] = value
|
|
76
|
+
|
|
72
77
|
# Reindex input dataframe
|
|
73
78
|
input_data.index = np.arange(
|
|
74
79
|
self._ix_last + 1, self._ix_last + 1 + len(input_data)
|
|
@@ -405,7 +405,12 @@ class Xopt(XoptBaseModel):
|
|
|
405
405
|
else:
|
|
406
406
|
return new_data
|
|
407
407
|
|
|
408
|
-
def random_evaluate(
|
|
408
|
+
def random_evaluate(
|
|
409
|
+
self,
|
|
410
|
+
n_samples=None,
|
|
411
|
+
seed=None,
|
|
412
|
+
custom_bounds: dict = None,
|
|
413
|
+
):
|
|
409
414
|
"""
|
|
410
415
|
Convenience method to generate random inputs using VOCs and evaluate them.
|
|
411
416
|
|
|
@@ -419,8 +424,9 @@ class Xopt(XoptBaseModel):
|
|
|
419
424
|
The number of random samples to generate.
|
|
420
425
|
seed : int, optional
|
|
421
426
|
The random seed for reproducibility.
|
|
422
|
-
|
|
423
|
-
|
|
427
|
+
custom_bounds : dict, optional
|
|
428
|
+
Dictionary of vocs-like ranges for random sampling
|
|
429
|
+
|
|
424
430
|
|
|
425
431
|
Returns
|
|
426
432
|
-------
|
|
@@ -428,7 +434,9 @@ class Xopt(XoptBaseModel):
|
|
|
428
434
|
The results of the evaluations added to the internal DataFrame.
|
|
429
435
|
|
|
430
436
|
"""
|
|
431
|
-
random_inputs = self.vocs.random_inputs(
|
|
437
|
+
random_inputs = self.vocs.random_inputs(
|
|
438
|
+
n_samples, seed=seed, custom_bounds=custom_bounds, include_constants=True
|
|
439
|
+
)
|
|
432
440
|
result = self.evaluate_data(random_inputs)
|
|
433
441
|
return result
|
|
434
442
|
|
|
@@ -37,7 +37,9 @@ class Evaluator(XoptBaseModel):
|
|
|
37
37
|
executor : NormalExecutor
|
|
38
38
|
NormalExecutor or any instantiated Executor object
|
|
39
39
|
vectorized : bool, default=False
|
|
40
|
-
If true,
|
|
40
|
+
If true, lists of evaluation points will be sent to the evaluator
|
|
41
|
+
function to be processed in parallel instead of evaluated seperately via
|
|
42
|
+
mapping.
|
|
41
43
|
"""
|
|
42
44
|
|
|
43
45
|
function: Callable
|
|
@@ -20,7 +20,8 @@ class ModelListExpectedInformationGain(MultiObjectiveAnalyticAcquisitionFunction
|
|
|
20
20
|
>>> EIG = ExpectedInformationGain(model, algo)
|
|
21
21
|
>>> eig = EIG(test_X)
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
24
25
|
model: A fitted independent multi-output (ModelList) model.
|
|
25
26
|
"""
|
|
26
27
|
|
|
@@ -62,13 +63,15 @@ class ModelListExpectedInformationGain(MultiObjectiveAnalyticAcquisitionFunction
|
|
|
62
63
|
def forward(self, X: Tensor) -> Tensor:
|
|
63
64
|
r"""Evaluate Expected Information Gain on the candidate set X.
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
66
68
|
X: A `(b1 x ... bk) x 1 x d`-dim batched tensor of `d`-dim design points.
|
|
67
69
|
Expected Information Gain is computed for each point individually,
|
|
68
70
|
i.e., what is considered are the marginal posteriors, not the
|
|
69
71
|
joint.
|
|
70
72
|
|
|
71
|
-
Returns
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
72
75
|
A `(b1 x ... bk)`-dim tensor of Expected Information Gain values at the
|
|
73
76
|
given design points `X`.
|
|
74
77
|
"""
|
|
@@ -46,7 +46,7 @@ class GridScanAlgorithm(Algorithm, ABC):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class GridMinimize(GridScanAlgorithm):
|
|
49
|
-
|
|
49
|
+
observable_names_ordered: List[str] = Field(
|
|
50
50
|
default=["y1"],
|
|
51
51
|
description="names of observable/objective models used in this algorithm",
|
|
52
52
|
)
|
|
@@ -3,7 +3,8 @@ import pickle
|
|
|
3
3
|
from copy import deepcopy
|
|
4
4
|
from typing import Dict
|
|
5
5
|
|
|
6
|
-
from pydantic import Field
|
|
6
|
+
from pydantic import Field, field_validator
|
|
7
|
+
from pydantic_core.core_schema import ValidationInfo
|
|
7
8
|
|
|
8
9
|
from xopt.generators.bayesian.bax.acquisition import ModelListExpectedInformationGain
|
|
9
10
|
from xopt.generators.bayesian.bax.algorithms import Algorithm
|
|
@@ -24,6 +25,12 @@ class BaxGenerator(BayesianGenerator):
|
|
|
24
25
|
|
|
25
26
|
_n_calls: int = 0
|
|
26
27
|
|
|
28
|
+
@field_validator("vocs", mode="after")
|
|
29
|
+
def validate_vocs(cls, v, info: ValidationInfo):
|
|
30
|
+
if v.n_objectives != 0:
|
|
31
|
+
raise ValueError("this generator only supports observables")
|
|
32
|
+
return v
|
|
33
|
+
|
|
27
34
|
def generate(self, n_candidates: int) -> list[dict]:
|
|
28
35
|
self._n_calls += 1
|
|
29
36
|
return super().generate(n_candidates)
|
|
@@ -31,7 +38,7 @@ class BaxGenerator(BayesianGenerator):
|
|
|
31
38
|
def _get_acquisition(self, model):
|
|
32
39
|
bax_model_ids = [
|
|
33
40
|
self.vocs.output_names.index(name)
|
|
34
|
-
for name in self.algorithm.
|
|
41
|
+
for name in self.algorithm.observable_names_ordered
|
|
35
42
|
]
|
|
36
43
|
bax_model = model.subset_output(bax_model_ids)
|
|
37
44
|
eig = ModelListExpectedInformationGain(
|
|
@@ -25,7 +25,7 @@ class BayesianExplorationGenerator(BayesianGenerator):
|
|
|
25
25
|
@field_validator("vocs", mode="after")
|
|
26
26
|
def validate_vocs(cls, v, info: ValidationInfo):
|
|
27
27
|
if v.n_objectives != 0:
|
|
28
|
-
raise ValueError("this generator only supports
|
|
28
|
+
raise ValueError("this generator only supports observables")
|
|
29
29
|
return v
|
|
30
30
|
|
|
31
31
|
def _get_acquisition(self, model):
|
|
@@ -55,7 +55,8 @@ class qPosteriorVariance(MCAcquisitionFunction):
|
|
|
55
55
|
X_pending: Optional[Tensor] = None,
|
|
56
56
|
) -> None:
|
|
57
57
|
r"""q-Upper Confidence Bound.
|
|
58
|
-
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
59
60
|
model: A fitted model.
|
|
60
61
|
sampler: The sampler used to draw base samples. Defaults to
|
|
61
62
|
`SobolQMCNormalSampler(num_samples=512, collapse_batch_dims=True)`
|
|
@@ -79,10 +80,13 @@ class qPosteriorVariance(MCAcquisitionFunction):
|
|
|
79
80
|
@t_batch_mode_transform()
|
|
80
81
|
def forward(self, X: Tensor) -> Tensor:
|
|
81
82
|
r"""Evaluate qUpperConfidenceBound on the candidate set `X`.
|
|
82
|
-
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
83
85
|
X: A `batch_sahpe x q x d`-dim Tensor of t-batches with `q` `d`-dim design
|
|
84
86
|
points each.
|
|
85
|
-
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
86
90
|
A `batch_shape'`-dim Tensor of Upper Confidence Bound values at the given
|
|
87
91
|
design points `X`, where `batch_shape'` is the broadcasted batch shape of
|
|
88
92
|
model and input `X`.
|
{xopt-2.2.0 → xopt-2.2.2}/xopt/generators/bayesian/custom_botorch/constrained_acquisition.py
RENAMED
|
@@ -18,8 +18,8 @@ class FeasibilityObjective(GenericMCObjective):
|
|
|
18
18
|
infeasible_cost: float = 0.0,
|
|
19
19
|
eta: float = 1e-3,
|
|
20
20
|
) -> None:
|
|
21
|
-
def ones_callable(X):
|
|
22
|
-
return torch.ones(
|
|
21
|
+
def ones_callable(Z, X=None):
|
|
22
|
+
return torch.ones(Z.shape[:-1])
|
|
23
23
|
|
|
24
24
|
super().__init__(objective=ones_callable)
|
|
25
25
|
self.constraints = constraints
|
|
@@ -29,7 +29,8 @@ class FeasibilityObjective(GenericMCObjective):
|
|
|
29
29
|
def forward(self, samples: Tensor, X: Optional[Tensor] = None) -> Tensor:
|
|
30
30
|
r"""Evaluate the feasibility-weighted objective on the samples.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
33
34
|
samples: A `sample_shape x batch_shape x q x m`-dim Tensors of
|
|
34
35
|
samples from a model posterior.
|
|
35
36
|
X: A `batch_shape x q x d`-dim tensor of inputs. Relevant only if
|
|
@@ -38,7 +38,8 @@ class XoptHeteroskedasticSingleTaskGP(BatchedMultiOutputGPyTorchModel, ExactGP):
|
|
|
38
38
|
to specify mean and covariance modules.
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
42
43
|
train_X: A `batch_shape x n x d` tensor of training features.
|
|
43
44
|
train_Y: A `batch_shape x n x m` tensor of training observations.
|
|
44
45
|
train_Yvar: A `batch_shape x n x m` tensor of observed measurement
|
|
@@ -11,10 +11,13 @@ class Identity(AcquisitionFunction):
|
|
|
11
11
|
@t_batch_mode_transform()
|
|
12
12
|
def forward(self, X: Tensor) -> Tensor:
|
|
13
13
|
r"""Evaluate qUpperConfidenceBound on the candidate set `X`.
|
|
14
|
-
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
15
16
|
X: A `batch_sahpe x q x d`-dim Tensor of t-batches with `q` `d`-dim design
|
|
16
17
|
points each.
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
18
21
|
A `batch_shape'`-dim Tensor of Upper Confidence Bound values at the given
|
|
19
22
|
design points `X`, where `batch_shape'` is the broadcasted batch shape of
|
|
20
23
|
model and input `X`.
|
|
@@ -55,7 +55,8 @@ class ProximalAcquisitionFunction(AcquisitionFunction):
|
|
|
55
55
|
r"""Derived Acquisition Function weighted by proximity to recently
|
|
56
56
|
observed point.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
59
60
|
acq_function: The base acquisition function, operating on input tensors
|
|
60
61
|
of feature dimension `d`.
|
|
61
62
|
proximal_weights: A `d` dim tensor used to bias locality
|
|
@@ -89,10 +90,12 @@ class ProximalAcquisitionFunction(AcquisitionFunction):
|
|
|
89
90
|
def forward(self, X: Tensor) -> Tensor:
|
|
90
91
|
r"""Evaluate base acquisition function with proximal weighting.
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
93
95
|
X: Input tensor of feature dimension `d` .
|
|
94
96
|
|
|
95
|
-
Returns
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
96
99
|
Base acquisition function evaluated on tensor `X` multiplied by proximal
|
|
97
100
|
weighting.
|
|
98
101
|
"""
|
|
@@ -143,7 +146,8 @@ def _validate_model(model: Model, proximal_weights: Tensor) -> None:
|
|
|
143
146
|
Perform vaidation checks on model used in base acquisition function to make sure
|
|
144
147
|
it is compatible with proximal weighting.
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
147
151
|
model: Model associated with base acquisition function to be validated.
|
|
148
152
|
proximal_weights: A `d` dim tensor used to bias locality
|
|
149
153
|
along each axis.
|
|
@@ -14,7 +14,8 @@ class CustomMean(Mean):
|
|
|
14
14
|
):
|
|
15
15
|
"""Custom prior mean for a GP based on an arbitrary model.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
18
19
|
model: Representation of the model.
|
|
19
20
|
input_transformer: Module used to transform inputs in the GP.
|
|
20
21
|
outcome_transformer: Module used to transform outcomes in the GP.
|