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
execute

Draw samples from the model, execute the algorithm on the samples, and return algorithm results.

perform_virtual_measurement

Perform the virtual measurement and calculate objective values at the given inputs.

Source code in xopt/generators/bayesian/bax/algorithms.py
 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
 88
 89
 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
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
    -------
    execute(self, model: Model, bounds: Tensor) -> AlgorithmResult
        Draw samples from the model, execute the algorithm on the samples, and return algorithm results.
    perform_virtual_measurement(self, model: Model, x: Tensor, bounds: Tensor, n_samples: int, tkwargs: dict = None) -> VirtualMeasurementResult
        Perform the virtual measurement and calculate objective values at the given inputs.
    """

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

    @computed_field
    @property
    def class_path(self) -> str:
        return f"{self.__class__.__module__}.{self.__class__.__name__}"

    @abstractmethod
    def execute(self, model: Model, bounds: Tensor) -> AlgorithmResult:
        """
        Draw samples from the model, execute the algorithm on the samples, and return results.

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

        Returns
        -------
        AlgorithmResult
            The algorithm result with input and output execution paths.
        """
        raise NotImplementedError

    @abstractmethod
    def perform_virtual_measurement(
        self,
        model: Model,
        x: Tensor,
        bounds: Tensor,
        n_samples: int,
        tkwargs: dict = None,
    ) -> VirtualMeasurementResult:
        """
        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
        -------
        VirtualMeasurementResult
            The virtual measurement result with computed objective values.
        """
        raise NotImplementedError  # pragma: no cover

execute(model, bounds) abstractmethod

Draw samples from the model, execute the algorithm on the samples, and return results.

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
AlgorithmResult

The algorithm result with input and output execution paths.

Source code in xopt/generators/bayesian/bax/algorithms.py
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@abstractmethod
def execute(self, model: Model, bounds: Tensor) -> AlgorithmResult:
    """
    Draw samples from the model, execute the algorithm on the samples, and return results.

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

    Returns
    -------
    AlgorithmResult
        The algorithm result with input and output execution paths.
    """
    raise NotImplementedError

perform_virtual_measurement(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
VirtualMeasurementResult

The virtual measurement result with computed objective values.

Source code in xopt/generators/bayesian/bax/algorithms.py
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
@abstractmethod
def perform_virtual_measurement(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> VirtualMeasurementResult:
    """
    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
    -------
    VirtualMeasurementResult
        The virtual measurement result with computed objective values.
    """
    raise NotImplementedError  # pragma: no cover

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
134
135
136
137
138
139
140
141
142
143
144
145
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
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
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
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

execute(model, bounds) abstractmethod

Draw samples from the model, execute the algorithm on the samples, and return results.

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
AlgorithmResult

The algorithm result with input and output execution paths.

Source code in xopt/generators/bayesian/bax/algorithms.py
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@abstractmethod
def execute(self, model: Model, bounds: Tensor) -> AlgorithmResult:
    """
    Draw samples from the model, execute the algorithm on the samples, and return results.

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

    Returns
    -------
    AlgorithmResult
        The algorithm result with input and output execution paths.
    """
    raise NotImplementedError

perform_virtual_measurement(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
VirtualMeasurementResult

The virtual measurement result with computed objective values.

Source code in xopt/generators/bayesian/bax/algorithms.py
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
@abstractmethod
def perform_virtual_measurement(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> VirtualMeasurementResult:
    """
    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
    -------
    VirtualMeasurementResult
        The virtual measurement result with computed objective values.
    """
    raise NotImplementedError  # pragma: no cover

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.

perform_virtual_measurement

Evaluate the virtual measurement and calculate objective values (samples).

Source code in xopt/generators/bayesian/bax/algorithms.py
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
262
263
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
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.
    perform_virtual_measurement(self, model: Model, x: Tensor, bounds: Tensor, n_samples: int, tkwargs: dict = None) -> VirtualMeasurementResult
        Evaluate the virtual measurement and calculate objective values (samples).
    """

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

    def execute(self, model: Model, bounds: Tensor) -> GridOptimizeResult:
        """
        Execute the algorithm on samples and collect the results of the optimization.

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

        Returns
        -------
        GridOptimizeResult
            Contains best_inputs, best_objective, input_execution_paths, output_execution_paths, 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
        result = self.perform_virtual_measurement(
            model, test_points, bounds, self.n_samples
        )

        posterior_samples = result.objective
        # 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)
        solution_entropy = float(torch.log(x_opt.std(dim=0) ** 2).sum())

        algorithm_result = GridOptimizeResult(
            best_inputs=x_opt,
            best_objective=y_opt,
            input_execution_paths=x_opt.unsqueeze(-2),
            output_execution_paths=y_opt.unsqueeze(-2),
            test_points=test_points,
            posterior_samples=posterior_samples,
            solution_center=solution_center,
            solution_entropy=solution_entropy,
        )

        return algorithm_result

    def perform_virtual_measurement(
        self,
        model: Model,
        x: Tensor,
        bounds: Tensor,
        n_samples: int,
        tkwargs: dict = None,
    ) -> VirtualMeasurementResult:
        """
        Perform the virtual measurement (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
        -------
        VirtualMeasurementResult
            The virtual measurement result with calculated virtual objective values.
        """
        with torch.no_grad():
            post = model.posterior(x)
            objective_values = post.rsample(torch.Size([n_samples]))

        return VirtualMeasurementResult(objective=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
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
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

execute(model, bounds)

Execute the algorithm on samples and collect the results of the optimization.

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
GridOptimizeResult

Contains best_inputs, best_objective, input_execution_paths, output_execution_paths, and additional results.

Source code in xopt/generators/bayesian/bax/algorithms.py
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
262
263
264
265
266
267
268
def execute(self, model: Model, bounds: Tensor) -> GridOptimizeResult:
    """
    Execute the algorithm on samples and collect the results of the optimization.

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

    Returns
    -------
    GridOptimizeResult
        Contains best_inputs, best_objective, input_execution_paths, output_execution_paths, 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
    result = self.perform_virtual_measurement(
        model, test_points, bounds, self.n_samples
    )

    posterior_samples = result.objective
    # 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)
    solution_entropy = float(torch.log(x_opt.std(dim=0) ** 2).sum())

    algorithm_result = GridOptimizeResult(
        best_inputs=x_opt,
        best_objective=y_opt,
        input_execution_paths=x_opt.unsqueeze(-2),
        output_execution_paths=y_opt.unsqueeze(-2),
        test_points=test_points,
        posterior_samples=posterior_samples,
        solution_center=solution_center,
        solution_entropy=solution_entropy,
    )

    return algorithm_result

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

Perform the virtual measurement (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
VirtualMeasurementResult

The virtual measurement result with calculated virtual objective values.

Source code in xopt/generators/bayesian/bax/algorithms.py
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
def perform_virtual_measurement(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> VirtualMeasurementResult:
    """
    Perform the virtual measurement (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
    -------
    VirtualMeasurementResult
        The virtual measurement result with calculated virtual objective values.
    """
    with torch.no_grad():
        post = model.posterior(x)
        objective_values = post.rsample(torch.Size([n_samples]))

    return VirtualMeasurementResult(objective=objective_values)

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
perform_virtual_measurement

Perform the virtual measurement (samples) with curvature.

Source code in xopt/generators/bayesian/bax/algorithms.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
class CurvatureGridOptimize(GridOptimize):
    """
    Curvature grid optimization algorithm for BAX.

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

    Methods
    -------
    perform_virtual_measurement(self, model: Model, x: Tensor, bounds: Tensor, n_samples: int, tkwargs: dict = None) -> VirtualMeasurementResult
        Perform the virtual measurement (samples) with curvature.
    """

    use_mean: bool = False

    def perform_virtual_measurement(
        self,
        model: Model,
        x: Tensor,
        bounds: Tensor,
        n_samples: int,
        tkwargs: dict = None,
    ) -> VirtualMeasurementResult:
        """
        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
        -------
        VirtualMeasurementResult
            The virtual measurement result with calculated 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 VirtualMeasurementResult(objective=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
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
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

execute(model, bounds)

Execute the algorithm on samples and collect the results of the optimization.

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
GridOptimizeResult

Contains best_inputs, best_objective, input_execution_paths, output_execution_paths, and additional results.

Source code in xopt/generators/bayesian/bax/algorithms.py
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
262
263
264
265
266
267
268
def execute(self, model: Model, bounds: Tensor) -> GridOptimizeResult:
    """
    Execute the algorithm on samples and collect the results of the optimization.

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

    Returns
    -------
    GridOptimizeResult
        Contains best_inputs, best_objective, input_execution_paths, output_execution_paths, 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
    result = self.perform_virtual_measurement(
        model, test_points, bounds, self.n_samples
    )

    posterior_samples = result.objective
    # 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)
    solution_entropy = float(torch.log(x_opt.std(dim=0) ** 2).sum())

    algorithm_result = GridOptimizeResult(
        best_inputs=x_opt,
        best_objective=y_opt,
        input_execution_paths=x_opt.unsqueeze(-2),
        output_execution_paths=y_opt.unsqueeze(-2),
        test_points=test_points,
        posterior_samples=posterior_samples,
        solution_center=solution_center,
        solution_entropy=solution_entropy,
    )

    return algorithm_result

perform_virtual_measurement(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
VirtualMeasurementResult

The virtual measurement result with calculated virtual objective values with curvature.

Source code in xopt/generators/bayesian/bax/algorithms.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
def perform_virtual_measurement(
    self,
    model: Model,
    x: Tensor,
    bounds: Tensor,
    n_samples: int,
    tkwargs: dict = None,
) -> VirtualMeasurementResult:
    """
    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
    -------
    VirtualMeasurementResult
        The virtual measurement result with calculated 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 VirtualMeasurementResult(objective=objective_values)

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)