code: def add_model_df_lm(info_dict):
      """Adds degrees of freedom of the regression model to ``info_dict`` for linear models.
  
      The df is defined as the trace of hat matrix. In general, we have
      H=X(X'WX+a*I)^-1X'W and df=trace(H), where X is the design matrix, W is the weight matrix,
      a is the regularization parameter, and I it the identity matrix. For non-weighted methods,
      W is identity matrix, and for non-regularized methods, a is 0.
      For sparse solutions, this is calculated on the nonzero predictors.
  
      Only degree of freedom information will be added in this function.
      A series of these functions are used in a flow to get all information needed
      in linear model summary. The flow is
      `~greykite.algo.common.model_summary_utils.create_info_dict_lm`,
      `~greykite.algo.common.model_summary_utils.add_model_params_lm`,
      `~greykite.algo.common.model_summary_utils.add_model_df_lm`,
      `~greykite.algo.common.model_summary_utils.add_model_ss_lm`,
      `~greykite.algo.common.model_summary_utils.add_beta_var_lm`,
      `~greykite.algo.common.model_summary_utils.add_model_coef_df_lm`,
      `~greykite.algo.common.model_summary_utils.add_model_significance_lm`.
      For flow implementation, see
      `~greykite.algo.common.model_summary_utils.get_info_dict_lm`.
  
      Parameters
      ----------
      info_dict : `dict`
          The information dictionary returned by
          `~greykite.algo.common.model_summary_utils.add_model_params_lm`.
  
      Returns
      -------
      info_dict : `dict`
          The information dictionary with the following keys added:
  
              "x_nz" : `numpy.array`
                  The design matrix with columns corresponding to nonzero estimated coefficients.
              "condition_number" : `float`
                  The condition number for sample covariance matrix (weighted, adjusted).
              "xtwx_alphai_inv" : `numpy.array`
                  (X'WX+a*I)^-1
              "reg_df" : `float`
                  The regression degree of freedom, defined as the trace of hat matrix.
              "df_sse" : `float`
                  The degree of freedom of residuals, defined as n_sample - reg_df.
              "df_ssr" : `float`
                  The degree of freedom of the regression, defined as reg_df - 1.
              "df_sst" : `int`
                  The degree of freedom of total, defined as n_sample - 1.
      """
      x = info_dict["x"]
      n_sample = info_dict["n_sample"]
      w = info_dict.get("weights")
      if w is None:
          w = np.eye(n_sample)
      alpha = info_dict.get("alpha", 0)
      l1_ratio = info_dict.get("l1_ratio", 0)
      # In ElasticNet type models (ElasticNetCV, SGD with penalty == "elasticnet"), the penalty is
      # alpha * l1_ratio * ||beta||_1 + alpha * (1 - l1_ratio) * ||beta||_2^2.
      # For ridge regression, we have l1_ratio = 0, so the l2_norm regularization parameter is alpha.
      # For lasso regression, we have l1_ratio = 1, so the l2_norm regularization parameter is zero.
      alpha = alpha * (
          1 - l1_ratio
      )  # if model has l1_ratio, the l2 norm regularization parameter is alpha * (1 - l1_ratio)
      if info_dict["fit_algorithm"] in ["lasso", "lars", "lasso_lars"]:
          alpha = 0  # alpha is specifically for l2 norm regularization in calculating df
      nonzero_idx = info_dict["nonzero_index"]
      x_nz = x[:, nonzero_idx]
      xtwx = x_nz.T @ w @ x_nz
      xtwx_alphai = xtwx + alpha * np.eye(x_nz.shape[1])
      xtwx_alphai_inv = np.linalg.pinv(xtwx_alphai)
      # The hat matrix was x_nz @ xtwx_alphai_inv @ x_nz.T @ w.
      # The regression degrees of freedom is the trace of this hat matrix.
      # This matrix is n x n, which causes memory overflow for large n.
      # Using the trace property trace(ABCD)=trace(CDAB),
      # we compute x_nz.T @ w @ x_nz @ xtwx_alphai_inv,
      # which has dimension p x p.
      trace = np.trace(x_nz.T @ w @ x_nz @ xtwx_alphai_inv)
      info_dict["x_nz"] = x_nz
      info_dict["condition_number"] = np.linalg.cond(xtwx_alphai)
      info_dict["xtwx_alphai_inv"] = xtwx_alphai_inv  # (X'WX+aI)^-1
      info_dict["reg_df"] = trace
      info_dict["df_sse"] = n_sample - info_dict["reg_df"]
      info_dict["df_ssr"] = info_dict["reg_df"] - 1
      info_dict["df_sst"] = n_sample - 1
      return info_dict
  
imports: import numpy
  import pandas
  import statsmodels
  import statsmodels.api
  from scipy.stats import f
  from scipy.stats import norm
  from scipy.stats import t
  from sklearn.base import clone
  from sklearn.linear_model import Ridge
  from sklearn.model_selection import train_test_split
  from greykite.algo.common.col_name_utils import INTERCEPT
  from greykite.algo.common.col_name_utils import simplify_pred_cols
label_desc: Use image node_id 5 for calling a global function with example usage: add_model_df_lm(info_dict) and returns: info_dict