docs: def _minimize_powell(func, x0, cl_runtime_info, lower_bounds, upper_bounds,
                       constraints_func=None, data=None, options=None):
      """
      Options:
          patience (int): Used to set the maximum number of iterations to patience*(number_of_parameters+1)
          reset_method (str): one of 'EXTRAPOLATED_POINT' or 'RESET_TO_IDENTITY' lower case or upper case.
          patience_line_search (int): the patience of the searching algorithm. Defaults to the
              same patience as for the Powell algorithm itself.
      """
      options = options or {}
      nmr_problems = x0.shape[0]
      nmr_parameters = x0.shape[1]
  
      penalty_data, penalty_func = _get_penalty_function(nmr_parameters, constraints_func)
  
      eval_func = SimpleCLFunction.from_string('''
          double evaluate(local mot_float_type* x, void* data){
              double penalty = _mle_penalty(
                  x, 
                  ((_powell_eval_func_data*)data)->data,
                  ((_powell_eval_func_data*)data)->lower_bounds,
                  ((_powell_eval_func_data*)data)->upper_bounds,
                  ''' + str(options.get('penalty_weight', 1e30)) + ''',  
                  ((_powell_eval_func_data*)data)->penalty_data
              );
              
              double func_val = ''' + func.get_cl_function_name() + '''(x, ((_powell_eval_func_data*)data)->data, 0);
              
              if(isnan(func_val)){
                  return INFINITY;
              }
              
              return func_val + penalty;
          }
      ''', dependencies=[func, penalty_func])
  
      optimizer_func = Powell(eval_func, nmr_parameters, **_clean_options('Powell', options))
  
      kernel_data = {'model_parameters': Array(x0, ctype='mot_float_type', mode='rw'),
                     'data': Struct({'data': data,
                                     'lower_bounds': lower_bounds,
                                     'upper_bounds': upper_bounds,
                                     'penalty_data': penalty_data}, '_powell_eval_func_data')}
      kernel_data.update(optimizer_func.get_kernel_data())
  
      return_code = optimizer_func.evaluate(
          kernel_data, nmr_problems,
          use_local_reduction=all(env.is_gpu for env in cl_runtime_info.cl_environments),
          cl_runtime_info=cl_runtime_info)
  
      return OptimizeResults({'x': kernel_data['model_parameters'].get_data(),
                              'status': return_code})