xopt 2.2.2__tar.gz → 2.2.3__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.
Files changed (123) hide show
  1. {xopt-2.2.2/xopt.egg-info → xopt-2.2.3}/PKG-INFO +1 -1
  2. {xopt-2.2.2 → xopt-2.2.3}/requirements.txt +1 -1
  3. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_bayesian_generator.py +9 -0
  4. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_utils.py +1 -1
  5. {xopt-2.2.2 → xopt-2.2.3}/tests/test_evaluator.py +28 -1
  6. {xopt-2.2.2 → xopt-2.2.3}/tests/test_vocs.py +16 -0
  7. {xopt-2.2.2 → xopt-2.2.3}/xopt/_version.py +3 -3
  8. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/bayesian_exploration.py +0 -7
  9. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/bayesian_generator.py +17 -3
  10. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/expected_improvement.py +1 -2
  11. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/mggpo.py +1 -1
  12. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/mobo.py +1 -1
  13. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/objectives.py +7 -21
  14. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/upper_confidence_bound.py +1 -3
  15. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/utils.py +20 -7
  16. xopt-2.2.3/xopt/generators/bayesian/visualize.py +1091 -0
  17. {xopt-2.2.2 → xopt-2.2.3}/xopt/utils.py +5 -1
  18. {xopt-2.2.2 → xopt-2.2.3}/xopt/vocs.py +8 -0
  19. {xopt-2.2.2 → xopt-2.2.3/xopt.egg-info}/PKG-INFO +1 -1
  20. {xopt-2.2.2 → xopt-2.2.3}/xopt.egg-info/requires.txt +1 -1
  21. xopt-2.2.2/xopt/generators/bayesian/visualize.py +0 -417
  22. {xopt-2.2.2 → xopt-2.2.3}/LICENSE +0 -0
  23. {xopt-2.2.2 → xopt-2.2.3}/MANIFEST.in +0 -0
  24. {xopt-2.2.2 → xopt-2.2.3}/README.md +0 -0
  25. {xopt-2.2.2 → xopt-2.2.3}/benchmarks/__init__.py +0 -0
  26. {xopt-2.2.2 → xopt-2.2.3}/benchmarks/test_mo.py +0 -0
  27. {xopt-2.2.2 → xopt-2.2.3}/setup.cfg +0 -0
  28. {xopt-2.2.2 → xopt-2.2.3}/setup.py +0 -0
  29. {xopt-2.2.2 → xopt-2.2.3}/tests/__init__.py +0 -0
  30. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/__init__.py +0 -0
  31. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/__init__.py +0 -0
  32. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_bax.py +0 -0
  33. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_bayesian_exploration.py +0 -0
  34. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_constraints.py +0 -0
  35. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_custom_model.py +0 -0
  36. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_expected_improvement.py +0 -0
  37. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_hessian_kernel.py +0 -0
  38. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_high_level.py +0 -0
  39. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_mggpo.py +0 -0
  40. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_mobo.py +0 -0
  41. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_model_constructor.py +0 -0
  42. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_multi_fidelity.py +0 -0
  43. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_time_dependent_bo.py +0 -0
  44. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_turbo.py +0 -0
  45. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/bayesian/test_upper_confidence_bound.py +0 -0
  46. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/test_extremum_seeking.py +0 -0
  47. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/test_latin_hypercube.py +0 -0
  48. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/test_neldermead.py +0 -0
  49. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/test_random.py +0 -0
  50. {xopt-2.2.2 → xopt-2.2.3}/tests/generators/test_rcds.py +0 -0
  51. {xopt-2.2.2 → xopt-2.2.3}/tests/test_generator.py +0 -0
  52. {xopt-2.2.2 → xopt-2.2.3}/tests/test_io.py +0 -0
  53. {xopt-2.2.2 → xopt-2.2.3}/tests/test_mpi.py +0 -0
  54. {xopt-2.2.2 → xopt-2.2.3}/tests/test_numerical_optimizer.py +0 -0
  55. {xopt-2.2.2 → xopt-2.2.3}/tests/test_perf.py +0 -0
  56. {xopt-2.2.2 → xopt-2.2.3}/tests/test_pydantic.py +0 -0
  57. {xopt-2.2.2 → xopt-2.2.3}/tests/test_utils.py +0 -0
  58. {xopt-2.2.2 → xopt-2.2.3}/tests/test_xopt.py +0 -0
  59. {xopt-2.2.2 → xopt-2.2.3}/versioneer.py +0 -0
  60. {xopt-2.2.2 → xopt-2.2.3}/xopt/__init__.py +0 -0
  61. {xopt-2.2.2 → xopt-2.2.3}/xopt/asynchronous.py +0 -0
  62. {xopt-2.2.2 → xopt-2.2.3}/xopt/base.py +0 -0
  63. {xopt-2.2.2 → xopt-2.2.3}/xopt/errors.py +0 -0
  64. {xopt-2.2.2 → xopt-2.2.3}/xopt/evaluator.py +0 -0
  65. {xopt-2.2.2 → xopt-2.2.3}/xopt/generator.py +0 -0
  66. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/__init__.py +0 -0
  67. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/__init__.py +0 -0
  68. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/base_model.py +0 -0
  69. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/bax/__init__.py +0 -0
  70. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/bax/acquisition.py +0 -0
  71. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/bax/algorithms.py +0 -0
  72. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/bax_generator.py +0 -0
  73. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/__init__.py +0 -0
  74. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/constrained_acquisition.py +0 -0
  75. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/hessian_kernel.py +0 -0
  76. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/heteroskedastic.py +0 -0
  77. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/identity.py +0 -0
  78. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/log_acquisition_function.py +0 -0
  79. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/multi_fidelity.py +0 -0
  80. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/custom_botorch/proximal.py +0 -0
  81. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/models/__init__.py +0 -0
  82. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/models/prior_mean.py +0 -0
  83. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/models/standard.py +0 -0
  84. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/models/time_dependent.py +0 -0
  85. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/multi_fidelity.py +0 -0
  86. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/time_dependent.py +0 -0
  87. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/bayesian/turbo.py +0 -0
  88. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/es/__init__.py +0 -0
  89. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/es/extremumseeking.py +0 -0
  90. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/ga/__init__.py +0 -0
  91. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/ga/cnsga.py +0 -0
  92. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/ga/deap_creator.py +0 -0
  93. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/ga/deap_fitness_with_constraints.py +0 -0
  94. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/ga/fitness_with_constraints.py +0 -0
  95. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/random.py +0 -0
  96. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/rcds/__init__.py +0 -0
  97. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/rcds/rcds.py +0 -0
  98. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/scipy/__init__.py +0 -0
  99. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/scipy/latin_hypercube.py +0 -0
  100. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/scipy/neldermead.py +0 -0
  101. {xopt-2.2.2 → xopt-2.2.3}/xopt/generators/utils.py +0 -0
  102. {xopt-2.2.2 → xopt-2.2.3}/xopt/log.py +0 -0
  103. {xopt-2.2.2 → xopt-2.2.3}/xopt/mpi/__init__.py +0 -0
  104. {xopt-2.2.2 → xopt-2.2.3}/xopt/mpi/run.py +0 -0
  105. {xopt-2.2.2 → xopt-2.2.3}/xopt/numerical_optimizer.py +0 -0
  106. {xopt-2.2.2 → xopt-2.2.3}/xopt/pydantic.py +0 -0
  107. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/__init__.py +0 -0
  108. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/benchmarking.py +0 -0
  109. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/__init__.py +0 -0
  110. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/ackley_20.py +0 -0
  111. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/modified_tnk.py +0 -0
  112. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/multi_objective.py +0 -0
  113. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/problem.py +0 -0
  114. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/rosenbrock.py +0 -0
  115. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/sinusoid_1d.py +0 -0
  116. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/sphere_100.py +0 -0
  117. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/sphere_20.py +0 -0
  118. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/sphere_50.py +0 -0
  119. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/test_functions/tnk.py +0 -0
  120. {xopt-2.2.2 → xopt-2.2.3}/xopt/resources/testing.py +0 -0
  121. {xopt-2.2.2 → xopt-2.2.3}/xopt.egg-info/SOURCES.txt +0 -0
  122. {xopt-2.2.2 → xopt-2.2.3}/xopt.egg-info/dependency_links.txt +0 -0
  123. {xopt-2.2.2 → xopt-2.2.3}/xopt.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xopt
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Home-page: https://github.com/ChristopherMayes/xopt
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -2,7 +2,7 @@ deap
2
2
  numpy
