class_docstr: Operations related to the Lagrangian.
  
  Parameters
  ----------
  X : {numpy.ndarray, pandas.DataFrame}
      the training features
  sensitive_features : {numpy.ndarray, pandas.Series, pandas.DataFrame, list}
      the sensitive features to use for constraints
  y : {numpy.ndarray, pandas.Series, pandas.DataFrame, list}
      the training labels
  estimator :
      the estimator to fit in every iteration of :meth:`best_h` using a
      :meth:`fit` method with arguments `X`, `y`, and `sample_weight`
  constraints : fairlearn.reductions.Moment
      Object describing the fairness constraints. This provides the reweighting
      and relabelling.
  objective : fairlearn.reductions.Moment
      Object describing the optimization objective. The default is :code:`ErrorRate()` for
      binary classification and :code:`MeanLoss(...)` for regression.
  B : float
      bound on the L1-norm of the lambda vector
  opt_lambda : bool
      indicates whether to optimize lambda during the calculation of the
      Lagrangian; optional with default value True
  sample_weight_name : str
      Name of the argument to `estimator.fit()` which supplies the sample weights
      (defaults to `sample_weight`)
code: def solve_linprog(self, nu):
      n_hs = len(self.hs)
      n_constraints = len(self.constraints.index)
      if self.last_linprog_n_hs == n_hs:
          return self.last_linprog_result
      c = np.concatenate((self.errors, [self.B]))
      A_ub = np.concatenate(
          (
              self.gammas.sub(self.constraints.bound(), axis=0),
              -np.ones((n_constraints, 1)),
          ),
          axis=1,
      )
      b_ub = np.zeros(n_constraints)
      A_eq = np.concatenate(
          (np.ones((1, n_hs)), np.zeros((1, 1))), axis=1
      )
      b_eq = np.ones(1)
      result = opt.linprog(
          c,
          A_ub=A_ub,
          b_ub=b_ub,
          A_eq=A_eq,
          b_eq=b_eq,
          method="highs-ds",
      )
      Q = pd.Series(result.x[:-1], self.hs.index)
      dual_c = np.concatenate((b_ub, -b_eq))
      dual_A_ub = np.concatenate(
          (-A_ub.transpose(), A_eq.transpose()), axis=1
      )
      dual_b_ub = c
      dual_bounds = [
          (None, None) if i == n_constraints else (0, None)
          for i in range(n_constraints + 1)
      ]
      result_dual = opt.linprog(
          dual_c,
          A_ub=dual_A_ub,
          b_ub=dual_b_ub,
          bounds=dual_bounds,
          method="highs-ds",
      )
      lambda_vec = pd.Series(result_dual.x[:-1], self.constraints.index)
      self.last_linprog_n_hs = n_hs
      self.last_linprog_result = (
          Q,
          lambda_vec,
          self.eval_gap(Q, lambda_vec, nu),
      )
      return self.last_linprog_result
  
imports: import logging
  from time import time
  import numpy
  import pandas
  import scipy.optimize
  from sklearn import clone
  from sklearn.dummy import DummyClassifier
  from fairlearn.reductions._moments import ClassificationMoment
  from ._constants import _INDENTATION, _LINE, _PRECISION
label_desc: Use image node_id 4 for calling the _Lagrangian obj's underlying member method code with example usage: obj.solve_linprog(nu) and returns: self, self