4. Initial Training Data

For most black-box optimization methods, the user must prepare (a small amount) of initial training data consisting of several input-output pairs of the black-box function. The method of constructing such a dataset varies, but Amplify-BBOpt provides a helpful class called DatasetGenerator.

The DatasetGenerator class provides a way to generate a data set of input-output pairs. As of now, such pairs are randomly generated considering the set decision variable settings and user-defined constraints.

Here, we describe how you can utilize this class, using the black-box function objective_lift_drag (below code cell) as an example. We also consider the same constraints mentioned in “3. Constraint”.

from amplify_bbopt import RealVariable, blackbox, greater_equal, less_equal

from utils.pseudo_simulators import (
    pseudo_wing_simulator as wing_simulator,
)


@blackbox
def objective_lift_drag(
    wing_width: float = RealVariable(bounds=(1, 20), nbins=100),
    wing_height: float = RealVariable(bounds=(1, 5), nbins=20),
    wing_angle: float = RealVariable(bounds=(0, 45), nbins=20),
) -> float:
    """This black-box function executes wing_simulator() and returns
    the negative lift-drag ratio for a given wing's width, height, and angle.
    """
    lift, drag = wing_simulator(wing_width, wing_height, wing_angle)
    return -lift / drag  # value to minimize


# Construct constraints using decision variables
variables = objective_lift_drag.variables
constraint_sum = less_equal(variables.wing_width + variables.wing_height, 10)
constraint_sub = greater_equal(variables.wing_width - variables.wing_height, 4)

# Add the constraints to the black-box function/class instance
objective_lift_drag.add_constraint(constraint_sum)
objective_lift_drag.add_constraint(constraint_sub)

Let’s say you want to generate 3 input-output pairs of the black-box function objective_lift_drag(). You can do so by first instantiating the DatasetGenerator class as follows. By default, the dataset generator considers the set constraints so that all input-output pairs meet these constraints.

Then, to generate the input-output pairs, use generate method of the DatasetGenerator class. As for now, the input vectors are generated randomly (while holding the constraints), and the objective function is evaluated for these inputs to obtain corresponding outputs.

Here is an example.

from amplify_bbopt import DatasetGenerator

generator = DatasetGenerator(objective=objective_lift_drag)
data = generator.generate(num_samples=3)
amplify-bbopt | 2024/10/04 04:47:16 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 04:47:16 | INFO | #0/3 initial data for objective_lift_drag
amplify-bbopt | 2024/10/04 04:47:16 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 04:47:16 | INFO | #1/3 initial data for objective_lift_drag
amplify-bbopt | 2024/10/04 04:47:16 | INFO | ----------------------------------------
amplify-bbopt | 2024/10/04 04:47:16 | INFO | #2/3 initial data for objective_lift_drag

The return value has a type of DataList. You can see the values of generated input vectors and outputs as follows. You can also see that all these input value vectors satisfy the given constraints:

  • wing_width + wing_height <= 10

  • wing_width - wing_height >= 4

data.to_df()
wing_width wing_height wing_angle black-box objective
Sample #0 6.949495 1.842105 35.526316 -3.016282
Sample #1 7.909091 1.421053 23.684211 -4.276795
Sample #2 5.989899 1.210526 21.315789 -3.944039

You can set an output filepath for the DataList object and save the generated data.

To load the saved data as a DataList object, use load_dataset.

from amplify_bbopt import load_dataset

data.set_output_path("data/my_training_data.csv")
data.save()

loaded_data = load_dataset("data/my_training_data.csv")
loaded_data.to_df()
amplify-bbopt | 2024/10/04 04:47:16 | INFO | data saved as data/my_training_data.csv
amplify-bbopt | 2024/10/04 04:47:16 | INFO | data loaded from data/my_training_data.csv
wing_width wing_height wing_angle black-box objective
Sample #0 6.949495 1.842105 35.526316 -3.016282
Sample #1 7.909091 1.421053 23.684211 -4.276795
Sample #2 5.989899 1.210526 21.315789 -3.944039

Note

Once you set a file path of a DataList instance, the instance can overwrite the file whenever the DataList.save method is called. Amplify-BBOpt’s optimizers we discuss later call DataList.save at the end of each optimization cycle.