3
3
  pyyaml
4
4
  torch
5
- botorch>=0.9.2
5
+ botorch>=0.9.2,<=0.10.0
6
6
  gpytorch
7
7
  pandas
8
8
  pydantic>=2.3
@@ -239,3 +239,12 @@ class TestBayesianGenerator(TestCase):
239
239
  model.models[0].input_transform.bounds,
240
240
  torch.tensor(((0, 0), (1, 10))).double(),
241
241
  )
242
+
243
+ # test bad fixed feature name
244
+ gen = BayesianGenerator(vocs=TEST_VOCS_BASE)
245
+ gen.fixed_features = {"bad_name": 3.0}
246
+ data = deepcopy(TEST_VOCS_DATA)
247
+ gen.add_data(data)
248
+
249
+ with pytest.raises(KeyError):
250
+ gen.train_model()
@@ -10,7 +10,7 @@ class TestUtils:
10
10
  def test_mobo_objective(self):
11
11
  test_vocs_copy = deepcopy(TEST_VOCS_BASE)
12
12
  test_vocs_copy.objectives["y2"] = "MAXIMIZE"
13
- obj = create_mobo_objective(test_vocs_copy, tkwargs={})
13
+ obj = create_mobo_objective(test_vocs_copy)
14
14
 
15
15
  # test large sample shape
