9
9
from contextlib import ExitStack
10
10
from typing import Any , cast
11
11
from unittest import mock
12
+ from warnings import catch_warnings , simplefilter
12
13
13
14
import numpy as np
14
15
import torch
15
16
from ax .core .search_space import SearchSpaceDigest
16
17
from ax .models .torch .botorch_defaults import NO_OBSERVED_POINTS_MESSAGE
18
+ from ax .models .torch .botorch_modular .model import BoTorchModel
17
19
from ax .models .torch .botorch_moo import MultiObjectiveBotorchModel
18
20
from ax .models .torch .botorch_moo_defaults import (
19
21
get_outcome_constraint_transforms ,
24
26
pareto_frontier_evaluator ,
25
27
)
26
28
from ax .models .torch .utils import _get_X_pending_and_observed
29
+ from ax .models .torch_base import TorchModel
27
30
from ax .utils .common .random import with_rng_seed
28
31
from ax .utils .common .testutils import TestCase
32
+ from ax .utils .testing .mock import mock_botorch_optimize_context_manager
29
33
from botorch .models .gp_regression import SingleTaskGP
30
34
from botorch .utils .datasets import SupervisedDataset
31
35
from botorch .utils .multi_objective .hypervolume import infer_reference_point
32
36
from botorch .utils .testing import MockModel , MockPosterior
37
+ from gpytorch .utils .warnings import NumericalWarning
33
38
from torch ._tensor import Tensor
34
39
35
40
42
47
FIT_MODEL_MO_PATH = "ax.models.torch.botorch_defaults.fit_gpytorch_mll"
43
48
44
49
45
- # pyre-fixme[2]: Parameter must be annotated.
46
- def dummy_predict (model , X ) -> tuple [Tensor , Tensor ]:
47
- # Add column to X that is a product of previous elements.
48
- mean = torch .cat ([X , torch .prod (X , dim = 1 ).reshape (- 1 , 1 )], dim = 1 )
49
- cov = torch .zeros (mean .shape [0 ], mean .shape [1 ], mean .shape [1 ])
50
- return mean , cov
50
+ def _fit_model (
51
+ model : TorchModel , X : torch .Tensor , Y : torch .Tensor , Yvar : torch .Tensor
52
+ ) -> None :
53
+ bounds = [(0.0 , 4.0 ), (0.0 , 4.0 )]
54
+ datasets = [
55
+ SupervisedDataset (
56
+ X = X ,
57
+ Y = Y ,
58
+ Yvar = Yvar ,
59
+ feature_names = ["x1" , "x2" ],
60
+ outcome_names = ["a" , "b" , "c" ],
61
+ )
62
+ ]
63
+ search_space_digest = SearchSpaceDigest (feature_names = ["x1" , "x2" ], bounds = bounds )
64
+ with mock_botorch_optimize_context_manager (), catch_warnings ():
65
+ simplefilter (action = "ignore" , category = NumericalWarning )
66
+ model .fit (datasets = datasets , search_space_digest = search_space_digest )
51
67
52
68
53
69
class FrontierEvaluatorTest (TestCase ):
@@ -66,45 +82,24 @@ def setUp(self) -> None:
66
82
]
67
83
)
68
84
self .Yvar = torch .zeros (5 , 3 )
69
- self .outcome_constraints = (
70
- torch .tensor ([[0.0 , 0.0 , 1.0 ]]),
71
- torch .tensor ([[3.5 ]]),
72
- )
73
85
self .objective_thresholds = torch .tensor ([0.5 , 1.5 ])
74
86
self .objective_weights = torch .tensor ([1.0 , 1.0 ])
75
- bounds = [(0.0 , 4.0 ), (0.0 , 4.0 )]
76
- self .model = MultiObjectiveBotorchModel (model_predictor = dummy_predict )
77
- with mock .patch (FIT_MODEL_MO_PATH ) as _mock_fit_model :
78
- self .model .fit (
79
- datasets = [
80
- SupervisedDataset (
81
- X = self .X ,
82
- Y = self .Y ,
83
- Yvar = self .Yvar ,
84
- feature_names = ["x1" , "x2" ],
85
- outcome_names = ["a" , "b" , "c" ],
86
- )
87
- ],
88
- search_space_digest = SearchSpaceDigest (
89
- feature_names = ["x1" , "x2" ],
90
- bounds = bounds ,
91
- ),
92
- )
93
- _mock_fit_model .assert_called_once ()
94
87
95
88
def test_pareto_frontier_raise_error_when_missing_data (self ) -> None :
96
89
with self .assertRaises (ValueError ):
97
90
pareto_frontier_evaluator (
98
- model = self . model ,
91
+ model = MultiObjectiveBotorchModel () ,
99
92
objective_thresholds = self .objective_thresholds ,
100
93
objective_weights = self .objective_weights ,
101
94
Yvar = self .Yvar ,
102
95
)
103
96
104
97
def test_pareto_frontier_evaluator_raw (self ) -> None :
98
+ model = BoTorchModel ()
99
+ _fit_model (model = model , X = self .X , Y = self .Y , Yvar = self .Yvar )
105
100
Yvar = torch .diag_embed (self .Yvar )
106
101
Y , cov , indx = pareto_frontier_evaluator (
107
- model = self . model ,
102
+ model = model ,
108
103
objective_weights = self .objective_weights ,
109
104
objective_thresholds = self .objective_thresholds ,
110
105
Y = self .Y ,
@@ -118,7 +113,7 @@ def test_pareto_frontier_evaluator_raw(self) -> None:
118
113
119
114
# Omit objective_thresholds
120
115
Y , cov , indx = pareto_frontier_evaluator (
121
- model = self . model ,
116
+ model = model ,
122
117
objective_weights = self .objective_weights ,
123
118
Y = self .Y ,
124
119
Yvar = Yvar ,
@@ -131,7 +126,7 @@ def test_pareto_frontier_evaluator_raw(self) -> None:
131
126
132
127
# Change objective_weights so goal is to minimize b
133
128
Y , cov , indx = pareto_frontier_evaluator (
134
- model = self . model ,
129
+ model = model ,
135
130
objective_weights = torch .tensor ([1.0 , - 1.0 ]),
136
131
objective_thresholds = self .objective_thresholds ,
137
132
Y = self .Y ,
@@ -146,7 +141,7 @@ def test_pareto_frontier_evaluator_raw(self) -> None:
146
141
147
142
# test no points better than reference point
148
143
Y , cov , indx = pareto_frontier_evaluator (
149
- model = self . model ,
144
+ model = model ,
150
145
objective_weights = self .objective_weights ,
151
146
objective_thresholds = torch .full_like (self .objective_thresholds , 100.0 ),
152
147
Y = self .Y ,
@@ -157,8 +152,25 @@ def test_pareto_frontier_evaluator_raw(self) -> None:
157
152
self .assertTrue (torch .equal (torch .tensor ([], dtype = torch .long ), indx ))
158
153
159
154
def test_pareto_frontier_evaluator_predict (self ) -> None :
160
- Y , cov , indx = pareto_frontier_evaluator (
161
- model = self .model ,
155
+ def dummy_predict (
156
+ model : MultiObjectiveBotorchModel ,
157
+ X : Tensor ,
158
+ use_posterior_predictive : bool = False ,
159
+ ) -> tuple [Tensor , Tensor ]:
160
+ # Add column to X that is a product of previous elements.
161
+ mean = torch .cat ([X , torch .prod (X , dim = 1 ).reshape (- 1 , 1 )], dim = 1 )
162
+ cov = torch .zeros (mean .shape [0 ], mean .shape [1 ], mean .shape [1 ])
163
+ return mean , cov
164
+
165
+ # pyre-fixme: Incompatible parameter type [6]: In call
166
+ # `MultiObjectiveBotorchModel.__init__`, for argument `model_predictor`,
167
+ # expected `typing.Callable[[Model, Tensor, bool], Tuple[Tensor,
168
+ # Tensor]]` but got named arguments
169
+ model = MultiObjectiveBotorchModel (model_predictor = dummy_predict )
170
+ _fit_model (model = model , X = self .X , Y = self .Y , Yvar = self .Yvar )
171
+
172
+ Y , _ , indx = pareto_frontier_evaluator (
173
+ model = model ,
162
174
objective_weights = self .objective_weights ,
163
175
objective_thresholds = self .objective_thresholds ,
164
176
X = self .X ,
@@ -170,13 +182,17 @@ def test_pareto_frontier_evaluator_predict(self) -> None:
170
182
self .assertTrue (torch .equal (torch .arange (2 , 4 ), indx ))
171
183
172
184
def test_pareto_frontier_evaluator_with_outcome_constraints (self ) -> None :
173
- Y , cov , indx = pareto_frontier_evaluator (
174
- model = self .model ,
185
+ model = MultiObjectiveBotorchModel ()
186
+ Y , _ , indx = pareto_frontier_evaluator (
187
+ model = model ,
175
188
objective_weights = self .objective_weights ,
176
189
objective_thresholds = self .objective_thresholds ,
177
190
Y = self .Y ,
178
191
Yvar = self .Yvar ,
179
- outcome_constraints = self .outcome_constraints ,
192
+ outcome_constraints = (
193
+ torch .tensor ([[0.0 , 0.0 , 1.0 ]]),
194
+ torch .tensor ([[3.5 ]]),
195
+ ),
180
196
)
181
197
pred = self .Y [2 , :]
182
198
self .assertTrue (
0 commit comments