Specifiying generator options¶
We start with the generator defaults and modify as needed for conservative exploration, which should prevent any constraint violations.
In [1]:
Copied!
# set values if testing
import os
from copy import deepcopy
from xopt import Xopt, Evaluator
from xopt.generators.bayesian import BayesianExplorationGenerator
from xopt.resources.test_functions.tnk import evaluate_TNK, tnk_vocs
from xopt.vocs import get_feasibility_data
# Ignore all warnings
import warnings
warnings.filterwarnings("ignore")
SMOKE_TEST = os.environ.get("SMOKE_TEST")
NUM_MC_SAMPLES = 1 if SMOKE_TEST else 128
NUM_RESTARTS = 1 if SMOKE_TEST else 20
vocs = deepcopy(tnk_vocs)
vocs.objectives = {"y1": "EXPLORE"}
generator = BayesianExplorationGenerator(vocs=vocs)
generator.numerical_optimizer.n_restarts = NUM_RESTARTS
generator.numerical_optimizer.max_iter = 100
generator.n_monte_carlo_samples = NUM_MC_SAMPLES
generator.n_interpolate_points = 5
evaluator = Evaluator(function=evaluate_TNK)
X = Xopt(generator=generator, evaluator=evaluator)
X
# set values if testing
import os
from copy import deepcopy
from xopt import Xopt, Evaluator
from xopt.generators.bayesian import BayesianExplorationGenerator
from xopt.resources.test_functions.tnk import evaluate_TNK, tnk_vocs
from xopt.vocs import get_feasibility_data
# Ignore all warnings
import warnings
warnings.filterwarnings("ignore")
SMOKE_TEST = os.environ.get("SMOKE_TEST")
NUM_MC_SAMPLES = 1 if SMOKE_TEST else 128
NUM_RESTARTS = 1 if SMOKE_TEST else 20
vocs = deepcopy(tnk_vocs)
vocs.objectives = {"y1": "EXPLORE"}
generator = BayesianExplorationGenerator(vocs=vocs)
generator.numerical_optimizer.n_restarts = NUM_RESTARTS
generator.numerical_optimizer.max_iter = 100
generator.n_monte_carlo_samples = NUM_MC_SAMPLES
generator.n_interpolate_points = 5
evaluator = Evaluator(function=evaluate_TNK)
X = Xopt(generator=generator, evaluator=evaluator)
X
/home/runner/work/Xopt/Xopt/.venv/lib/python3.12/site-packages/torch/jit/_script.py:1491: FutureWarning: `torch.jit.script` is deprecated. Please switch to `torch.compile` or `torch.export`. warnings.warn(
Out[1]:
Xopt
________________________________
Version: 3.3.0
Data size: 0
Config as YAML:
data_dump_file: null
evaluator:
function: xopt.resources.test_functions.tnk.evaluate_TNK
function_kwargs:
raise_probability: 0
random_sleep: 0
sleep: 0
max_workers: 1
vectorized: false
generator:
computation_time: null
custom_objective: null
fixed_features: null
gp_constructor:
covar_modules: {}
custom_noise_prior: null
mean_modules: {}
name: standard
train_config: null
train_kwargs: null
train_method: lbfgs
train_model: true
trainable_mean_keys: []
transform_inputs: true
use_cached_hyperparameters: false
use_low_noise_prior: false
max_travel_distances: null
model: null
n_candidates: 1
n_interpolate_points: 5
n_monte_carlo_samples: 128
name: bayesian_exploration
numerical_optimizer:
discrete_max_batch_size: 2048
discrete_max_choices: 4096
max_iter: 100
max_time: 5.0
mixed_max_discrete_configurations: 512
n_restarts: 20
name: LBFGS
returns_id: false
supports_batch_generation: true
supports_constraints: true
supports_contextual_variables: true
supports_discrete_variables: true
supports_multi_objective: true
supports_no_objective: true
supports_single_objective: true
turbo_controller: null
use_cuda: false
vocs:
constants:
a:
dtype: null
type: Constant
value: dummy_constant
constraints:
c1:
dtype: null
type: GreaterThanConstraint
value: 0.0
c2:
dtype: null
type: LessThanConstraint
value: 0.5
objectives:
y1:
dtype: null
type: ExploreObjective
observables: {}
variables:
x1:
default_value: null
domain:
- 0.0
- 3.14159
dtype: null
type: ContinuousVariable
x2:
default_value: null
domain:
- 0.0
- 3.14159
dtype: null
type: ContinuousVariable
serialize_inline: false
serialize_torch: false
stopping_condition: null
strict: true
xopt_dump_file: null
Run exploration¶
We start with evaluating 2 points that we know satisfy the constraints. We then run 30 exploration steps.
In [2]:
Copied!
X.evaluate_data({"x1": [1.0, 0.75], "x2": [0.7, 0.95]})
X.evaluate_data({"x1": [1.0, 0.75], "x2": [0.7, 0.95]})
Out[2]:
| x1 | x2 | a | y1 | y2 | c1 | c2 | xopt_runtime | xopt_error | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.00 | 0.70 | dummy_constant | 1.00 | 0.70 | 0.584045 | 0.290 | 0.002915 | False |
| 1 | 0.75 | 0.95 | dummy_constant | 0.75 | 0.95 | 0.494833 | 0.265 | 0.000139 | False |
In [3]:
Copied!
for i in range(20):
print(f"step {i}")
X.step()
for i in range(20):
print(f"step {i}")
X.step()
step 0
step 1
step 2
step 3
step 4
step 5
step 6
step 7
step 8
step 9
step 10
step 11
step 12
step 13
step 14
step 15
step 16
step 17
step 18
step 19
In [4]:
Copied!
# view the data
X.data
# view the data
X.data
Out[4]:
| x1 | x2 | a | y1 | y2 | c1 | c2 | xopt_runtime | xopt_error | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.000000 | 0.700000 | dummy_constant | 1.000000 | 0.700000 | 0.584045 | 0.290000 | 0.002915 | False |
| 1 | 0.750000 | 0.950000 | dummy_constant | 0.750000 | 0.950000 | 0.494833 | 0.265000 | 0.000139 | False |
| 2 | 1.228318 | 1.388318 | dummy_constant | 1.228318 | 1.388318 | 2.380252 | 1.319556 | 0.013884 | False |
| 3 | 1.706636 | 1.826636 | dummy_constant | 1.706636 | 1.826636 | 5.163599 | 3.215934 | 0.006765 | False |
| 4 | 2.184954 | 2.264954 | dummy_constant | 2.184954 | 2.264954 | 8.808148 | 5.954133 | 0.002373 | False |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 97 | 0.886593 | 0.539485 | dummy_constant | 0.886593 | 0.539485 | 0.154940 | 0.151013 | 0.000143 | False |
| 98 | 0.953040 | 0.469117 | dummy_constant | 0.953040 | 0.469117 | 0.077342 | 0.206199 | 0.000106 | False |
| 99 | 1.019487 | 0.398750 | dummy_constant | 1.019487 | 0.398750 | 0.103363 | 0.280118 | 0.000117 | False |
| 100 | 1.085934 | 0.328383 | dummy_constant | 1.085934 | 0.328383 | 0.288479 | 0.372771 | 0.000116 | False |
| 101 | 1.152380 | 0.258015 | dummy_constant | 1.152380 | 0.258015 | 0.487320 | 0.484157 | 0.000115 | False |
102 rows × 9 columns
In [5]:
Copied!
# plot results
ax = X.data.plot("x1", "x2")
ax.set_aspect("equal")
# plot results
ax = X.data.plot("x1", "x2")
ax.set_aspect("equal")
Introspect models, acquisition function and feasibility prediction¶
During exploration we generate Gaussian Process models of each objective and constraint. We demonstrate how they are viewed below.
In [6]:
Copied!
fig, ax = X.generator.visualize_model(show_feasibility=True, n_grid=100)
fig, ax = X.generator.visualize_model(show_feasibility=True, n_grid=100)
Generator model hyperparameters¶
In [7]:
Copied!
# print generator model hyperparameters
for name, val in X.generator.model.named_parameters():
print(f"{name}:{val}")
X.generator.model.models[2].covar_module.lengthscale
# print generator model hyperparameters
for name, val in X.generator.model.named_parameters():
print(f"{name}:{val}")
X.generator.model.models[2].covar_module.lengthscale
models.0.likelihood.noise_covar.raw_noise:Parameter containing: tensor([0.0001], requires_grad=True) models.0.mean_module.raw_constant:Parameter containing: tensor(1.2955, requires_grad=True) models.0.covar_module.raw_lengthscale:Parameter containing: tensor([[ 0.3796, 22.4813]], requires_grad=True) models.1.likelihood.noise_covar.raw_noise:Parameter containing: tensor([0.0005], requires_grad=True) models.1.mean_module.raw_constant:Parameter containing: tensor(2.5499, requires_grad=True) models.1.covar_module.raw_lengthscale:Parameter containing: tensor([[0.3407, 0.4751]], requires_grad=True) models.2.likelihood.noise_covar.raw_noise:Parameter containing: tensor([0.0001], requires_grad=True) models.2.mean_module.raw_constant:Parameter containing: tensor(3.2987, requires_grad=True) models.2.covar_module.raw_lengthscale:Parameter containing: tensor([[0.3800, 0.5599]], requires_grad=True)
Out[7]:
Parameter containing: tensor([[0.3800, 0.5599]], requires_grad=True)
Examine the number of constraint violations¶
Using the convience function provided by the vocs object we can evaluate which samples violate either or both of our constraints.
In [8]:
Copied!
get_feasibility_data(X.vocs, X.data)
get_feasibility_data(X.vocs, X.data)
Out[8]:
| feasible_c1 | feasible_c2 | feasible | |
|---|---|---|---|
| 0 | True | True | True |
| 1 | True | True | True |
| 2 | True | False | False |
| 3 | True | False | False |
| 4 | True | False | False |
| ... | ... | ... | ... |
| 97 | True | True | True |
| 98 | True | True | True |
| 99 | True | True | True |
| 100 | True | True | True |
| 101 | True | True | True |
102 rows × 3 columns
In [9]:
Copied!
# generate next point
X.generator.generate(1)
# generate next point
X.generator.generate(1)
Out[9]:
[{'x1': 0.9509721177219344, 'x2': 0.42494031233427276},
{'x1': 0.7495638417200993, 'x2': 0.5918652576632502},
{'x1': 0.5481555657182642, 'x2': 0.7587902029922274},
{'x1': 0.3467472897164292, 'x2': 0.9257151483212048},
{'x1': 0.14533901371459426, 'x2': 1.092640093650182}]