Multi-Objective Optimizer¶
Amplify-BBOpt provides an optimizer class called “MultiObjectiveOptimizer
” that handle optimization problems with multiple black-box objective functions and constaints spanning these objective functions.
Here is an example using the objective functions, obj_1
and obj_2
, from the previous page. As you can see in the various examples below, using MultiObjectiveOptimizer
is not so different from using basic optimizers.
Basic usage¶
For initial training data, you can simply pass a list of all objective functions to DatasetGenerator
to create initial training dataset. The additional constraints spanning different objective functions are taken care of as well during the data generation.
As for the optimizer, instantiate a basic optimizer for each of objective functions involed. This identifies various optimization settings specific to the black-box function, such as what surrogate model to use and what weight to impose for the black-box function. Once the basic optimizers are instantiated for the individual objective functions, pass them in a list to the MultiObjectiveOptimizer
, along with other usual arguments such as client
. Finally, you can execute the MultiObjectiveOptimizer.optimize
method to perform black-box optimization with multiple-objective functions.
from datetime import timedelta
import numpy as np
from amplify import FixstarsClient
from amplify_bbopt import (
BinaryVariable,
DatasetGenerator,
IntegerVariable,
KernelQAOptimizer,
MultiObjectiveOptimizer,
blackbox,
equal_to,
)
np.set_printoptions(legacy="1.25")
# Set up solver client
client = FixstarsClient()
client.parameters.timeout = timedelta(milliseconds=2000) # 2 seconds
# client.token = "xxxxxxxxxxx" # Enter your Amplify AE API token.
@blackbox
def obj_1(
a: bool = BinaryVariable(),
b: int = IntegerVariable(bounds=(1, 5)),
) -> int:
print(f"{a=}, {b=}")
if a:
return -b
return b
@blackbox
def obj_2(
c: int = IntegerVariable(bounds=(1, 5)),
d: int = IntegerVariable(bounds=(1, 5)),
) -> int:
print(f"{c=}, {d=}")
return c * d * d
my_constraint = equal_to(obj_1.variables.b - obj_2.variables.c, 0)
# You can add "my_constraint" to obj_1 instead.
obj_2.add_constraint(my_constraint)
data_1, data_2 = DatasetGenerator([obj_1, obj_2]).generate(num_samples=3)
optimizer_1 = KernelQAOptimizer(data_1, obj_1)
optimizer_2 = KernelQAOptimizer(data_2, obj_2)
multi_optimizer = MultiObjectiveOptimizer(
[optimizer_1, optimizer_2], client=client
)
multi_optimizer.optimize(num_cycles=2)
print(f"{multi_optimizer.best_solution=}") # Solution (optimal input)
print(f"{multi_optimizer.best_objective=:.3e}") # Objective function value
Show code cell output
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #0/3 initial data for obj_1
amplify-bbopt | 2024/10/04 06:09:08 | INFO | - [obj]: a=False, b=1
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #1/3 initial data for obj_1
amplify-bbopt | 2024/10/04 06:09:08 | INFO | - [obj]: a=True, b=3
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #2/3 initial data for obj_1
amplify-bbopt | 2024/10/04 06:09:08 | INFO | - [obj]: a=False, b=4
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #0/3 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:08 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #1/3 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:08 | INFO | - [obj]: c=3, d=5
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #2/3 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:08 | INFO | - [obj]: c=4, d=2
amplify-bbopt | 2024/10/04 06:09:08 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:08 | INFO | #1/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:08 | INFO | model corrcoef: 0.985, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:08 | INFO | model corrcoef: 1.000, beta: 0.0
2024-10-04 06:09:10 [amplify:WARNING] Connection timeout, retrying after sleep 1 second (1/3)
amplify-bbopt | 2024/10/04 06:09:14 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:14 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:14 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:14 | INFO | obj_2: 1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:14 | INFO | y_hat_total=0.000e+00, y_best=0.000e+00
amplify-bbopt | 2024/10/04 06:09:14 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:14 | INFO | #2/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:14 | INFO | model corrcoef: 0.984, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:14 | INFO | model corrcoef: 1.000, beta: 0.0
2024-10-04 06:09:15 [amplify:WARNING] Connection timeout, retrying after sleep 1 second (1/3)
amplify-bbopt | 2024/10/04 06:09:19 | INFO | identical input was obtained (0), x_hat={'a': True, 'b': 1, 'c': 1, 'd': 1}.
amplify-bbopt | 2024/10/04 06:09:19 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:19 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:19 | INFO | - [obj]: c=1, d=2
amplify-bbopt | 2024/10/04 06:09:19 | INFO | obj_2: 4.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:19 | INFO | y_hat_total=3.000e+00, y_best=0.000e+00
multi_optimizer.best_solution={'a': True, 'b': 1, 'c': 1, 'd': 1}
multi_optimizer.best_objective=0.000e+00
Note
Here is more detailed description of what happens when you pass a list of objective functions [obj_1, obj_2]
to DatasetGenerator
or a list of basic optimizers [optimizer_1, optimizer_2]
to MultiObjectiveOptimizer
. These two classes will instantiate the BlackBoxFuncList
class with all the objective functions involved. The BlackBoxFuncList
knows not only all the objective functions, but also constraints, how they should be modelled, numerous others. During its instantiation process, the BlackBoxFuncList
class tries to unify the variables across the objective functions – the variables with the same name in the different objective functions are unified into one variable with the same attributes. Then the class updates the constraints accordingly. Internally, both DatasetGenerator
and MultiObjectiveOptimizer
work towards the resulting BlackBoxFuncList
class instance.
That means that it does not matter whether you add_constraint
the my_constraint
to obj_1
or obj_2
, as long as the constraint is added one of the objective functions.
Using different surrogate/acquisition models¶
You can use different surrogate/acquisition models for different objective functions. To do so, simply instantiate different basic optimizers with different model/trainer settings. Here is an example based on the above code snippet.
from amplify_bbopt import FMQAOptimizer
# obj_1 is modeled by kernel-based model as before
optimizer_1 = KernelQAOptimizer(data_1, obj_1)
# Now obj_2 is modeled by FM
optimizer_2 = FMQAOptimizer(data_2, obj_2)
optimizer_2.trainer.set_model_params(k=20)
optimizer_2.trainer.set_train_params(epochs=1000)
multi_optimizer = MultiObjectiveOptimizer(
[optimizer_1, optimizer_2], client=client
)
multi_optimizer.optimize(num_cycles=2)
print(f"{multi_optimizer.best_solution=}") # Solution (optimal input)
print(f"{multi_optimizer.best_objective=:.3e}") # Objective function value
Show code cell output
amplify-bbopt | 2024/10/04 06:09:19 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:19 | INFO | #1/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:19 | INFO | model corrcoef: 0.985, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:20 | WARNING | No data split is performed for this cycle.
amplify-bbopt | 2024/10/04 06:09:21 | INFO | model corrcoef: 1.000
2024-10-04 06:09:22 [amplify:WARNING] Connection timeout, retrying after sleep 1 second (1/3)
amplify-bbopt | 2024/10/04 06:09:27 | INFO | - [obj]: a=True, b=2
amplify-bbopt | 2024/10/04 06:09:27 | INFO | obj_1: -2.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:27 | INFO | - [obj]: c=2, d=1
amplify-bbopt | 2024/10/04 06:09:27 | INFO | obj_2: 2.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:27 | INFO | y_hat_total=0.000e+00, y_best=0.000e+00
amplify-bbopt | 2024/10/04 06:09:27 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:27 | INFO | #2/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:27 | INFO | model corrcoef: 0.988, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:27 | WARNING | No data split is performed for this cycle.
amplify-bbopt | 2024/10/04 06:09:28 | INFO | model corrcoef: 1.000
amplify-bbopt | 2024/10/04 06:09:31 | INFO | identical input was obtained (0), x_hat={'a': True, 'b': 2, 'c': 2, 'd': 1}.
amplify-bbopt | 2024/10/04 06:09:31 | INFO | - [obj]: a=True, b=2
amplify-bbopt | 2024/10/04 06:09:31 | INFO | obj_1: -2.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:31 | INFO | - [obj]: c=2, d=2
amplify-bbopt | 2024/10/04 06:09:31 | INFO | obj_2: 8.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:31 | INFO | y_hat_total=6.000e+00, y_best=0.000e+00
multi_optimizer.best_solution={'a': True, 'b': 2, 'c': 2, 'd': 1}
multi_optimizer.best_objective=0.000e+00
Weighting for objectives¶
Weigiting objective functions is useful when you have certain objective functions to optimize more than the other objective functions. Also, appropriately weighting generally results in better optimization result, especially when the output value ranges from objective functions have relatively large discrepancies. In such cases, it may be a good idea to add a larger weight for the objective functions that return relatively small values overall. In multiple-objective optimization, the weight is only multiplied to the part of the optimization model (amplify.Model
). For the output values from any objective functions, the weight will not be multiplied.
By default, the weights for all objectives are set as 1.
Setting a constant weight¶
Here is an example to use different weights for objective functions. Again, we use the same obj_1
and obj_2
. As you can see the definitions, the value ranges of obj_1
is \(\pm5\) and that for obj_2
is between 1 and 25. Thus, let us give a larger weight for obj_1
.
optimizer_1 = KernelQAOptimizer(data_1, obj_1, objective_weight=10)
optimizer_2 = KernelQAOptimizer(data_2, obj_2)
multi_optimizer = MultiObjectiveOptimizer(
[optimizer_1, optimizer_2], client=client
)
multi_optimizer.optimize(num_cycles=2)
print(f"{multi_optimizer.best_solution=}") # Solution (optimal input)
print(f"{multi_optimizer.best_objective=:.3e}") # Objective function value
Show code cell output
amplify-bbopt | 2024/10/04 06:09:31 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:31 | INFO | #1/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:31 | INFO | model corrcoef: 0.985, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:31 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:34 | INFO | - [obj]: a=True, b=3
amplify-bbopt | 2024/10/04 06:09:34 | INFO | obj_1: -3.00000e+00 (wt: 1.000e+01)
amplify-bbopt | 2024/10/04 06:09:34 | INFO | - [obj]: c=3, d=1
amplify-bbopt | 2024/10/04 06:09:34 | INFO | obj_2: 3.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:34 | INFO | y_hat_total=0.000e+00, y_best=0.000e+00
amplify-bbopt | 2024/10/04 06:09:34 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:34 | INFO | #2/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:34 | INFO | model corrcoef: 0.992, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:34 | INFO | model corrcoef: 1.000, beta: 0.0
2024-10-04 06:09:35 [amplify:WARNING] Connection timeout, retrying after sleep 1 second (1/3)
amplify-bbopt | 2024/10/04 06:09:40 | INFO | identical input was obtained (0), x_hat={'a': True, 'b': 3, 'c': 3, 'd': 1}.
amplify-bbopt | 2024/10/04 06:09:40 | INFO | - [obj]: a=True, b=3
amplify-bbopt | 2024/10/04 06:09:40 | INFO | obj_1: -3.00000e+00 (wt: 1.000e+01)
amplify-bbopt | 2024/10/04 06:09:40 | INFO | - [obj]: c=3, d=2
amplify-bbopt | 2024/10/04 06:09:40 | INFO | obj_2: 1.20000e+01 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:40 | INFO | y_hat_total=9.000e+00, y_best=0.000e+00
multi_optimizer.best_solution={'a': True, 'b': 3, 'c': 3, 'd': 1}
multi_optimizer.best_objective=0.000e+00
Setting a weight function¶
You can also pass your own weight function that takes the optimizer itself. This function is called right before the optimization model (amplify.Model
) is made at each cycle.
Here is an example of such weight function, which uses the KernelQAOptimizer.i_cycle
attribute to compute the weight. You can see that the weight (wt.
) for obj_1
evolves from 1.0 to 2.0 in the execution output below.
def my_weight(optimizer: KernelQAOptimizer) -> float:
"""Compute weight based on the current number of optimization cycle."""
weight = optimizer.i_cycle + 1.0
return weight
optimizer_1 = KernelQAOptimizer(data_1, obj_1, objective_weight=my_weight)
optimizer_2 = KernelQAOptimizer(data_2, obj_2)
multi_optimizer = MultiObjectiveOptimizer(
[optimizer_1, optimizer_2], client=client
)
multi_optimizer.optimize(num_cycles=2)
print(f"{multi_optimizer.best_solution=}") # Solution (optimal input)
print(f"{multi_optimizer.best_objective=:.3e}") # Objective function value
Show code cell output
amplify-bbopt | 2024/10/04 06:09:40 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:40 | INFO | #1/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:40 | INFO | model corrcoef: 0.985, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:40 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:43 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:43 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:43 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:43 | INFO | obj_2: 1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:43 | INFO | y_hat_total=0.000e+00, y_best=0.000e+00
amplify-bbopt | 2024/10/04 06:09:43 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:43 | INFO | #2/2 optimization cycle, constraint wt: 1.50e+02
amplify-bbopt | 2024/10/04 06:09:43 | INFO | model corrcoef: 0.984, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:43 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:46 | INFO | identical input was obtained (0), x_hat={'a': True, 'b': 1, 'c': 1, 'd': 1}.
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | obj_1: -1.00000e+00 (wt: 2.000e+00)
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: c=1, d=2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | obj_2: 4.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:46 | INFO | y_hat_total=3.000e+00, y_best=0.000e+00
multi_optimizer.best_solution={'a': True, 'b': 1, 'c': 1, 'd': 1}
multi_optimizer.best_objective=0.000e+00
Flexible initial data preparation¶
In the above Basic usage, initial training datasets for the objective functions, obj_1
and obj_2
are generated by the same DatasetGenerator
instance for the same amount (num_samples=3
). However, there may be cases where one objective function is more complex and requires larger dataset than the others. Or, you already have enough training dataset for one of the objective functions from the past experiments, and only need to prepare datasets for the rest of the objective functions. In such cases, you can generate training datasets for each objective function seperately.
Note
When DatasetGenerator.generate
is used seperately for different objective functions, there are some limitations:
Constraints that uses variables across different objective functions cannot be considered. If there are such constraints, set
meet_constraints=False
in the instantiation of theDatasetGenerator
class.If there are decision variables that are used in multiple black-box objective functions, the values of the such variables in the generated training datasets are likely to take different values for different objective functions.
That said, the above limitations should not matter to the construction of individual surrogate/acquisition models during optimization cycles, as each model is constructed seperately and does not need to know the other objective functions.
Here is an exmaples of generating datasets seperately with different numbers of samples.
# Creating datasets with different numbers of samples.
data_1 = DatasetGenerator(obj_1, meet_constraints=False).generate(num_samples=3)
data_2 = DatasetGenerator(obj_2, meet_constraints=False).generate(num_samples=5)
optimizer_1 = KernelQAOptimizer(data_1, obj_1)
optimizer_2 = KernelQAOptimizer(data_2, obj_2)
multi_optimizer = MultiObjectiveOptimizer(
[optimizer_1, optimizer_2], client=client
)
multi_optimizer.optimize(num_cycles=2)
print(f"{multi_optimizer.best_solution=}") # Solution (optimal input)
print(f"{multi_optimizer.best_objective=:.3e}") # Objective function value
Show code cell output
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #0/3 initial data for obj_1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: a=True, b=4
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #1/3 initial data for obj_1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: a=True, b=2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #2/3 initial data for obj_1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: a=False, b=1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #0/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: c=5, d=4
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #1/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: c=3, d=2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #2/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: c=2, d=1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #3/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #4/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:46 | INFO | - [obj]: c=1, d=5
amplify-bbopt | 2024/10/04 06:09:46 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:46 | INFO | #1/2 optimization cycle, constraint wt: 1.60e+02
amplify-bbopt | 2024/10/04 06:09:46 | INFO | model corrcoef: 0.986, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:46 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:50 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:50 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:50 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:50 | INFO | obj_2: 1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:50 | INFO | y_hat_total=0.000e+00, y_best=0.000e+00
amplify-bbopt | 2024/10/04 06:09:50 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:50 | INFO | #2/2 optimization cycle, constraint wt: 1.60e+02
amplify-bbopt | 2024/10/04 06:09:50 | INFO | model corrcoef: 0.969, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:50 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:53 | INFO | identical input was obtained (0), x_hat={'a': True, 'b': 1, 'c': 1, 'd': 1}.
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:53 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: c=1, d=2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | obj_2: 4.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:53 | INFO | y_hat_total=3.000e+00, y_best=0.000e+00
multi_optimizer.best_solution={'a': True, 'b': 1, 'c': 1, 'd': 1}
multi_optimizer.best_objective=0.000e+00
Here is an exmaples where the data for obj_1
is already generated beforehand and loaded from the saved data, while the data for obj_2
is generated by using DatasetGenerator
. As noted above, do not forget to set meet_constraints=False
when there is a constraint which considers variables in different black-box opbjective functions.
from amplify_bbopt import load_dataset
# Creating datasets with different numbers of samples.
data_1 = load_dataset(filepath="./data/obj_1.csv", allow_overwrite=False)
data_2 = DatasetGenerator(obj_2, meet_constraints=False).generate(num_samples=5)
optimizer_1 = KernelQAOptimizer(data_1, obj_1)
optimizer_2 = KernelQAOptimizer(data_2, obj_2)
multi_optimizer = MultiObjectiveOptimizer(
[optimizer_1, optimizer_2], client=client
)
multi_optimizer.optimize(num_cycles=2)
print(f"{multi_optimizer.best_solution=}") # Solution (optimal input)
print(f"{multi_optimizer.best_objective=:.3e}") # Objective function value
Show code cell output
amplify-bbopt | 2024/10/04 06:09:53 | INFO | data loaded from ./data/obj_1.csv
amplify-bbopt | 2024/10/04 06:09:53 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:53 | INFO | #0/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: c=5, d=4
amplify-bbopt | 2024/10/04 06:09:53 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:53 | INFO | #1/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: c=3, d=2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:53 | INFO | #2/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: c=2, d=1
amplify-bbopt | 2024/10/04 06:09:53 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:53 | INFO | #3/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:53 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:53 | INFO | #4/5 initial data for obj_2
amplify-bbopt | 2024/10/04 06:09:53 | INFO | - [obj]: c=1, d=5
amplify-bbopt | 2024/10/04 06:09:53 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:53 | INFO | #1/2 optimization cycle, constraint wt: 1.60e+02
amplify-bbopt | 2024/10/04 06:09:53 | INFO | model corrcoef: 0.967, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:53 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:56 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:56 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:56 | INFO | - [obj]: c=1, d=1
amplify-bbopt | 2024/10/04 06:09:56 | INFO | obj_2: 1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:56 | INFO | y_hat_total=0.000e+00, y_best=0.000e+00
amplify-bbopt | 2024/10/04 06:09:56 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 06:09:56 | INFO | #2/2 optimization cycle, constraint wt: 1.60e+02
amplify-bbopt | 2024/10/04 06:09:56 | INFO | model corrcoef: 0.967, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:56 | INFO | model corrcoef: 1.000, beta: 0.0
amplify-bbopt | 2024/10/04 06:09:59 | INFO | identical input was obtained (0), x_hat={'a': True, 'b': 1, 'c': 1, 'd': 1}.
amplify-bbopt | 2024/10/04 06:09:59 | INFO | - [obj]: a=True, b=1
amplify-bbopt | 2024/10/04 06:09:59 | INFO | obj_1: -1.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:59 | INFO | - [obj]: c=1, d=2
amplify-bbopt | 2024/10/04 06:09:59 | INFO | obj_2: 4.00000e+00 (wt: 1.000e+00)
amplify-bbopt | 2024/10/04 06:09:59 | INFO | y_hat_total=3.000e+00, y_best=0.000e+00
multi_optimizer.best_solution={'a': True, 'b': 1, 'c': 1, 'd': 1}
multi_optimizer.best_objective=0.000e+00