class_docstr: Fit a regression model using quadratic inference functions (QIF).
  
  QIF is an alternative to GEE that can be more efficient, and that
  offers different approaches for model selection and inference.
  
  Parameters
  ----------
  endog : array_like
      The dependent variables of the regression.
  exog : array_like
      The independent variables of the regression.
  groups : array_like
      Labels indicating which group each observation belongs to.
      Observations in different groups should be independent.
  family : genmod family
      An instance of a GLM family.
  cov_struct : QIFCovariance instance
      An instance of a QIFCovariance.
  
  References
  ----------
  A. Qu, B. Lindsay, B. Li (2000).  Improving Generalized Estimating
  Equations using Quadratic Inference Functions, Biometrika 87:4.
  www.jstor.org/stable/2673612
docstr: Calculate the gradient of the QIF objective function.
  
  Parameters
  ----------
  params : array_like
      The model parameters at which the gradient is evaluated.
  
  Returns
  -------
  grad : array_like
      The gradient vector of the QIF objective function.
  gn_deriv : array_like
      The gradients of each estimating equation with
      respect to the parameter.
docstr_tok: ["Calculate","the","gradient","of","the","QIF","objective","function",".","Parameters","--","--","--","--","--","params",":","array_like","The","model","parameters","at","which","the","gradient","is","evaluated",".","Returns","--","--","--","-","grad",":","array_like","The","gradient","vector","of","the","QIF","objective","function",".","gn_deriv",":","array_like","The","gradients","of","each","estimating","equation","with","respect","to","the","parameter","."]
code: def objective(self, params):
      """
      Calculate the gradient of the QIF objective function.
  
      Parameters
      ----------
      params : array_like
          The model parameters at which the gradient is evaluated.
  
      Returns
      -------
      grad : array_like
          The gradient vector of the QIF objective function.
      gn_deriv : array_like
          The gradients of each estimating equation with
          respect to the parameter.
      """
  
      endog = self.endog
      exog = self.exog
      lpr = np.dot(exog, params)
      mean = self.family.link.inverse(lpr)
      va = self.family.variance(mean)
  
      # Mean derivative
      idl = self.family.link.inverse_deriv(lpr)
      idl2 = self.family.link.inverse_deriv2(lpr)
      vd = self.family.variance.deriv(mean)
  
      m = self.cov_struct.num_terms
      p = exog.shape[1]
  
      d = p * m
      gn = np.zeros(d)
      gi = np.zeros(d)
      gi_deriv = np.zeros((d, p))
      gn_deriv = np.zeros((d, p))
      cn_deriv = [0] * p
      cmat = np.zeros((d, d))
  
      fastvar = self.family.variance is varfuncs.constant
      fastlink = isinstance(
          self.family.link,
          # TODO: Remove links.identity after deprecation final
          (links.Identity, links.identity),
      )
  
      for ix in self.groups_ix:
          sd = np.sqrt(va[ix])
          resid = endog[ix] - mean[ix]
          sresid = resid / sd
          deriv = exog[ix, :] * idl[ix, None]
  
          jj = 0
          for j in range(m):
              # The derivative of each term in (5) of Qu et al.
              # There are four terms involving beta in a product.
              # Iterated application of the product rule gives
              # the gradient as a sum of four terms.
              c = self.cov_struct.mat(len(ix), j)
              crs1 = np.dot(c, sresid) / sd
              gi[jj : jj + p] = np.dot(deriv.T, crs1)
              crs2 = np.dot(c, -deriv / sd[:, None]) / sd[:, None]
              gi_deriv[jj : jj + p, :] = np.dot(deriv.T, crs2)
              if not (fastlink and fastvar):
                  for k in range(p):
                      m1 = np.dot(
                          exog[ix, :].T, idl2[ix] * exog[ix, k] * crs1
                      )
                      if not fastvar:
                          vx = (
                              -0.5
                              * vd[ix]
                              * deriv[:, k]
                              / va[ix] ** 1.5
                          )
                          m2 = np.dot(deriv.T, vx * np.dot(c, sresid))
                          m3 = np.dot(
                              deriv.T, np.dot(c, vx * resid) / sd
                          )
                      else:
                          m2, m3 = 0, 0
                      gi_deriv[jj : jj + p, k] += m1 + m2 + m3
              jj += p
  
          for j in range(p):
              u = np.outer(gi, gi_deriv[:, j])
              cn_deriv[j] += u + u.T
  
          gn += gi
          gn_deriv += gi_deriv
  
          cmat += np.outer(gi, gi)
  
      ngrp = len(self.groups_ix)
      gn /= ngrp
      gn_deriv /= ngrp
      cmat /= ngrp**2
  
      qif = np.dot(gn, np.linalg.solve(cmat, gn))
  
      gcg = np.zeros(p)
      for j in range(p):
          cn_deriv[j] /= len(self.groups_ix) ** 2
          u = np.linalg.solve(cmat, cn_deriv[j]).T
          u = np.linalg.solve(cmat, u)
          gcg[j] = np.dot(gn, np.dot(u, gn))
  
      grad = 2 * np.dot(gn_deriv.T, np.linalg.solve(cmat, gn)) - gcg
  
      return qif, grad, cmat, gn, gn_deriv
  
imports: import numpy
  from collections import defaultdict
  import statsmodels.base.model
  from statsmodels.genmod import families
  from statsmodels.genmod.generalized_linear_model import GLM
  from statsmodels.genmod.families import links
  from statsmodels.genmod.families import varfuncs
  import statsmodels.regression.linear_model
  import statsmodels.base.wrapper
  from statsmodels.tools.decorators import cache_readonly
label_desc: Use image node_id 3 for calling the QIF obj's underlying member method code with example usage: obj.objective(params) and returns: qif,  grad,  cmat,  gn,  gn_deriv