code: def build_penalties(self, verbose=False):
          
          if self.isintercept:
              return np.array([[0.]])
  
          Ps = []
          for penalty, lam in zip(self.penalties, self.lam):
              if penalty == 'auto':
                  if self.dtype == 'numerical':
                      if self._name == 'spline_term':
                          if self.basis in ['cp']:
                              penalty = 'periodic'
                          else:
                              penalty = 'derivative'
                      else:
                          penalty = 'l2'
                  if self.dtype == 'categorical':
                      penalty = 'l2'
              if penalty is None:
                  penalty = 'none'
              if penalty in PENALTIES:
                  penalty = PENALTIES[penalty]
  
              P = penalty(self.n_coefs, coef=None) # penalties dont need coef
              Ps.append(np.multiply(P, lam))
          return np.sum(Ps)
docs: builds the GAM block-diagonal penalty matrix in quadratic form
          out of penalty matrices specified for each feature.
  
          each feature penalty matrix is multiplied by a lambda for that feature.
  
          so for m features:
          P = block_diag[lam0 * P0, lam1 * P1, lam2 * P2, ... , lamm * Pm]
  
  
          Parameters
          ---------
          None
  
          Returns
          -------
          P : sparse CSC matrix containing the model penalties in quadratic form