pythonhyperopt

Using hyperopt with constraints


To create search space for hyperopt, we can simply do:

space = {
    'x': hp.uniform('x', -10, 10),
    'y': hp.uniform('y', -10, 10)
}

However, how can I do this when I want a condition like x + y = 1? And extend it to many variables like x+y+z+t = 1


Solution

  • I had the same problem. What worked for me was defining the function for parameter optimization without one of the parameters whose value was then calculated on the fly by subtracting the values of the other parameters from a constant (the number you want the parameters to add up to):

    from hyperopt import fmin, tpe, hp
    
    # the function; parameter t is calculated automatically, only x,y,z are optimized
    def fn(x,y,z):
        t = 1 - x - y - z # ensures that x+y+z+t always equals 1
        return x*y+z*t # whatever
    
    # Define the search space for the function arguments
    space = hp.choice('parameters', [
        {'x': hp.uniform('x', -10, 10),
        'y': hp.uniform('y', -10, 10),
        'z': hp.uniform('z', -10, 10)},
    ])
    
    # Define the objective function
    def objective(params):
        x = params['x']
        y = params['y']
        z = params['z']
        return fn(x,y,z)
    
    # Use the fmin function to find the minimum value
    best = fmin(objective, space, algo=tpe.suggest, max_evals=1000)
    
    # Print the best parameters and the minimum value
    print(best)
    print(objective(best))
    
    # the value of t can be easily calculated by subtracting from one
    t = 1 - best["x"] - best["y"] - best["z"]
    

    Originally, I also tried an iteration of bene's approach, testing if x+y+z+t = 1 and returning infinity if not:

    def fn(x,y,z):
        if(x+y+z+t!=1):
          return float('inf')
        else:
          return x*y+z*t
    

    But this never produced any other value than Inf as the probability of four randomly chosen floats to add up exactly to 1 is negligible.