Example 13 - Using AX for plug flow reactor yield optimization¶
In this notebook, we will demonstrate the syntax of Ax. Both Ax and NEXTorch are Python wrappers around BoTorch for Bayesian Optimization. By comparing to Example 5, the reader can see the differences in the synatx and designs of two software. We will use the loop and developer API for Ax, which corresponds to automatic and human-in-the-loop optimization.
From Example 5, the optimal value of the yield obtained by NEXTorhc is around 50.1%. We can see Ax reach a similar value.
Note: this notebook requires ax to be install first. See instructions on the Ax documentation page.
1. Import Ax and other packages¶
[12]:
import numpy as np
import os, sys
from IPython.display import display
import pprint
pp = pprint.PrettyPrinter(indent=4)
# ax functions from the loop API
from ax.service.managed_loop import optimize
# ax functions from the developer API
from ax import (
Data,
ParameterType,
RangeParameter,
SearchSpace,
SimpleExperiment,
)
from ax.modelbridge.registry import Models
# ax visualization functions
from ax.plot.contour import plot_contour
from ax.plot.trace import optimization_trace_single_method
from ax.plot.slice import plot_slice
from ax.utils.notebook.plotting import render, init_notebook_plotting
init_notebook_plotting()
[INFO 09-25 21:45:45] ax.utils.notebook.plotting: Injecting Plotly library into cell. Do not overwrite or delete cell.
2. Define the objective function and the design space¶
[13]:
#%% Define the objective function
project_path = os.path.abspath(os.path.join(os.getcwd(), '..\..'))
sys.path.insert(0, project_path)
# Set the path for objective function
objective_path = os.path.join(project_path, 'examples', 'PFR')
sys.path.insert(0, objective_path)
from fructose_pfr_model_function import Reactor
#%% Define the design space
# Three input temperature C, pH, log10(residence time)
x_name_simple = ['T', 'pH', 'tf']
X_name_list = ['T', 'pH', r'$\rm log_{10}(tf_{min})$']
X_units = [r'$\rm ^{o}C $', '', '']
# Add the units
X_name_with_unit = []
for i, var in enumerate(X_name_list):
if not X_units[i] == '':
var = var + ' ('+ X_units[i] + ')'
X_name_with_unit.append(var)
# One output
Y_name_with_unit = 'Yield %'
# combine X and Y names
var_names = X_name_with_unit + [Y_name_with_unit]
# Set the operating range for each parameter
X_ranges = [[140, 200], # Temperature ranges from 140-200 degree C
[0, 1], # pH values ranges from 0-1
[-2, 2]] # log10(residence time) ranges from -2-2
# Set the reponse range
Y_plot_range = [0, 50]
# Get the information of the design space
n_dim = len(X_name_list) # the dimension of inputs
n_objective = 1 # the dimension of outputs
# known optimal value from Example 5
y_known = 50.1
# Objective function
def objective_function(
X_real, # dict of parameter names to values of those parameters
weight=None, # optional weight argument
):
# given a dict of parameter , compute a value for each metric
x1 = X_real[x_name_simple[0]]
x2 = X_real[x_name_simple[1]]
x3 = X_real[x_name_simple[2]]
Conditions = {'T_degC (C)': x1, 'pH': x2, 'tf (min)' : 10**x3}
yi, _ = Reactor(**Conditions) # only keep the first output
# a dictionary of objective names to tuples of mean and standard error for those objectives
# the initial standard error is assume to be 0
return {Y_name_with_unit: (yi, 0)}
3. Automatic optimization using the loop API¶
This is a lightweight API to use Ax for optimization. The initial sampling plan (usually Sobol sampling), acquisition function, and number of sampling points per trial are already fixed. The disadvantage is that it is often hard to customize the optimization loop.
[14]:
# set the total number of sampling points
n_total = 64
# optimization function main body
best_parameters_1, values_1, experiment_1, model_1 = optimize(
parameters=[
{
"name": x_name_simple[0],
"type": "range",
"bounds": X_ranges[0],
"value_type": "float", # Optional, defaults to inference from type of "bounds".
"log_scale": False, # Optional, defaults to False.
},
{
"name": x_name_simple[1],
"type": "range",
"bounds": X_ranges[1],
"value_type": "float",
"log_scale": False,
},
{
"name": x_name_simple[2],
"type": "range",
"bounds": X_ranges[2],
"value_type": "float",
"log_scale": False,
},
],
experiment_name="PFR",
objective_name=Y_name_with_unit,
evaluation_function=objective_function,
minimize=False,
total_trials=n_total,
arms_per_trial=1,
)
# the values of the best parameter
print('Optimal parameters:')
pp.pprint(best_parameters_1)
# the optimal objective value
print('Optimal objective value:')
pp.pprint(values_1)
[INFO 09-25 21:45:45] ax.modelbridge.dispatch_utils: Using GPEI (Bayesian optimization) since there are more continuous parameters than there are categories for the unordered categorical parameters.
[INFO 09-25 21:45:45] ax.modelbridge.dispatch_utils: Using Bayesian Optimization generation strategy: GenerationStrategy(name='Sobol+GPEI', steps=[Sobol for 5 trials, GPEI for subsequent trials]). Iterations after 5 will take longer to generate due to model-fitting.
[INFO 09-25 21:45:45] ax.service.managed_loop: Started full optimization with 64 steps.
[INFO 09-25 21:45:45] ax.service.managed_loop: Running optimization trial 1...
[INFO 09-25 21:45:45] ax.service.managed_loop: Running optimization trial 2...
[INFO 09-25 21:45:45] ax.service.managed_loop: Running optimization trial 3...
[INFO 09-25 21:45:45] ax.service.managed_loop: Running optimization trial 4...
[INFO 09-25 21:45:46] ax.service.managed_loop: Running optimization trial 5...
[INFO 09-25 21:45:46] ax.service.managed_loop: Running optimization trial 6...
[INFO 09-25 21:45:46] ax.service.managed_loop: Running optimization trial 7...
[INFO 09-25 21:45:47] ax.service.managed_loop: Running optimization trial 8...
[INFO 09-25 21:45:47] ax.service.managed_loop: Running optimization trial 9...
[INFO 09-25 21:45:48] ax.service.managed_loop: Running optimization trial 10...
[INFO 09-25 21:45:49] ax.service.managed_loop: Running optimization trial 11...
[INFO 09-25 21:45:50] ax.service.managed_loop: Running optimization trial 12...
[INFO 09-25 21:45:51] ax.service.managed_loop: Running optimization trial 13...
[INFO 09-25 21:45:51] ax.service.managed_loop: Running optimization trial 14...
[INFO 09-25 21:45:52] ax.service.managed_loop: Running optimization trial 15...
[INFO 09-25 21:45:53] ax.service.managed_loop: Running optimization trial 16...
[INFO 09-25 21:45:54] ax.service.managed_loop: Running optimization trial 17...
[INFO 09-25 21:45:55] ax.service.managed_loop: Running optimization trial 18...
[INFO 09-25 21:45:56] ax.service.managed_loop: Running optimization trial 19...
[INFO 09-25 21:45:57] ax.service.managed_loop: Running optimization trial 20...
[INFO 09-25 21:45:57] ax.service.managed_loop: Running optimization trial 21...
[INFO 09-25 21:45:58] ax.service.managed_loop: Running optimization trial 22...
[INFO 09-25 21:46:00] ax.service.managed_loop: Running optimization trial 23...
[INFO 09-25 21:46:02] ax.service.managed_loop: Running optimization trial 24...
[INFO 09-25 21:46:02] ax.service.managed_loop: Running optimization trial 25...
[INFO 09-25 21:46:03] ax.service.managed_loop: Running optimization trial 26...
[INFO 09-25 21:46:04] ax.service.managed_loop: Running optimization trial 27...
[INFO 09-25 21:46:05] ax.service.managed_loop: Running optimization trial 28...
[INFO 09-25 21:46:06] ax.service.managed_loop: Running optimization trial 29...
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\scipy\integrate\_ode.py:1182: UserWarning:
dopri5: larger nsteps is needed
[INFO 09-25 21:46:07] ax.service.managed_loop: Running optimization trial 30...
[INFO 09-25 21:46:08] ax.service.managed_loop: Running optimization trial 31...
[INFO 09-25 21:46:09] ax.service.managed_loop: Running optimization trial 32...
[INFO 09-25 21:46:10] ax.service.managed_loop: Running optimization trial 33...
[INFO 09-25 21:46:11] ax.service.managed_loop: Running optimization trial 34...
[INFO 09-25 21:46:12] ax.service.managed_loop: Running optimization trial 35...
[INFO 09-25 21:46:12] ax.service.managed_loop: Running optimization trial 36...
[INFO 09-25 21:46:14] ax.service.managed_loop: Running optimization trial 37...
[INFO 09-25 21:46:15] ax.service.managed_loop: Running optimization trial 38...
[INFO 09-25 21:46:16] ax.service.managed_loop: Running optimization trial 39...
[INFO 09-25 21:46:18] ax.service.managed_loop: Running optimization trial 40...
[INFO 09-25 21:46:22] ax.service.managed_loop: Running optimization trial 41...
[INFO 09-25 21:46:24] ax.service.managed_loop: Running optimization trial 42...
[INFO 09-25 21:46:26] ax.service.managed_loop: Running optimization trial 43...
[INFO 09-25 21:46:26] ax.service.managed_loop: Running optimization trial 44...
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\scipy\integrate\_ode.py:1182: UserWarning:
dopri5: larger nsteps is needed
[INFO 09-25 21:46:27] ax.service.managed_loop: Running optimization trial 45...
[INFO 09-25 21:46:28] ax.service.managed_loop: Running optimization trial 46...
[INFO 09-25 21:46:29] ax.service.managed_loop: Running optimization trial 47...
[INFO 09-25 21:46:31] ax.service.managed_loop: Running optimization trial 48...
[INFO 09-25 21:46:32] ax.service.managed_loop: Running optimization trial 49...
[INFO 09-25 21:46:34] ax.service.managed_loop: Running optimization trial 50...
[INFO 09-25 21:46:34] ax.service.managed_loop: Running optimization trial 51...
[INFO 09-25 21:46:35] ax.service.managed_loop: Running optimization trial 52...
[INFO 09-25 21:46:36] ax.service.managed_loop: Running optimization trial 53...
[INFO 09-25 21:46:38] ax.service.managed_loop: Running optimization trial 54...
[INFO 09-25 21:46:40] ax.service.managed_loop: Running optimization trial 55...
[INFO 09-25 21:46:42] ax.service.managed_loop: Running optimization trial 56...
[INFO 09-25 21:46:43] ax.service.managed_loop: Running optimization trial 57...
[INFO 09-25 21:46:44] ax.service.managed_loop: Running optimization trial 58...
[INFO 09-25 21:46:46] ax.service.managed_loop: Running optimization trial 59...
[INFO 09-25 21:46:48] ax.service.managed_loop: Running optimization trial 60...
[INFO 09-25 21:46:50] ax.service.managed_loop: Running optimization trial 61...
[INFO 09-25 21:46:52] ax.service.managed_loop: Running optimization trial 62...
[INFO 09-25 21:46:54] ax.service.managed_loop: Running optimization trial 63...
[INFO 09-25 21:46:56] ax.service.managed_loop: Running optimization trial 64...
Optimal parameters:
{'T': 200.0, 'pH': 0.5126827382174883, 'tf': -1.3324093248882605}
Optimal objective value:
( {'Yield %': 50.10119996393495},
{'Yield %': {'Yield %': 2.6388860573819856e-05}})
Visualize the final model reponses¶
By fixing the value of pH (x2), we plot the 2D reponse surfaces by varying T (x1) and tf (x3).
[15]:
x2_fixed_real = 0.7 # fixed x2 value
x_indices = [0, 2] # 0-indexing, for x1 and x3
slice_values = {x_name_simple[1]: x2_fixed_real}
contour_plot = plot_contour(model=model_1,
param_x=x_name_simple[x_indices[0]],
param_y=x_name_simple[x_indices[1]],
metric_name=Y_name_with_unit,
slice_values=slice_values)
render(contour_plot)
Plot the optimum discovred in each trial.
[16]:
# collect the objective values from each trial
objectives_values_1 = np.array([[trial.objective_mean for trial in experiment_1.trials.values()]])
best_objective_plot = optimization_trace_single_method(
y=np.maximum.accumulate(objectives_values_1, axis=1),
optimum=y_known,
title="Model performance vs. # of iterations",
ylabel=Y_name_with_unit,
)
render(best_objective_plot)
4. Human-in-the-loop optimization with the developer API¶
The developer API allows customization of the optimization loop. To introduce human-in-the-loop optimization, we explicitly convert the sampling points predicted by the acquisition function to a pandas Dataframe (data_all). Next, experiments shall be performed at these parameter values (in data_next) and the objective values shall be collected. A new Dataframe (data_new) containing all previous points is then needed to be passed a GP model. We show that multiple points can be taken
at each trial.
Since the objective function is passed into the SimpleExperiment object at the beginning, the evalution of objective function is still wrapped under the hood. However, for the majority of laboratory experiments, the objective function is unknown a priori. We do not know the best way to construct an Experiment object with no objective function input, or how to export the next sampling points before objective function evalution. These are all important elements for the human-in-the-loop
design.
For NEXTorch syntax, readers can refer to Example 4. No objective function input is required for an Experiment object.
[17]:
n_init = 10 #initial experiments
n_samples_per_trial = 3 # number of sampling points per trial
n_trials = int((n_total-n_init)/n_samples_per_trial) #number of trials
# define the parameter space (or search space)
search_space = SearchSpace(
parameters=[
RangeParameter(
name=x_name_i, parameter_type=ParameterType.FLOAT, lower=X_range_i[0], upper=X_range_i[1]
)
for x_name_i, X_range_i in zip(x_name_simple, X_ranges)
]
)
# initialize a SimpleExperiment object
experiment_2 = SimpleExperiment(
name="PFR",
search_space=search_space,
evaluation_function=objective_function,
objective_name=Y_name_with_unit,
minimize=False,
)
# Define Initial Sobol sampling plan
print("Running Sobol initialization trials...")
sobol = Models.SOBOL(experiment_2.search_space)
for i in range(n_init):
experiment_2.new_trial(generator_run=sobol.gen(1))
# Show the initial sampling points
print("The initial sampling data: ")
data_all = experiment_2.eval().df
display(data_all)
Running Sobol initialization trials...
The initial sampling data:
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\scipy\integrate\_ode.py:1182: UserWarning:
dopri5: larger nsteps is needed
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 0 | 0_0 | Yield % | 7.952644e+00 | 0.0 | 0 |
| 1 | 1_0 | Yield % | -5.432170e-11 | 0.0 | 1 |
| 2 | 2_0 | Yield % | 3.105702e+01 | 0.0 | 2 |
| 3 | 3_0 | Yield % | 4.790725e-02 | 0.0 | 3 |
| 4 | 4_0 | Yield % | 1.884997e+00 | 0.0 | 4 |
| 5 | 5_0 | Yield % | 4.747235e+01 | 0.0 | 5 |
| 6 | 6_0 | Yield % | 9.640585e+00 | 0.0 | 6 |
| 7 | 7_0 | Yield % | 5.101536e+00 | 0.0 | 7 |
| 8 | 8_0 | Yield % | 4.330709e+01 | 0.0 | 8 |
| 9 | 9_0 | Yield % | 2.424362e+01 | 0.0 | 9 |
[18]:
# Optimization loop main body
for i in range(n_trials):
print(f"Running GP+EI optimization trial {i+1}/{n_trials}...")
# Import the dataframe containing all previous point
# Reinitialize GP+EI model at each step with the updated data
data_new = Data(data_all)
model_2 = Models.BOTORCH(experiment=experiment_2, data=data_new)
experiment_2.new_batch_trial(generator_run=model_2.gen(n=3))
# Export the next sampling points into a dataframe
data_all = experiment_2.eval().df
data_next = data_all[-n_samples_per_trial:]
print(f"The new data points in iteration {i+1} are:")
display(data_next)
print("Done!")
Running GP+EI optimization trial 1/18...
The new data points in iteration 1 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 10 | 10_0 | Yield % | 41.298076 | 0.0 | 10 |
| 11 | 10_1 | Yield % | 46.843116 | 0.0 | 10 |
| 12 | 10_2 | Yield % | 47.403937 | 0.0 | 10 |
Running GP+EI optimization trial 2/18...
The new data points in iteration 2 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 13 | 11_0 | Yield % | 47.058547 | 0.0 | 11 |
| 14 | 11_1 | Yield % | 43.302009 | 0.0 | 11 |
| 15 | 11_2 | Yield % | 38.391249 | 0.0 | 11 |
Running GP+EI optimization trial 3/18...
The new data points in iteration 3 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 16 | 12_0 | Yield % | 44.782317 | 0.0 | 12 |
| 17 | 12_1 | Yield % | 46.967598 | 0.0 | 12 |
| 18 | 12_2 | Yield % | 45.833647 | 0.0 | 12 |
Running GP+EI optimization trial 4/18...
The new data points in iteration 4 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 19 | 13_0 | Yield % | 47.867416 | 0.0 | 13 |
| 20 | 13_1 | Yield % | 48.093134 | 0.0 | 13 |
| 21 | 13_2 | Yield % | 45.756795 | 0.0 | 13 |
Running GP+EI optimization trial 5/18...
The new data points in iteration 5 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 22 | 14_0 | Yield % | 47.481168 | 0.0 | 14 |
| 23 | 14_1 | Yield % | 49.071212 | 0.0 | 14 |
| 24 | 14_2 | Yield % | 49.291180 | 0.0 | 14 |
Running GP+EI optimization trial 6/18...
The new data points in iteration 6 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 25 | 15_0 | Yield % | 47.982709 | 0.0 | 15 |
| 26 | 15_1 | Yield % | 49.012296 | 0.0 | 15 |
| 27 | 15_2 | Yield % | 20.783304 | 0.0 | 15 |
Running GP+EI optimization trial 7/18...
The new data points in iteration 7 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 28 | 16_0 | Yield % | 49.037532 | 0.0 | 16 |
| 29 | 16_1 | Yield % | 49.942003 | 0.0 | 16 |
| 30 | 16_2 | Yield % | 48.650191 | 0.0 | 16 |
Running GP+EI optimization trial 8/18...
The new data points in iteration 8 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 31 | 17_0 | Yield % | 48.310584 | 0.0 | 17 |
| 32 | 17_1 | Yield % | 44.241994 | 0.0 | 17 |
| 33 | 17_2 | Yield % | 35.070482 | 0.0 | 17 |
Running GP+EI optimization trial 9/18...
The new data points in iteration 9 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 34 | 18_0 | Yield % | 44.510480 | 0.0 | 18 |
| 35 | 18_1 | Yield % | 49.309863 | 0.0 | 18 |
| 36 | 18_2 | Yield % | 49.531132 | 0.0 | 18 |
Running GP+EI optimization trial 10/18...
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\gpytorch\utils\cholesky.py:44: NumericalWarning:
A not p.d., added jitter of 1.0e-08 to the diagonal
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\gpytorch\utils\cholesky.py:44: NumericalWarning:
A not p.d., added jitter of 1.0e-08 to the diagonal
The new data points in iteration 10 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 37 | 19_0 | Yield % | -4.297795e-11 | 0.0 | 19 |
| 38 | 19_1 | Yield % | 3.582672e+01 | 0.0 | 19 |
| 39 | 19_2 | Yield % | 4.144920e+01 | 0.0 | 19 |
Running GP+EI optimization trial 11/18...
The new data points in iteration 11 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 40 | 20_0 | Yield % | 48.940496 | 0.0 | 20 |
| 41 | 20_1 | Yield % | 45.412358 | 0.0 | 20 |
| 42 | 20_2 | Yield % | 49.999238 | 0.0 | 20 |
Running GP+EI optimization trial 12/18...
The new data points in iteration 12 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 43 | 21_0 | Yield % | 50.098608 | 0.0 | 21 |
| 44 | 21_1 | Yield % | 48.458128 | 0.0 | 21 |
| 45 | 21_2 | Yield % | 48.179908 | 0.0 | 21 |
Running GP+EI optimization trial 13/18...
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\gpytorch\utils\cholesky.py:44: NumericalWarning:
A not p.d., added jitter of 1.0e-08 to the diagonal
The new data points in iteration 13 are:
C:\Users\yifan\Anaconda3\envs\torch\lib\site-packages\scipy\integrate\_ode.py:1182: UserWarning:
dopri5: larger nsteps is needed
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 46 | 22_0 | Yield % | 4.978738e+01 | 0.0 | 22 |
| 47 | 22_1 | Yield % | -5.399813e-11 | 0.0 | 22 |
| 48 | 22_2 | Yield % | 2.792262e+01 | 0.0 | 22 |
Running GP+EI optimization trial 14/18...
The new data points in iteration 14 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 49 | 23_0 | Yield % | 49.960202 | 0.0 | 23 |
| 50 | 23_1 | Yield % | 50.092853 | 0.0 | 23 |
| 51 | 23_2 | Yield % | 48.490446 | 0.0 | 23 |
Running GP+EI optimization trial 15/18...
The new data points in iteration 15 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 52 | 24_0 | Yield % | 49.938541 | 0.0 | 24 |
| 53 | 24_1 | Yield % | 49.631065 | 0.0 | 24 |
| 54 | 24_2 | Yield % | 50.055893 | 0.0 | 24 |
Running GP+EI optimization trial 16/18...
The new data points in iteration 16 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 55 | 25_0 | Yield % | 1.069913 | 0.0 | 25 |
| 56 | 25_1 | Yield % | 49.932068 | 0.0 | 25 |
| 57 | 25_2 | Yield % | 10.203983 | 0.0 | 25 |
Running GP+EI optimization trial 17/18...
The new data points in iteration 17 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 58 | 26_0 | Yield % | 50.095001 | 0.0 | 26 |
| 59 | 26_1 | Yield % | 49.129794 | 0.0 | 26 |
| 60 | 26_2 | Yield % | 47.987654 | 0.0 | 26 |
Running GP+EI optimization trial 18/18...
The new data points in iteration 18 are:
| arm_name | metric_name | mean | sem | trial_index | |
|---|---|---|---|---|---|
| 61 | 27_0 | Yield % | 50.093298 | 0.0 | 27 |
| 62 | 27_1 | Yield % | 27.434912 | 0.0 | 27 |
| 63 | 27_2 | Yield % | 49.155569 | 0.0 | 27 |
Done!
Visualize the final model reponses¶
By fixing the value of pH (x2), we plot the 2D reponse surfaces by varying T (x1) and tf (x3).
[19]:
x2_fixed_real = 0.7 # fixed x2 value
x_indices = [0, 2] # 0-indexing, for x1 and x3
slice_values = {x_name_simple[1]: x2_fixed_real}
contour_plot = plot_contour(model=model_2,
param_x=x_name_simple[x_indices[0]],
param_y=x_name_simple[x_indices[1]],
metric_name=Y_name_with_unit,
slice_values=slice_values)
render(contour_plot)
By fixing the value of T (x1) and pH (x2), we plot the 1D reponse by varying and tf (x3).
[20]:
# set the fixed parameter values
slice_values = { x_name_simple[0]: np.max(X_ranges[0]),
x_name_simple[1]: x2_fixed_real}
print('Fixed parameter values:')
pp.pprint(slice_values)
render(plot_slice(model_2, x_name_simple[2], Y_name_with_unit, slice_values = slice_values))
Fixed parameter values:
{'T': 200, 'pH': 0.7}
Plot the optimum discovered in each trial.
[21]:
# collect the objective values from the final Dataframe
objective_values_2 = np.array([data_all['mean']])
best_objective_plot = optimization_trace_single_method(
y=np.maximum.accumulate(objective_values_2, axis = 1),
optimum=y_known,
title="Model performance vs. # of iterations",
ylabel=Y_name_with_unit,
)
render(best_objective_plot)
Thumbnail of this notebook