class_docstr: Transformation for linear constraints `R params = q`
  
  Note, the transformation from the reduced to the full parameters is an
  affine and not a linear transformation if q is not zero.
  
  
  Parameters
  ----------
  R : array_like
      Linear restriction matrix
  q : arraylike or None
      values of the linear restrictions
  
  
  Notes
  -----
  The reduced parameters are not sorted with respect to constraints.
  
  TODO: error checking, eg. inconsistent constraints, how?
  
  Inconsistent constraints will raise an exception in the calculation of
  the constant or offset. However, homogeneous constraints, where q=0, will
  can have a solution where the relevant parameters are constraint to be
  zero, as in the following example::
  
      b1 + b2 = 0 and b1 + 2*b2 = 0, implies that b2 = 0.
  
  The transformation applied from full to reduced parameter space does not
  raise and exception if the constraint does not hold.
  TODO: maybe change this, what's the behavior in this case?
  
  
  The `reduce` transform is applied to the array of explanatory variables,
  `exog`, when transforming a linear model to impose the constraints.
code: def __init__(self, R, q=None):
      # The calculations are based on Stata manual for makecns
      R = self.R = np.atleast_2d(R)
      if q is not None:
          q = self.q = np.asarray(q)
  
      k_constr, k_vars = R.shape
      self.k_constr, self.k_vars = k_constr, k_vars
      self.k_unconstr = k_vars - k_constr
  
      m = np.eye(k_vars) - R.T.dot(np.linalg.pinv(R).T)
      evals, evecs = np.linalg.eigh(m)
  
      # This normalizes the transformation so the larges element is 1.
      # It makes it easier to interpret simple restrictions, e.g. b1 + b2 = 0
      # TODO: make this work, there is something wrong, does not round-trip
      #       need to adjust constant
      # evecs_maxabs = np.max(np.abs(evecs), 0)
      # evecs = evecs / evecs_maxabs
  
      self.evals = evals
      self.evecs = evecs  # temporarily attach as attribute
      L = self.L = evecs[:, :k_constr]
      self.transf_mat = evecs[:, k_constr:]
  
      if q is not None:
          # use solve instead of inv
          # self.constant = q.T.dot(np.linalg.inv(L.T.dot(R.T)).dot(L.T))
          try:
              self.constant = q.T.dot(
                  np.linalg.solve(L.T.dot(R.T), L.T)
              )
          except np.linalg.LinAlgError as e:
              raise ValueError(
                  "possibly inconsistent constraints. error "
                  "generated by\n%r" % (e,)
              )
      else:
          self.constant = 0
  
file_path: statsmodels/statsmodels/base/_constraints.py
imports: import numpy
label_desc: Use image node_id 1 to create a new TransformRestriction object  with example: obj = TransformRestriction(R, q)