# HYPERPARAMETER OPTIMIZATION
import optuna
from {{import_library}} import {{ model_name }}


# NEED CV: ex.) optuna.integration.OptunaSearchCV()
class Objective(object):
    def __init__(self, __feature_train, __target_train, __feature_test, __target_test, __random_state):
        self.__feature_train = __feature_train
        self.__target_train = __target_train
        self.__feature_test = __feature_test
        self.__target_test = __target_test 
        self.__random_state = __random_state
    def __call__(self, trial):
        def set_hyperparameters(trial):
            params = {}
{{ params }}
            return params
        
        # SET DATA
        import numpy as np
    
        if isinstance(self.__feature_train, pd.DataFrame):
            __feature_train = self.__feature_train
        elif isinstance(self.__feature_train, np.ndarray):
            __feature_train = pd.DataFrame(self.__feature_train)
        else:
            __feature_train = pd.DataFrame(self.__feature_train.toarray())
    
        if isinstance(self.__target_train, pd.DataFrame):
            __target_train = self.__target_train
        elif isinstance(self.__target_train, np.ndarray):
            __target_train = pd.DataFrame(self.__target_train)
        else:
            __target_train = pd.DataFrame(self.__target_train.toarray())
    
        if isinstance(self.__feature_test, pd.DataFrame):
            __feature_test = self.__feature_test
        elif isinstance(self.__feature_test, np.ndarray):
            __feature_test = pd.DataFrame(self.__feature_test)
        else:
            __feature_test = pd.DataFrame(self.__feature_test.toarray())
    
        if isinstance(self.__target_test, pd.DataFrame):
            __target_test = self.__target_test
        elif isinstance(self.__target_test, np.ndarray):
            __target_test = pd.DataFrame(self.__target_test)
        else:
            __target_test = pd.DataFrame(self.__target_test.toarray())

        # MODEL 
        params = set_hyperparameters(trial)
{% if flag_no_random_seed_model %}
        __model = {{ model_name }}(**params)
{% else %}
        __model = {{ model_name }}(random_state=self.__random_state, **params)
{% endif %}
{% if is_multioutput_regression%}
        from sklearn.multioutput import MultiOutputRegressor

        __model = MultiOutputRegressor(__model)
{% elif is_multioutput_classification %}
        from sklearn.multioutput import MultiOutputClassifier

        __model = MultiOutputClassifier(__model)
{% endif %}
{% set xgbclassifier = "XGBClassifier" %}
{% if model_name == xgbclassifier %}
        from sklearn.preprocessing import LabelEncoder

        _le = LabelEncoder()
        __target_train = _le.fit_transform(__target_train)
{% endif %}

{% if pipeline.task.target_columns|length == 1 %}
{% if model_name == xgbclassifier %}
        __model.fit(__feature_train, __target_train.ravel())
{% else %}
        __model.fit(__feature_train, __target_train.values.ravel())
{% endif %}
{% else %}
        __model.fit(__feature_train, __target_train)
{% endif %}
{% if flag_predict_proba == False %}
        __y_pred = __model.predict(__feature_test)
{% if model_name == xgbclassifier and not flag_predict_proba%}
        __y_pred = _le.inverse_transform(__y_pred)
{% endif %}
{% elif flag_predict_proba == True %}
        __y_pred = __model.predict_proba(__feature_test)
{% filter indent(width=8, first=True) %}
{{ binary_classification_snippet }}
{% endfilter %}
{% endif %}
        
{{ evaluation }}
        
        return score
    
n_trials = {{ pipeline.config.hyperparameter_tuning_n_trials }}
timeout = {{ timeout }} 
random_state = {{ pipeline.config.hyperparameter_tuning_random_state}} 
random_state_model = {{ pipeline.task.seed_for_model}} 

{% set maximize_metrics = [macros.Metric.AUC.value, macros.Metric.Accuracy.value, macros.Metric.F1.value, macros.Metric.R2.value, macros.Metric.Gini.value, macros.Metric.ROC_AUC.value] %}
{% set minimize_metrics = [macros.Metric.RMSE.value, macros.Metric.RMSLE.value, macros.Metric.MAE.value, macros.Metric.LogLoss.value] %}

{% if pipeline.adaptation_metric in maximize_metrics %}
direction = 'maximize' 
{% elif pipeline.adaptation_metric in minimize_metrics %}
direction = 'minimize' 
{% else %}
direction = 'maximize' 
{% endif %}
    
study = optuna.create_study(direction=direction,
                sampler=optuna.samplers.TPESampler(seed=random_state)) 
{{ enqueue_default_hyperparameters }}
study.optimize(Objective(__feature_train, __target_train, __feature_test, __target_test, random_state_model), 
                n_trials=n_trials, 
                timeout=timeout)
best_params = study.best_params

print("best params:", best_params)
print("RESULT: {{ pipeline.task.adaptation_metric }}: " + str(study.best_value))