Skip to content

BAX Algorithms

Algorithm

Bases: XoptBaseModel, ABC

Base class for algorithms used in BAX.

Attributes:

Name Type Description
name ClassVar[str]

The name of the algorithm.

n_samples PositiveInt

Number of execution paths to generate.

Methods:

Name Description
get_execution_paths

Get execution paths for the algorithm.

evaluate_virtual_objective

Evaluate the virtual objective at the given inputs.

Source code in xopt/generators/bayesian/bax/algorithms.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class Algorithm(XoptBaseModel, ABC):
    """
    Base class for algorithms used in BAX.

    Attributes
    ----------
    name : ClassVar[str]
        The name of the algorithm.
    n_samples : PositiveInt
        Number of execution paths to generate.

    Methods
    -------
    get_execution_paths(self, model: Model, bounds: Tensor) -> Tuple[Tensor, Tensor, Dict]
        Get execution paths for the algorithm.
    evaluate_virtual_objective(self, model: Model, x: Tensor, bounds: Tensor, n_samples: int, tkwargs: dict = None) -> Tensor
        Evaluate the virtual objective at the given inputs.
    """

    name: ClassVar[str] = "base_algorithm"
    n_samples: PositiveInt = Field(
        default=20, description="number of execution paths to generate"
    )

    @abstractmethod
    def get_execution_paths(
        self, model: Model, bounds: Tensor
    ) -> Tuple[Tensor, Tensor, Dict]:
        """
        Get execution paths for the algorithm.

        Parameters
        ----------
        model : Model
            The model to use for generating execution paths.
        bounds : Tensor
            The bounds for the optimization.

        Returns
        -------
        Tuple[Tensor, Tensor, Dict]
            The execution paths, their corresponding values, and additional results.
        """
        pass

    @abstractmethod
    def evaluate_virtual_objective(
        self,
        model: Model,
        x: Tensor,
        bounds: Tensor,
        n_samples: int,
        tkwargs: dict = None,
    ) -> Tensor:
        """
        Evaluate the virtual objective at the given inputs.

        Parameters
        ----------
        model : Model
            The model to use for evaluating the virtual objective.
        x : Tensor
            The inputs at which to evaluate the virtual objective.
        bounds : Tensor
            The bounds for the optimization.
        n_samples : int
            The number of samples to generate.
        tkwargs : dict, optional
            Additional keyword arguments for the evaluation.

        Returns
        -------
        Tensor
            The evaluated virtual objective values.
        """
        pass  # pragma: no cover

evaluate_virtual_objective(model, x, bounds, n_samples, tkwargs=None) abstractmethod

Evaluate the virtual objective at the given inputs.

Parameters:

Name Type Description Default
model Model

The model to use for evaluating the virtual objective.

required
x Tensor

The inputs at which to evaluate the virtual objective.

required
bounds Tensor

The bounds for the optimization.

required
n_samples int

The number of samples to generate.

required
tkwargs dict

Additional keyword arguments for the evaluation.

None

Returns:

Type Description
Tensor

The evaluated virtual objective values.