16
16
  test_samples = torch.randn(3, 4, 5, 3).double()
@@ -3,7 +3,8 @@ from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
3
3
  import numpy as np
4
4
  import pandas as pd
5
5
 
6
- from xopt import Evaluator
6
+ from xopt import Evaluator, Xopt
7
+ from xopt.generators import RandomGenerator
7
8
  from xopt.vocs import VOCS
8
9
 
9
10
 
@@ -135,3 +136,29 @@ class TestEvaluator:
135
136
  executor = ProcessPoolExecutor(max_workers=MAX_WORKERS)
136
137
  ev = Evaluator(function=self.f, executor=executor, max_workers=MAX_WORKERS)
137
138
  ev.evaluate_data(in10)
139
+
140
+ def test_evaluate_return_types(self):
141
+ def a(input):
142
+ return {"f": 1.0}
143
+
144
+ def b(input):
145
+ return {"f": [1.0]}
146
+
147
+ def c(input):
148
+ return {"f": np.array(1.0)}
149
+
150
+ def d(input):
151
+ return {"f": [1.0, 1.0]}
152
+
153
+ def e(input):
154
+ return {"f": np.ones(3)}
155
+
156
+ vocs = VOCS(variables={"x": [0, 1]}, objectives={"f": "MINIMIZE"})
157
+
158
+ for func in [a, b, c, d, e]:
159
+ evaluator = Evaluator(function=func)
160
+ generator = RandomGenerator(vocs=vocs)
161
+
162
+ X = Xopt(evaluator=evaluator, generator=generator, vocs=vocs)
163
+
164
+ X.random_evaluate(5)
@@ -27,6 +27,14 @@ class TestVOCS(object):
27
27
  variables={"x": [0, 1]},
28
28
  )
29
29
 
30
+ def test_output_names(self):
31
+ test_vocs = VOCS(
32
+ variables={"x": [0, 1]},
33
+ objectives={"y1": "MINIMIZE"},
34
+ constraints={"c1": ["GREATER_THAN", 0], "c2": ["LESS_THAN", 0]},
35
+ )
36
+ assert test_vocs.output_names == ["y1", "c1", "c2"]
37
+
30
38
  def test_constraint_specification(self):
