def METHOD_NAME(ranker_name, RankerType, data, static_params, param_space, log_file, out_file):
    if not os.path.exists(log_file):
        with open(log_file, 'w') as f:
            json.dump({}, f)
    def objective(params):
        ranker_params = deepcopy(static_params)
        ranker_params.update(params)
        print('Fit with params: ' + _params_to_str(ranker_params))
        with open(log_file, 'r') as f:
            log = json.load(f)
            if ranker_name not in log:
                log[ranker_name] = {}
            params_str = _params_to_str(params)
            if params_str in log[ranker_name]:
                print('Return result from cache')
                return max(log[ranker_name][params_str]['ndcg'])
        ranker = RankerType(ranker_params)
        start = datetime.datetime.now()
        ranker.fit(data)
        train_time = datetime.datetime.now() - start
        train_time = train_time.total_seconds()
        eval_log = ranker.eval_ndcg(data)
        log[ranker_name][params_str] = {
            'time': train_time,
            'ndcg': eval_log
        }
        dump = log_file + '.dmp'
        with open(dump, 'w') as f:
            json.dump(log, f, indent=4, sort_keys=True)
        os.rename(dump, log_file)
        return max(eval_log)
    best_params = argmin(fn=objective, space=param_space)
    print('Best params:' + str(best_params))
    with open(out_file, 'w') as f:
        json.dump(best_params, f, indent=4, sort_keys=True)
    return best_params