Convert a generic tree model to the protobuf spec.

    This currently supports:
      * Decision tree regression

    Parameters
    ----------
    model: str | Booster
        Path on disk where the XGboost JSON representation of the model is or
        a handle to the XGboost model.

    feature_names : list of strings or None
        Names of each of the features. When set to None, the feature names are
        extracted from the model.

    target: str,
        Name of the output column.

    force_32bit_float: bool
        If True, then the resulting CoreML model will use 32 bit floats internally.

    Returns
    -------
    model_spec: An object of type Model_pb.
        Protobuf representation of the model

def convert_tree_ensemble(model, feature_names, target, force_32bit_float):
    """Convert a generic tree model to the protobuf spec.

    This currently supports:
      * Decision tree regression

    Parameters
    ----------
    model: str | Booster
        Path on disk where the XGboost JSON representation of the model is or
        a handle to the XGboost model.

    feature_names : list of strings or None
        Names of each of the features. When set to None, the feature names are
        extracted from the model.

    target: str,
        Name of the output column.

    force_32bit_float: bool
        If True, then the resulting CoreML model will use 32 bit floats internally.

    Returns
    -------
    model_spec: An object of type Model_pb.
        Protobuf representation of the model
    """
    if not(_HAS_XGBOOST):
        raise RuntimeError('xgboost not found. xgboost conversion API is disabled.')

    import json
    import os
    feature_map = None
    if isinstance(model,  (_xgboost.core.Booster, _xgboost.XGBRegressor)):

        # Testing a few corner cases that we don't support
        if isinstance(model, _xgboost.XGBRegressor):
            try:
                objective = model.get_xgb_params()["objective"]
            except:
                objective = None
            if objective in ["reg:gamma", "reg:tweedie"]:
                raise ValueError("Regression objective '%s' not supported for export." % objective)

        # Now use the booster API.
        if isinstance(model, _xgboost.XGBRegressor):
            # Name change in 0.7
            if hasattr(model, 'get_booster'):
                model = model.get_booster()
            else:
                model = model.booster()

        # Xgboost sometimes has feature names in there. Sometimes does not.
        if (feature_names is None) and (model.feature_names is None):
            raise ValueError("Feature names not present in the model. Must be provided during conversion.")
            feature_names = model.feature_names
        if feature_names is None:
            feature_names = model.feature_names

        xgb_model_str = model.get_dump(with_stats=True, dump_format = 'json')

        if model.feature_names:
            feature_map = {f:i for i,f in enumerate(model.feature_names)}

    # Path on the file system where the XGboost model exists.
    elif isinstance(model, str):
        if not os.path.exists(model):
            raise TypeError("Invalid path %s." % model)
        with open(model) as f:
            xgb_model_str = json.load(f)
        feature_map = {f:i for i,f in enumerate(feature_names)}
    else:
        raise TypeError("Unexpected type. Expecting XGBoost model.")

    mlkit_tree = _TreeEnsembleRegressor(feature_names, target)
    mlkit_tree.set_default_prediction_value(0.5)
    for xgb_tree_id, xgb_tree_str in enumerate(xgb_model_str):
        xgb_tree_json = json.loads(xgb_tree_str)
        recurse_json(mlkit_tree, xgb_tree_json, xgb_tree_id, node_id = 0,
                feature_map = feature_map, force_32bit_float = force_32bit_float)

    return mlkit_tree.spec