Source code in xopt/generators/bayesian/bax/algorithms.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@abstractmethod
def evaluate_virtual_objective(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> Tensor:
    """
    Evaluate the virtual objective at the given inputs.

    Parameters
    ----------
    model : Model
        The model to use for evaluating the virtual objective.
    x : Tensor
        The inputs at which to evaluate the virtual objective.
    bounds : Tensor
        The bounds for the optimization.
    n_samples : int
        The number of samples to generate.
    tkwargs : dict, optional
        Additional keyword arguments for the evaluation.

    Returns
    -------
    Tensor
        The evaluated virtual objective values.
    """
    pass  # pragma: no cover

get_execution_paths(model, bounds) abstractmethod

Get execution paths for the algorithm.

Parameters:

Name Type Description Default
model Model

The model to use for generating execution paths.

required
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tuple[Tensor, Tensor, Dict]

The execution paths, their corresponding values, and additional results.

Source code in xopt/generators/bayesian/bax/algorithms.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@abstractmethod
def get_execution_paths(
    self, model: Model, bounds: Tensor
) -> Tuple[Tensor, Tensor, Dict]:
    """
    Get execution paths for the algorithm.

    Parameters
    ----------
    model : Model
        The model to use for generating execution paths.
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tuple[Tensor, Tensor, Dict]
        The execution paths, their corresponding values, and additional results.
    """
    pass

yaml(**kwargs)

serialize first then dump to yaml string

Source code in xopt/pydantic.py
231
232
233
234
235
236
237
238
def yaml(self, **kwargs):
    """serialize first then dump to yaml string"""
    output = json.loads(
        self.to_json(
            **kwargs,
        )
    )
    return yaml.dump(output)

GridScanAlgorithm

Bases: Algorithm, ABC

Grid scan algorithm for BAX.

Attributes:

Name Type Description
name str

The name of the algorithm.

n_mesh_points PositiveInt

Number of mesh points along each axis.

Methods:

Name Description
create_mesh

Create a mesh for evaluating posteriors on.

Source code in xopt/generators/bayesian/bax/algorithms.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
class GridScanAlgorithm(Algorithm, ABC):
    """
    Grid scan algorithm for BAX.

    Attributes
    ----------
    name : str
        The name of the algorithm.
    n_mesh_points : PositiveInt
        Number of mesh points along each axis.

    Methods
    -------
    create_mesh(self, bounds: Tensor) -> Tensor
        Create a mesh for evaluating posteriors on.
    """

    name = "grid_scan_algorithm"
    n_mesh_points: PositiveInt = Field(
        default=10, description="number of mesh points along each axis"
    )

    def create_mesh(self, bounds: Tensor) -> Tensor:
        """
        Create a mesh for evaluating posteriors on.

        Parameters
        ----------
        bounds : Tensor
            The bounds for the optimization.

        Returns
        -------
        Tensor
            The mesh points.

        Raises
        ------
        ValueError
            If the bounds do not have the shape [2, ndim].
        """
        if len(bounds) != 2:
            raise ValueError("bounds must have the shape [2, ndim]")

        dim = len(bounds[0])
        linspace_list = [
            torch.linspace(bounds.T[i][0], bounds.T[i][1], self.n_mesh_points)
            for i in range(dim)
        ]

        xx = torch.meshgrid(*linspace_list, indexing="ij")
        mesh_pts = torch.stack(xx).flatten(start_dim=1).T

        return mesh_pts

create_mesh(bounds)

Create a mesh for evaluating posteriors on.

Parameters:

Name Type Description Default
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tensor

The mesh points.

Raises:

Type Description
ValueError

If the bounds do not have the shape [2, ndim].

Source code in xopt/generators/bayesian/bax/algorithms.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def create_mesh(self, bounds: Tensor) -> Tensor:
    """
    Create a mesh for evaluating posteriors on.

    Parameters
    ----------
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tensor
        The mesh points.

    Raises
    ------
    ValueError
        If the bounds do not have the shape [2, ndim].
    """
    if len(bounds) != 2:
        raise ValueError("bounds must have the shape [2, ndim]")

    dim = len(bounds[0])
    linspace_list = [
        torch.linspace(bounds.T[i][0], bounds.T[i][1], self.n_mesh_points)
        for i in range(dim)
    ]

    xx = torch.meshgrid(*linspace_list, indexing="ij")
    mesh_pts = torch.stack(xx).flatten(start_dim=1).T

    return mesh_pts

evaluate_virtual_objective(model, x, bounds, n_samples, tkwargs=None) abstractmethod

Evaluate the virtual objective at the given inputs.

Parameters:

Name Type Description Default
model Model

The model to use for evaluating the virtual objective.

required
x Tensor

The inputs at which to evaluate the virtual objective.

required
bounds Tensor

The bounds for the optimization.

required
n_samples int

The number of samples to generate.

required
tkwargs dict

Additional keyword arguments for the evaluation.

None

Returns:

Type Description
Tensor

The evaluated virtual objective values.

Source code in xopt/generators/bayesian/bax/algorithms.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
@abstractmethod
def evaluate_virtual_objective(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> Tensor:
    """
    Evaluate the virtual objective at the given inputs.

    Parameters
    ----------
    model : Model
        The model to use for evaluating the virtual objective.
    x : Tensor
        The inputs at which to evaluate the virtual objective.
    bounds : Tensor
        The bounds for the optimization.
    n_samples : int
        The number of samples to generate.
    tkwargs : dict, optional
        Additional keyword arguments for the evaluation.

    Returns
    -------
    Tensor
        The evaluated virtual objective values.
    """
    pass  # pragma: no cover

get_execution_paths(model, bounds) abstractmethod

Get execution paths for the algorithm.

Parameters:

Name Type Description Default
model Model

The model to use for generating execution paths.

required
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tuple[Tensor, Tensor, Dict]

The execution paths, their corresponding values, and additional results.

Source code in xopt/generators/bayesian/bax/algorithms.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@abstractmethod
def get_execution_paths(
    self, model: Model, bounds: Tensor
) -> Tuple[Tensor, Tensor, Dict]:
    """
    Get execution paths for the algorithm.

    Parameters
    ----------
    model : Model
        The model to use for generating execution paths.
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tuple[Tensor, Tensor, Dict]
        The execution paths, their corresponding values, and additional results.
    """
    pass

yaml(**kwargs)

serialize first then dump to yaml string

Source code in xopt/pydantic.py
231
232
233
234
235
236
237
238
def yaml(self, **kwargs):
    """serialize first then dump to yaml string"""
    output = json.loads(
        self.to_json(
            **kwargs,
        )
    )
    return yaml.dump(output)

GridOptimize

Bases: GridScanAlgorithm

Grid optimization algorithm for BAX.

Attributes:

Name Type Description
observable_names_ordered List[str]

Names of observable/objective models used in this algorithm.

minimize bool

Whether to minimize the objective function.

Methods:

Name Description
get_execution_paths

Get execution paths that minimize the objective function.

evaluate_virtual_objective

Evaluate the virtual objective (samples).

Source code in xopt/generators/bayesian/bax/algorithms.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
class GridOptimize(GridScanAlgorithm):
    """
    Grid optimization algorithm for BAX.

    Attributes
    ----------
    observable_names_ordered : List[str]
        Names of observable/objective models used in this algorithm.
    minimize : bool
        Whether to minimize the objective function.

    Methods
    -------
    get_execution_paths(self, model: Model, bounds: Tensor) -> Tuple[Tensor, Tensor, Dict]
        Get execution paths that minimize the objective function.
    evaluate_virtual_objective(self, model: Model, x: Tensor, bounds: Tensor, n_samples: int, tkwargs: dict = None) -> Tensor
        Evaluate the virtual objective (samples).
    """

    observable_names_ordered: List[str] = Field(
        default=["y1"],
        description="names of observable/objective models used in this algorithm",
    )
    minimize: bool = True

    def get_execution_paths(
        self, model: Model, bounds: Tensor
    ) -> Tuple[Tensor, Tensor, Dict]:
        """
        Get execution paths that minimize the objective function.

        Parameters
        ----------
        model : Model
            The model to use for generating execution paths.
        bounds : Tensor
            The bounds for the optimization.

        Returns
        -------
        Tuple[Tensor, Tensor, Dict]
            The execution paths, their corresponding values, and additional results.
        """
        # build evaluation mesh
        test_points = self.create_mesh(bounds)
        if isinstance(model, ModelList):
            test_points = test_points.to(model.models[0].train_targets)
        else:
            test_points = test_points.to(model.train_targets)

        # get samples of the model posterior at mesh points
        posterior_samples = self.evaluate_virtual_objective(
            model, test_points, bounds, self.n_samples
        )

        # get points that minimize each sample (execution paths)
        if self.minimize:
            y_opt, opt_idx = torch.min(posterior_samples, dim=-2)
        else:
            y_opt, opt_idx = torch.max(posterior_samples, dim=-2)

        opt_idx = opt_idx.squeeze(dim=[-1])
        x_opt = test_points[opt_idx]

        # get the solution_center and solution_entropy for Turbo
        # note: the entropy calc here drops a constant scaling factor
        solution_center = x_opt.mean(dim=0).numpy()
        solution_entropy = float(torch.log(x_opt.std(dim=0) ** 2).sum())

        # collect secondary results in a dict
        results_dict = {
            "test_points": test_points,
            "posterior_samples": posterior_samples,
            "execution_paths": torch.hstack((x_opt, y_opt)),
            "solution_center": solution_center,
            "solution_entropy": solution_entropy,
        }

        # return execution paths
        return x_opt.unsqueeze(-2), y_opt.unsqueeze(-2), results_dict

    def evaluate_virtual_objective(
        self,
        model: Model,
        x: Tensor,
        bounds: Tensor,
        n_samples: int,
        tkwargs: dict = None,
    ) -> Tensor:
        """
        Evaluate the virtual objective (samples).

        Parameters
        ----------
        model : Model
            The model to use for evaluating the virtual objective.
        x : Tensor
            The inputs at which to evaluate the virtual objective.
        bounds : Tensor
            The bounds for the optimization.
        n_samples : int
            The number of samples to generate.
        tkwargs : dict, optional
            Additional keyword arguments for the evaluation.

        Returns
        -------
        Tensor
            The evaluated virtual objective values.
        """
        # get samples of the model posterior at inputs given by x
        with torch.no_grad():
            post = model.posterior(x)
            objective_values = post.rsample(torch.Size([n_samples]))

        return objective_values

create_mesh(bounds)

Create a mesh for evaluating posteriors on.

Parameters:

Name Type Description Default
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tensor

The mesh points.

Raises:

Type Description
ValueError

If the bounds do not have the shape [2, ndim].

Source code in xopt/generators/bayesian/bax/algorithms.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def create_mesh(self, bounds: Tensor) -> Tensor:
    """
    Create a mesh for evaluating posteriors on.

    Parameters
    ----------
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tensor
        The mesh points.

    Raises
    ------
    ValueError
        If the bounds do not have the shape [2, ndim].
    """
    if len(bounds) != 2:
        raise ValueError("bounds must have the shape [2, ndim]")

    dim = len(bounds[0])
    linspace_list = [
        torch.linspace(bounds.T[i][0], bounds.T[i][1], self.n_mesh_points)
        for i in range(dim)
    ]

    xx = torch.meshgrid(*linspace_list, indexing="ij")
    mesh_pts = torch.stack(xx).flatten(start_dim=1).T

    return mesh_pts

evaluate_virtual_objective(model, x, bounds, n_samples, tkwargs=None)

Evaluate the virtual objective (samples).

Parameters:

Name Type Description Default
model Model

The model to use for evaluating the virtual objective.

required
x Tensor

The inputs at which to evaluate the virtual objective.

required
bounds Tensor

The bounds for the optimization.

required
n_samples int

The number of samples to generate.

required
tkwargs dict

Additional keyword arguments for the evaluation.

None

Returns:

Type Description
Tensor

The evaluated virtual objective values.

Source code in xopt/generators/bayesian/bax/algorithms.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
def evaluate_virtual_objective(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> Tensor:
    """
    Evaluate the virtual objective (samples).

    Parameters
    ----------
    model : Model
        The model to use for evaluating the virtual objective.
    x : Tensor
        The inputs at which to evaluate the virtual objective.
    bounds : Tensor
        The bounds for the optimization.
    n_samples : int
        The number of samples to generate.
    tkwargs : dict, optional
        Additional keyword arguments for the evaluation.

    Returns
    -------
    Tensor
        The evaluated virtual objective values.
    """
    # get samples of the model posterior at inputs given by x
    with torch.no_grad():
        post = model.posterior(x)
        objective_values = post.rsample(torch.Size([n_samples]))

    return objective_values

get_execution_paths(model, bounds)

Get execution paths that minimize the objective function.

Parameters:

Name Type Description Default
model Model

The model to use for generating execution paths.

required
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tuple[Tensor, Tensor, Dict]

The execution paths, their corresponding values, and additional results.

Source code in xopt/generators/bayesian/bax/algorithms.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def get_execution_paths(
    self, model: Model, bounds: Tensor
) -> Tuple[Tensor, Tensor, Dict]:
    """
    Get execution paths that minimize the objective function.

    Parameters
    ----------
    model : Model
        The model to use for generating execution paths.
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tuple[Tensor, Tensor, Dict]
        The execution paths, their corresponding values, and additional results.
    """
    # build evaluation mesh
    test_points = self.create_mesh(bounds)
    if isinstance(model, ModelList):
        test_points = test_points.to(model.models[0].train_targets)
    else:
        test_points = test_points.to(model.train_targets)

    # get samples of the model posterior at mesh points
    posterior_samples = self.evaluate_virtual_objective(
        model, test_points, bounds, self.n_samples
    )

    # get points that minimize each sample (execution paths)
    if self.minimize:
        y_opt, opt_idx = torch.min(posterior_samples, dim=-2)
    else:
        y_opt, opt_idx = torch.max(posterior_samples, dim=-2)

    opt_idx = opt_idx.squeeze(dim=[-1])
    x_opt = test_points[opt_idx]

    # get the solution_center and solution_entropy for Turbo
    # note: the entropy calc here drops a constant scaling factor
    solution_center = x_opt.mean(dim=0).numpy()
    solution_entropy = float(torch.log(x_opt.std(dim=0) ** 2).sum())

    # collect secondary results in a dict
    results_dict = {
        "test_points": test_points,
        "posterior_samples": posterior_samples,
        "execution_paths": torch.hstack((x_opt, y_opt)),
        "solution_center": solution_center,
        "solution_entropy": solution_entropy,
    }

    # return execution paths
    return x_opt.unsqueeze(-2), y_opt.unsqueeze(-2), results_dict

yaml(**kwargs)

serialize first then dump to yaml string

Source code in xopt/pydantic.py
231
232
233
234
235
236
237
238
def yaml(self, **kwargs):
    """serialize first then dump to yaml string"""
    output = json.loads(
        self.to_json(
            **kwargs,
        )
    )
    return yaml.dump(output)

CurvatureGridOptimize

Bases: GridOptimize

Curvature grid optimization algorithm for BAX.

Attributes:

Name Type Description
use_mean bool

Whether to use the mean of the posterior distribution.

Methods:

Name Description
evaluate_virtual_objective

Evaluate the virtual objective (samples) with curvature.

Source code in xopt/generators/bayesian/bax/algorithms.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
class CurvatureGridOptimize(GridOptimize):
    """
    Curvature grid optimization algorithm for BAX.

    Attributes
    ----------
    use_mean : bool
        Whether to use the mean of the posterior distribution.

    Methods
    -------
    evaluate_virtual_objective(self, model: Model, x: Tensor, bounds: Tensor, n_samples: int, tkwargs: dict = None) -> Tensor
        Evaluate the virtual objective (samples) with curvature.
    """

    use_mean: bool = False

    def evaluate_virtual_objective(
        self,
        model: Model,
        x: Tensor,
        bounds: Tensor,
        n_samples: int,
        tkwargs: dict = None,
    ) -> Tensor:
        """
        Evaluate the virtual objective (samples) with curvature.

        Parameters
        ----------
        model : Model
            The model to use for evaluating the virtual objective.
        x : Tensor
            The inputs at which to evaluate the virtual objective.
        bounds : Tensor
            The bounds for the optimization.
        n_samples : int
            The number of samples to generate.
        tkwargs : dict, optional
            Additional keyword arguments for the evaluation.

        Returns
        -------
        Tensor
            The evaluated virtual objective values with curvature.
        """
        # get samples of the model posterior at inputs given by x
        with torch.no_grad():
            post = model.posterior(x)
            if self.use_mean:
                objective_values = post.mean.unsqueeze(0)
            else:
                objective_values = post.rsample(torch.Size([n_samples]))

        # pad sides with a single value on left and right
        # zero second order gradient at edges
        padding = (0, 0, 1, 1)  # e.g., padding with 1 value on both left and right
        objective_values = torch.nn.functional.pad(
            objective_values, padding, mode="replicate"
        )
        objective_values = torch.diff(objective_values, 2, dim=-2)
        objective_values[:, 0] = 0
        objective_values[:, -1] = 0

        return objective_values

create_mesh(bounds)

Create a mesh for evaluating posteriors on.

Parameters:

Name Type Description Default
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tensor

The mesh points.

Raises:

Type Description
ValueError

If the bounds do not have the shape [2, ndim].

Source code in xopt/generators/bayesian/bax/algorithms.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def create_mesh(self, bounds: Tensor) -> Tensor:
    """
    Create a mesh for evaluating posteriors on.

    Parameters
    ----------
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tensor
        The mesh points.

    Raises
    ------
    ValueError
        If the bounds do not have the shape [2, ndim].
    """
    if len(bounds) != 2:
        raise ValueError("bounds must have the shape [2, ndim]")

    dim = len(bounds[0])
    linspace_list = [
        torch.linspace(bounds.T[i][0], bounds.T[i][1], self.n_mesh_points)
        for i in range(dim)
    ]

    xx = torch.meshgrid(*linspace_list, indexing="ij")
    mesh_pts = torch.stack(xx).flatten(start_dim=1).T

    return mesh_pts

evaluate_virtual_objective(model, x, bounds, n_samples, tkwargs=None)

Evaluate the virtual objective (samples) with curvature.

Parameters:

Name Type Description Default
model Model

The model to use for evaluating the virtual objective.

required
x Tensor

The inputs at which to evaluate the virtual objective.

required
bounds Tensor

The bounds for the optimization.

required
n_samples int

The number of samples to generate.

required
tkwargs dict

Additional keyword arguments for the evaluation.

None

Returns:

Type Description
Tensor

The evaluated virtual objective values with curvature.

Source code in xopt/generators/bayesian/bax/algorithms.py
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
def evaluate_virtual_objective(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> Tensor:
    """
    Evaluate the virtual objective (samples) with curvature.

    Parameters
    ----------
    model : Model
        The model to use for evaluating the virtual objective.
    x : Tensor
        The inputs at which to evaluate the virtual objective.
    bounds : Tensor
        The bounds for the optimization.
    n_samples : int
        The number of samples to generate.
    tkwargs : dict, optional
        Additional keyword arguments for the evaluation.

    Returns
    -------
    Tensor
        The evaluated virtual objective values with curvature.
    """
    # get samples of the model posterior at inputs given by x
    with torch.no_grad():
        post = model.posterior(x)
        if self.use_mean:
            objective_values = post.mean.unsqueeze(0)
        else:
            objective_values = post.rsample(torch.Size([n_samples]))

    # pad sides with a single value on left and right
    # zero second order gradient at edges
    padding = (0, 0, 1, 1)  # e.g., padding with 1 value on both left and right
    objective_values = torch.nn.functional.pad(
        objective_values, padding, mode="replicate"
    )
    objective_values = torch.diff(objective_values, 2, dim=-2)
    objective_values[:, 0] = 0
    objective_values[:, -1] = 0

    return objective_values

get_execution_paths(model, bounds)

Get execution paths that minimize the objective function.

Parameters:

Name Type Description Default
model Model

The model to use for generating execution paths.

required
bounds Tensor

The bounds for the optimization.

required

Returns:

Type Description
Tuple[Tensor, Tensor, Dict]

The execution paths, their corresponding values, and additional results.

Source code in xopt/generators/bayesian/bax/algorithms.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def get_execution_paths(
    self, model: Model, bounds: Tensor
) -> Tuple[Tensor, Tensor, Dict]:
    """
    Get execution paths that minimize the objective function.

    Parameters
    ----------
    model : Model
        The model to use for generating execution paths.
    bounds : Tensor
        The bounds for the optimization.

    Returns
    -------
    Tuple[Tensor, Tensor, Dict]
        The execution paths, their corresponding values, and additional results.
    """
    # build evaluation mesh
    test_points = self.create_mesh(bounds)
    if isinstance(model, ModelList):
        test_points = test_points.to(model.models[0].train_targets)
    else:
        test_points = test_points.to(model.train_targets)

    # get samples of the model posterior at mesh points
    posterior_samples = self.evaluate_virtual_objective(
        model, test_points, bounds, self.n_samples
    )

    # get points that minimize each sample (execution paths)
    if self.minimize:
        y_opt, opt_idx = torch.min(posterior_samples, dim=-2)
    else:
        y_opt, opt_idx = torch.max(posterior_samples, dim=-2)

    opt_idx = opt_idx.squeeze(dim=[-1])
    x_opt = test_points[opt_idx]

    # get the solution_center and solution_entropy for Turbo
    # note: the entropy calc here drops a constant scaling factor
    solution_center = x_opt.mean(dim=0).numpy()
    solution_entropy = float(torch.log(x_opt.std(dim=0) ** 2).sum())

    # collect secondary results in a dict
    results_dict = {
        "test_points": test_points,
        "posterior_samples": posterior_samples,
        "execution_paths": torch.hstack((x_opt, y_opt)),
        "solution_center": solution_center,
        "solution_entropy": solution_entropy,
    }

    # return execution paths
    return x_opt.unsqueeze(-2), y_opt.unsqueeze(-2), results_dict

yaml(**kwargs)

serialize first then dump to yaml string

Source code in xopt/pydantic.py
231
232
233
234
235
236
237
238
def yaml(self, **kwargs):
    """serialize first then dump to yaml string"""
    output = json.loads(
        self.to_json(
            **kwargs,
        )
    )
    return yaml.dump(output)