code: def fit_model_via_design_matrix(
      x_train,
      y_train,
      fit_algorithm,
      sample_weight=None,
      fit_algorithm_params=None,
  ):
      """Fits the predictive model and returns the prediction function.
  
      Parameters
      ----------
      x_train : `numpy.array`
          The design matrix
      y_train : `numpy.array`
          The vector of responses
      fit_algorithm : `str`
          The type of predictive model used in fitting
          (implemented by sklearn and statsmodels).
  
          Available models are:
  
              - ``"statsmodels_ols"``   : `statsmodels.regression.linear_model.OLS`
              - ``"statsmodels_wls"``   : `statsmodels.regression.linear_model.WLS`
              - ``"statsmodels_gls"``   : `statsmodels.regression.linear_model.GLS`
              - ``"statsmodels_glm"``   : `statsmodels.genmod.generalized_linear_model.GLM`
              - ``"linear"``            : `statsmodels.regression.linear_model.OLS`
              - ``"elastic_net"``       : `sklearn.linear_model.ElasticNetCV`
              - ``"ridge"``             : `sklearn.linear_model.RidgeCV`
              - ``"lasso"``             : `sklearn.linear_model.LassoCV`
              - ``"sgd"``               : `sklearn.linear_model.SGDRegressor`
              - ``"lars"``              : `sklearn.linear_model.LarsCV`
              - ``"lasso_lars"``        : `sklearn.linear_model.LassoLarsCV`
              - ``"rf"``                : `sklearn.ensemble.RandomForestRegressor`
              - ``"gradient_boosting"`` : `sklearn.ensemble.GradientBoostingRegressor`
              - ``"quantile_regression"`` : `~greykite.algo.common.l1_quantile_regression.QuantileRegression`
  
          See `~greykite.algo.common.ml_models.fit_model_via_design_matrix`
          for the sklearn and statsmodels classes that implement these methods, and their parameters.
  
          "linear" is the same as "statsmodels_ols", because `statsmodels.regression.linear_model.OLS`
          is more stable than `sklearn.linear_model.LinearRegression`.
      sample_weight : `numpy.array` or None, default None
          The vector of weights to be used in weighted models.
          These weights will be used to weigh each loss potentially differently.
      fit_algorithm_params : `dict` or None, default None
          Parameters passed to the requested ``fit_algorithm``.
          If None, uses the defaults defined in this function.
  
      Returns
      -------
      ml_model : `class`
          a trained predictive model with available predict method
      """
      fit_algorithm_dict = {
          "statsmodels_ols": sm.OLS,  # ordinary least squares
          "statsmodels_wls": sm.WLS,  # weighted least squares
          "statsmodels_gls": sm.GLS,  # generalized least squares
          "statsmodels_glm": sm.GLM,  # generalized linear models
          # "linear" has been redirected to statsmodels OLS instead of sklearn LinearRegression
          # We've found sklearn LinearRegression's solution to be unstable under some cases.
          # The reason could be that sklearn calls lapack backend that depends on the build environment.
          # statsmodels uses simple QR decomposition/pseudo inverse to compute the solution,
          # and provides stabler solutions.
          "linear": sm.OLS,
          "elastic_net": ElasticNetCV,
          "ridge": RidgeCV,
          "lasso": LassoCV,
          "sgd": SGDRegressor,  # fits linear, elastic_net, ridge, lasso via SGD. Default is ridge with alpha = 0.0001
          "lars": LarsCV,
          "lasso_lars": LassoLarsCV,
          "rf": RandomForestRegressor,
          "gradient_boosting": GradientBoostingRegressor,
          "quantile_regression": QuantileRegression,
      }
  
      # for our purposes, we may want different defaults from those provided in the classes
      # sets the default `cv` and `n_estimators`
      default_fit_algorithm_params = {
          "statsmodels_ols": dict(),
          "statsmodels_wls": dict(),
          "statsmodels_gls": dict(),
          "statsmodels_glm": dict(
              family=sm.families.Gamma()
          ),  # default is gamma distribution
          "linear": dict(),
          "elastic_net": dict(cv=5),
          "ridge": dict(
              cv=5, alphas=np.logspace(-5, 5, 30)
          ),  # by default RidgeCV only has 3 candidate alphas, not enough
          "lasso": dict(cv=5),
          "sgd": dict(),
          "lars": dict(cv=5),
          "lasso_lars": dict(cv=5),
          "rf": dict(n_estimators=100),
          "gradient_boosting": dict(),
          "quantile_regression": dict(
              quantile=0.5, alpha=0
          ),  # unregularized version modeling median
      }
  
      # Re-standardizes the weights so that they sum up to data length
      # Note that in the case of no weights, each weight will be 1,
      # which also sums up to data length
      if sample_weight is not None:
          if fit_algorithm not in ["ridge", "statsmodels_wls"]:
              raise ValueError(
                  "sample weights are passed. "
                  f"However {fit_algorithm} does not support weighted regression."
              )
          sample_weight = (
              len(sample_weight) * sample_weight / sum(sample_weight)
          )
  
      if fit_algorithm not in fit_algorithm_dict.keys():
          raise ValueError(
              f"The fit algorithm requested was not found: {fit_algorithm}. "
              f"Must be one of {list(fit_algorithm_dict.keys())}"
          )
  
      # overwrites default params with those provided by user
      params = default_fit_algorithm_params.get(fit_algorithm, {})
      if fit_algorithm_params is not None:
          params.update(fit_algorithm_params)
  
      # ``ml_model`` refers to fitted machine-learning model object
      if "statsmodels" in fit_algorithm or fit_algorithm == "linear":
          if (
              fit_algorithm == "statsmodels_wls"
              and sample_weight is not None
          ):
              ml_model = fit_algorithm_dict[fit_algorithm](
                  endog=y_train,
                  exog=x_train,
                  weights=sample_weight,
                  **params,
              )
          else:
              ml_model = fit_algorithm_dict[fit_algorithm](
                  endog=y_train, exog=x_train, **params
              )
          ml_model = ml_model.fit()
          # Adds .coef_ and .intercept_ to statsmodels, so we could fetch parameters from .coef_ for all models.
          # Intercept is already included in params, setting .intercept_=0 in case it is needed.
          ml_model.coef_ = ml_model.params
          ml_model.intercept_ = 0.0
      else:
          ml_model = fit_algorithm_dict[fit_algorithm](**params)
          if fit_algorithm == "ridge":
              ml_model.fit(
                  X=x_train, y=y_train, sample_weight=sample_weight
              )
          else:
              ml_model.fit(X=x_train, y=y_train)
  
      return ml_model
  