31
39
  good_constraint_list = [
32
40
  ["LESS_THAN", 0],
@@ -234,6 +242,14 @@ class TestVOCS(object):
234
242
  assert idx == 1
235
243
  assert val == 0.1
236
244
 
245
+ # test error handling
246
+ with pytest.raises(RuntimeError):
247
+ vocs.select_best(pd.DataFrame())
248
+
249
+ vocs.constraints = {"c1": ["GREATER_THAN", 10.5]}
250
+ with pytest.raises(RuntimeError):
251
+ vocs.select_best(pd.DataFrame())
252
+
237
253
  @pytest.mark.filterwarnings("ignore: All-NaN axis encountered")
238
254
  def test_cumulative_optimum(self):
239
255
  vocs = deepcopy(TEST_VOCS_BASE)
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-04-15T14:51:35-0400",
11
+ "date": "2024-05-07T12:53:58-0400",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "a613b00abdb1ff1809a10b82096947803834cec5",
15
- "version": "2.2.2"
14
+ "full-revisionid": "2a01895f05a19d2a26b527c4c47baaf099cd0110",
15
+ "version": "2.2.3"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -13,7 +13,6 @@ from xopt.generators.bayesian.bayesian_generator import (
13
13
  BayesianGenerator,
14
14
  formatted_base_docstring,
15
15
  )
16
- from xopt.generators.bayesian.objectives import create_exploration_objective
17
16
 
18
17
 
19
18
  class BayesianExplorationGenerator(BayesianGenerator):
@@ -38,12 +37,6 @@ class BayesianExplorationGenerator(BayesianGenerator):
38
37
 
39
38
  return qPV
40
39
 
41
- def _get_objective(self):
42
- """return exploration objective, which only captures the output of the first
43
- model output"""
44
-
45
- return create_exploration_objective(self.vocs, self._tkwargs)
46
-
47
40
 
48
41
  class qPosteriorVariance(MCAcquisitionFunction):
49
42
  def __init__(
@@ -375,6 +375,11 @@ class BayesianGenerator(Generator, ABC):
375
375
  # get bounds for each fixed_feature (vocs bounds take precedent)
376
376
  for key in self.fixed_features:
377
377
  if key not in variable_bounds:
378
+ if key not in data:
379
+ raise KeyError(
380
+ "generator data needs to contain fixed feature "
381
+ f"column name `{key}`"
382
+ )
378
383
  f_data = data[key]
379
384
  bounds = [f_data.min(), f_data.max()]
380
385
  if bounds[1] - bounds[0] < 1e-8:
@@ -545,11 +550,20 @@ class BayesianGenerator(Generator, ABC):
545
550
  pass
546
551
 
547
552
  def _get_objective(self):
548
- """return default objective (scalar objective) determined by vocs"""
553
+ """
554
+ return default objective (scalar objective) determined by vocs
555
+
556
+ If objectives are specified the returned function will weight model
557
+ by +/- 1.0 according to MAXIMIZE/MINIMIZE keys in vocs.
558
+
559
+ If no objectives are specified, the returned function will weight observable
560
+ models by +1.0. This is used in Bayesian exploration.
561
+
562
+ """
549
563
  return create_mc_objective(self.vocs, self._tkwargs)
550
564
 
551
565
  def _get_constraint_callables(self):
552
- """return default objective (scalar objective) determined by vocs"""
566
+ """return constratint callable determined by vocs"""
553
567
  constraint_callables = create_constraint_callables(self.vocs)
554
568
  if len(constraint_callables) == 0:
555
569
  constraint_callables = None
@@ -755,7 +769,7 @@ class MultiObjectiveBayesianGenerator(BayesianGenerator, ABC):
755
769
 
756
770
  n_objectives = self.vocs.n_objectives
757
771
  weights = torch.zeros(n_objectives)
758
- weights = set_botorch_weights(weights, self.vocs)
772
+ weights = set_botorch_weights(self.vocs).to(**self._tkwargs)
759
773
  objective_data = objective_data * weights
760
774
 
761
775
  # compute hypervolume
@@ -36,8 +36,7 @@ class ExpectedImprovementGenerator(BayesianGenerator):
36
36
  )
37
37
  else:
38
38
  # analytic acquisition function for single candidate generation
39
- weights = torch.zeros(self.vocs.n_outputs).to(**self._tkwargs)
40
- weights = set_botorch_weights(weights, self.vocs)
39
+ weights = set_botorch_weights(self.vocs).to(**self._tkwargs)
41
40
  posterior_transform = ScalarizedPosteriorTransform(weights)
42
41
  acq = ExpectedImprovement(
43
42
  model, best_f=best_f, posterior_transform=posterior_transform
@@ -61,7 +61,7 @@ class MGGPOGenerator(MultiObjectiveBayesianGenerator):
61
61
  return acq
62
62
 
63
63
  def _get_objective(self):
64
- return create_mobo_objective(self.vocs, self._tkwargs)
64
+ return create_mobo_objective(self.vocs)
65
65
 
66
66
  def _get_acquisition(self, model):
67
67
  # get reference point from data
@@ -15,7 +15,7 @@ class MOBOGenerator(MultiObjectiveBayesianGenerator):
15
15
  Hypervolume Improvement acquisition function"""
16
16
 
17
17
  def _get_objective(self):
18
- return create_mobo_objective(self.vocs, self._tkwargs)
18
+ return create_mobo_objective(self.vocs)
19
19
 
20
20
  def get_acquisition(self, model):
21
21
  """
@@ -41,7 +41,7 @@ def constraint_function(Z, vocs, name):
41
41
 
42
42
  def create_constraint_callables(vocs):
43
43
  if vocs.constraints is not None:
44
- constraint_names = list(vocs.constraints.keys())
44
+ constraint_names = vocs.constraint_names
45
45
  constraint_callables = []
46
46
  for name in constraint_names:
47
47
  constraint_callables += [
@@ -62,10 +62,7 @@ def create_mc_objective(vocs, tkwargs):
62
62
  create the objective object
63
63
 
64
64
  """
65
- n_outputs = vocs.n_outputs
66
- weights = torch.zeros(n_outputs).to(**tkwargs)
67
-
68
- weights = set_botorch_weights(weights, vocs)
65
+ weights = set_botorch_weights(vocs)
69
66
 
70
67
  def obj_callable(Z, X=None):
71
68
  return torch.matmul(Z, weights.reshape(-1, 1)).squeeze(-1)
@@ -73,26 +70,15 @@ def create_mc_objective(vocs, tkwargs):
73
70
  return GenericMCObjective(obj_callable)
74
71
 
75
72
 
76
- def create_exploration_objective(vocs, tkwargs):
77
- n_outputs = vocs.n_outputs
78
- weights = torch.zeros(n_outputs).to(**tkwargs)
79
- weights[0] = 1.0
80
-
81
- def obj_callable(Z, X=None):
82
- return torch.matmul(Z, weights.reshape(-1, 1)).squeeze(-1)
83
-
84
- return GenericMCObjective(obj_callable)
85
-
86
-
87
- def create_mobo_objective(vocs, tkwargs):
73
+ def create_mobo_objective(vocs):
88
74
  """
89
75
  botorch assumes maximization so we need to negate any objectives that have
90
76
  minimize keyword and zero out anything that is a constraint
91
77
  """
92
- n_objectives = vocs.n_objectives
93
- weights = torch.zeros(n_objectives).to(**tkwargs)
94
- weights = set_botorch_weights(weights, vocs)
78
+ output_names = vocs.output_names
79
+ objective_indicies = [output_names.index(name) for name in vocs.objectives]
80
+ weights = set_botorch_weights(vocs)[objective_indicies]
95
81
 
96
82
  return WeightedMCMultiOutputObjective(
97
- weights, outcomes=list(range(vocs.n_objectives)), num_outcomes=vocs.n_objectives
83
+ weights, outcomes=objective_indicies, num_outcomes=vocs.n_objectives
98
84
  )
@@ -1,6 +1,5 @@
1
1
  import warnings
2
2
 
3
- import torch
4
3
  from botorch.acquisition import (
5
4
  qUpperConfidenceBound,
6
5
  ScalarizedPosteriorTransform,
@@ -62,8 +61,7 @@ beta : float, default 2.0
62
61
  )
63
62
  else:
64
63
  # analytic acquisition function for single candidate generation
65
- weights = torch.zeros(self.vocs.n_outputs).to(**self._tkwargs)
66
- weights = set_botorch_weights(weights, self.vocs)
64
+ weights = set_botorch_weights(self.vocs)
67
65
  posterior_transform = ScalarizedPosteriorTransform(weights)
68
66
  acq = UpperConfidenceBound(
69
67
  model, beta=self.beta, posterior_transform=posterior_transform
@@ -65,13 +65,26 @@ def get_training_data(
65
65
  return train_X, train_Y, train_Yvar
66
66
 
67
67
 
68
- def set_botorch_weights(weights, vocs: VOCS):
69
- """set weights to multiply xopt objectives for botorch objectives"""
70
- for idx, ele in enumerate(vocs.objective_names):
71
- if vocs.objectives[ele] == "MINIMIZE":
72
- weights[idx] = -1.0
73
- elif vocs.objectives[ele] == "MAXIMIZE":
74
- weights[idx] = 1.0
68
+ def set_botorch_weights(vocs: VOCS):
69
+ """set weights to multiply xopt objectives or observables for botorch objectives"""
70
+ output_names = vocs.output_names
71
+
72
+ weights = torch.zeros(len(output_names), dtype=torch.double)
73
+
74
+ if vocs.n_objectives > 0:
75
+ # if objectives exist this is an optimization problem
76
+ # set weights according to the index of the models -- corresponds to the
77
+ # ordering of output names
78
+ for objective_name in vocs.objective_names:
79
+ if vocs.objectives[objective_name] == "MINIMIZE":
80
+ weights[output_names.index(objective_name)] = -1.0
81
+ elif vocs.objectives[objective_name] == "MAXIMIZE":
82
+ weights[output_names.index(objective_name)] = 1.0
83
+ if vocs.n_objectives == 0:
84
+ # if no objectives exist this may be an exploration problem, weight each
85
+ # observable by 1.0
86
+ for observable_name in vocs.observables:
87
+ weights[output_names.index(observable_name)] = 1.0
75
88
 
76
89
  return weights
77
90