Multiple Objectives

For some black-box optimization problems, there are not one, but multiple-objective functions to be considered. Such optimization problems are called multiple-objective optimization. Here, we will explain what multiple-objective optimizations are more specifically, and how we can implement them by using Amplify-BBOpt.

Dependency between objective functions

The objective functions considered in a multiple-objective optimization prolem are dependent via decision variables and/or constraint conditions. Thus, the purpose of multiple-objective optimization is to find a solution where these individual objectives are minimized on a desired balance. Here is very simple examples (for the purpose of explanation, these functions are not really black-box).

Dependency via decision variables

Let us use the below code to understand black-box objective functions having this type of dependency. As you can see, these two objective functions, obj_1 and obj_2, are dependent via the integer variable of b.

Now, suppose you only consider the first objective function obj_1. The best solution to minimize the output from obj_1 would be clearly {"a": True, "b": 5}, which yields the output of -5.

However, the second objective function obj_2 returns the sum of b and c. Thus, the above solution, {"a": True, "b": 5}, is not a good choice for obj_2. Instead of finding a solution that minimize only one of obj_1 or obj_2, we would like to find a solution that minimize both obj_1 and obj_2 to the similar extent.

from amplify_bbopt import BinaryVariable, IntegerVariable

def obj_1(
    a: bool = BinaryVariable(),
    b: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    if a:
        return -b
    return b


def obj_2(
    b: int = IntegerVariable(bounds=(1, 5)),
    d: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    return b * d

Dependency via constraints

The below code considers the same objective functions, obj_1 and obj_2, but obj_2 now takes (decision) variables c and d. Thus, these two functions no longer share the same variable b. However, there is a constraint where:

  • b of obj_1 minus c of obj_2 must be 0.

This essentially imposes the same dependency via decision variables above, in a sence that second argument of obj_1 equals the first argument of obj_2.

def obj_1(
    a: bool = BinaryVariable(),
    b: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    if a:
        return -b
    return b


def obj_2(
    c: int = IntegerVariable(bounds=(1, 5)),
    d: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    return c * d

# Constraint: b - c = 0

Although these two dependencies via decision variables and via constraints are not always interchangable, you can see that there are cases where multiple objective functions are dependent one another, and optimization for such objective functions need to consder these dependencies.

Considering multiple objectives

We saw that multiple objective functions can be dependent. Here, we explains how we can perform black-box optimization when there are multiple black-box objective functions dependenet one another. There are two methods to do so.

Method 1: using a unified objective

The first method is to combine all black-box objectives into one unified black-box objective function such that only one surrogate/acquisition model is constructed at each optimization cycle.

Below is an example using objective functions, obj_1 and obj_2, demonstrated above. In this example, we will create one black-box objective function class instance with @blackbox decorator, and this instance is named unified_obj. In unified_obj, there are two objectives, obj_1 and obj_2, but the optimizer would only care about the unified output from the unified_obj function. A surrogate/acquisition model is also constructed for unified_obj, and the obj_1 and obj_2 are implicitly considered in this method.

In this method, you can simply pass unified_obj to one of optimizer classes to create an optimizer instance, and start your optimization on par with typical black-box optimization with an objective function.

from amplify_bbopt import BinaryVariable, IntegerVariable, blackbox


@blackbox
def unified_obj(
    a: bool = BinaryVariable(),
    b: int = IntegerVariable(bounds=(1, 5)),
    d: int = IntegerVariable(bounds=(1, 5)),
):
    def obj_1(a: bool, b: int) -> int:
        if a:
            return -b
        return b

    def obj_2(b: int, d: int) -> int:
        return b * d

    out_1 = obj_1(a, b)
    out_2 = obj_2(b, d)

    return out_1 + out_2

Method 2: using multiple objectives

By contrast, in the method 2, we will consider these two objectives seperately, as they are. This means during the optimization cycles, two corresponding surrogate/acquisition models are seperately constructed. Then these models are combined (with appropriate weight if necessary) and converted to optimization model (typically a QUBO model by amplify.Model) to proceed to find a optimial solution for the model.

Below is an example of using this method.

@blackbox
def obj_1(
    a: bool = BinaryVariable(),
    b: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    if a:
        return -b
    return b


@blackbox
def obj_2(
    b: int = IntegerVariable(bounds=(1, 5)),
    d: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    return b * d

For the above objective functions, you can also implement as follows, by using a constraint to impose the condition where that second argument of obj_1 equals the first argument of obj_2. Note that the Constraint object my_constraint considers variables from two different objective functions.

from amplify_bbopt import equal_to


@blackbox
def obj_1(
    a: bool = BinaryVariable(),
    b: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    if a:
        return -b
    return b


@blackbox
def obj_2(
    c: int = IntegerVariable(bounds=(1, 5)),
    d: int = IntegerVariable(bounds=(1, 5)),
) -> int:
    return c * d


my_constraint = equal_to(obj_1.variables.b - obj_2.variables.c, 0)

Now, we have two objective functions (and a constraint). In the next page we will describe “Multi-Objective Optimizer” to handle multiple objective functions and constraints spanning different objective functions.