imports: import random
  import re
  import traceback
  import warnings
  from typing import Dict
  from typing import List
  from typing import Optional
  import matplotlib
  import numpy
  import pandas
  import patsy
  import scipy
  import statsmodels.api
  from pandas.plotting import register_matplotlib_converters
  from sklearn.ensemble import GradientBoostingRegressor
  from sklearn.ensemble import RandomForestRegressor
  from sklearn.linear_model import ElasticNetCV
  from sklearn.linear_model import LarsCV
  from sklearn.linear_model import LassoCV
  from sklearn.linear_model import LassoLarsCV
  from sklearn.linear_model import RidgeCV
  from sklearn.linear_model import SGDRegressor
  from greykite.algo.common.l1_quantile_regression import QuantileRegression
  from greykite.algo.uncertainty.conditional.conf_interval import conf_interval
  from greykite.algo.uncertainty.conditional.conf_interval import predict_ci
  from greykite.common.constants import RESIDUAL_COL
  from greykite.common.constants import R2_null_model_score
  from greykite.common.evaluation import calc_pred_err
  from greykite.common.evaluation import r2_null_model_score
  from greykite.common.features.normalize import normalize_df
  from greykite.common.logging import LoggingLevelEnum
  from greykite.common.logging import log_message
  from greykite.common.python_utils import group_strs_with_regex_patterns
  from greykite.common.viz.timeseries_plotting import plot_multivariate
  import matplotlib.pyplot
label_desc: Use image node_id 3 for calling a global function with example usage: fit_model_via_design_matrix(x_train, y_train, fit_algorithm, sample_weight, fit_algorithm_params) and returns: ml_model