The good news: Every of Paref’s algorithms has underlying theoretical guarantees. In other words, whenever the MOO does not perform as expected, there are only two possible reasons:

  • a bug in the implementation of the blackbox function

  • the optimization did not converge to the right value

How to check if the blackbox function is implemented correctly?#

Check again if the blackbox function’s

  • design space dimension

  • target space dimension

  • bounds

are correct. Next, check if the blackbox function’s __call__ method returns reasonable values by calling the bbf.y and bbf.xattribute. If you see any unexpected values, the blackbox function is most likely implemented incorrectly.

How to check if the optimization did succeed?#

The most common error in the optimization process results from a bad scaling of the target values. Ensure that the target values are approximately all in the same range, ideally between 0 and 1. Do so by subtracting and multiplying all target values by appropriate positive constants within the __call__ methode of the blackbox function.

⚠️WARNING⚠️:

  • the constant with which the target values are multiplied must be strictly positive. Otherwise, the Pareto front will get changed!

  • do not make this functional, for example by calling a MinMaxScaler, as this would make the optimization highly unstable.

Example Assume the first value of some 2-dimensional bbf with method
def __call__(self, x: np.ndarray) -> np.ndarray:
    ... # Some code implementing the relationship between x and f(x)
    return result

is known to be in the range of -100 to -1000 and the second value in the range of 0.001 to 0.002. This (rather extreme) setup would most likely cause a bad optimization. Then, the following modification would be appropriate:

def __call__(self, x: np.ndarray) -> np.ndarray:
    ... # Some code implementing the relationship between x and f(x)
    return (result-np.array([1000,-0.001])  # Subtract the lower bounds
           /np.array([900,0.001]))  # Divide by the range

It is most likely that the optimization will succeed after such an adjustment.

However, sometimes this is not enough, and we need a take a closer look at the optimization process. Paref works as follows:

  1. a GPR is trained on the evaluations

  2. the (expensive) blackbox function is replaced by the (cheap) GPR

  3. the concatination of the GPR with some Pareto reflection is minimized

Step 1.: check if the training did succeed

This information is provided by the Info class:

from paref.express.info import Info
info = Info(blackbox_function=bbf, training_iter=2000, learning_rate=0.05)  # Create an instance of the Paref Info class
Info.model_fitness

and have a look at the plot of the training. If you recognize the plot to be ‘non-converging’ try more training iterations (e.g. training_iter=5000) and check if the training converged:

info = Info(blackbox_function=bbf, training_iter=5000, learning_rate=0.05)  # Create an instance of the Paref Info class
Info.model_fitness

If you recognize the plot to be non-convex (e.g. if there are any spikes) this is mostly likely caused by a bad scaling of the target values (see above) or a learning rate too large. For the latter, decrease the learning rate (e.g. learning_rate=0.01) and check if the training succeeds. You will mostly likely need to increase the number of training iterations (e.g. training_iter=5000). You can check if those changes did succeed by calling the Info class again.

info = Info(blackbox_function=bbf, training_iter=5000, learning_rate=0.01)  # Create an instance of the Paref Info class
Info.model_fitness

After determining appropriate values for the learning rate and the number of training iterations, initialize the MOO algorithm again with those values.

Step 2.: check if the GPR is a good approximation of the blackbox function How well the GPR approximates the blackbox function is determined by two factors:
  • the complexity of the bbf

  • the number of evaluations (i.e. training points)

The good news is that any GPR can approximate any bbf arbitrarily well if the number of evaluations is large enough. In other words, the optimization will succeed as long as the target space is explored sufficiently. The bad news is that there is no general rule of thumb for the number of evaluations.

However, Paref provides an indicator for the quality of the GPR by calling

from paref.express.info import Info
info = Info(blackbox_function=bbf, training_iter=2000, learning_rate=0.05)  # Create an instance of the Paref Info class
Info.model_fitness

and having a look at the ‘average uncertainty’. A high uncertainty suggests a bad approximation. In such case and after ensuring all of the above steps, you will need to explore the target space further. Currently, you can only do this by

  • calling the bbf.perform_lhc method again or

  • manually adding new points to the target space by calling the bbf to some design.

We are working on additional methods for (automatically and optimally) exploring the target space. If nothing of the above worked feel free to contact me!