pythonhyperopt

Does Hyperopt support subset of choices?


I have a set of choices A. I would like to get back a subset of choices A. Is this possible with Hyperopt?

input:

{
    'A': hp.choice('A', [0, 1, 2, 3])
}

output:

{
    'A': [0, 2]
}

Solution

  • No, but if you specifically want that functionality, there are multiple ways to do so.

    1. You can define a new expression and add it to existing hyperopt parameter expressions as given in the documentation. For example, in your case, you can do:

      import hyperopt.pyll
      from hyperopt.pyll import scope
      from hyperopt.pyll.stochastic import sample
      
      # Add a new method as you want
      @scope.define
      def foo(choices, num_choices):
          return np.random.choice(choices, size=num_choices, replace=False)
      
      choices = [1,2,3,4]
      
      # Define the space like below and use in fmin
      space = scope.foo(choices, 2)
      
      # Call this multiple times to see how space behaves
      print(sample(space))   
      

      See the documention of numpy.random.choice to know how that works.

      Note:

      • The foo method will return a subset of choices (in a numpy array). So make sure that in your objective function, you are using the internal multiple values like x[0], x[1], ... etc.
      • Add scope.undefine(foo) in the end of your code, or else you will have to restart your terminal / kernel each time before running the code.
      • The hyperopt wiki specifically prohibits to define new types of parameter search spaces just as we did above because that may affect the search strategy or perform non-optimally.
    2. If you are allowed to choose two values with replacement (that means that sometimes both values in the subset will be same. This is the reason we used replace=False in point 1), then the following can be done:

      choices = [1,2,3,4]
      space = [hp.choice('c1', choices), 
               hp.choice('c2', choices)]
      

      Then in your objective function, you can access your two values as x[0], x[1].

      But from your question, it seems like you want to have the sub-choices and that implies without replacement, so a sub-set of [1,1] or [2,2] etc. is invalid. In that case, you should use the Trials object to define status.

    A sample program for point 2 is below:

    from hyperopt import fmin, tpe, hp, STATUS_OK, STATUS_FAIL, Trials
    
    def objective(x):
        # Check if the supplied choices are valid or not
        x1 = x[0]
        x2 = x[1]
        if x1 == x2:
            # If invalid, only return the status and hyperopt will understand
            return {'status': STATUS_FAIL} 
    
    
        # Normal flow of objective
        # Do your coding here
    
        # In the end, return this  
        return {
            'loss': actual_loss,  # Fill the actual loss here
            'status': STATUS_OK
            }
    
    choices = [1,2,3,4]
    space = [hp.choice('c1', choices), 
             hp.choice('c2', choices)]
    
    trials = Trials()   
    best = fmin(objective, space=space, algo=tpe.suggest, max_evals=100, trials=trials)
    
    from hyperopt import space_eval
    print(space_eval(space, best))
    

    Hope this helps.